1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "checklistmenu.hxx"
21 #include "checklistmenu.hrc"
22 #include "strload.hxx"
23 #include "globstr.hrc"
25 #include <vcl/decoview.hxx>
26 #include <vcl/settings.hxx>
27 #include <tools/wintypes.hxx>
29 #include "AccessibleFilterMenu.hxx"
30 #include "AccessibleFilterTopWindow.hxx"
32 #include <com/sun/star/accessibility/XAccessible.hpp>
33 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
34 #include <svtools/fmtfield.hxx>
35 #include <svtools/treelistentry.hxx>
36 #include "document.hxx"
38 using namespace com::sun::star
;
39 using ::com::sun::star::uno::Reference
;
40 using ::com::sun::star::accessibility::XAccessible
;
41 using ::com::sun::star::accessibility::XAccessibleContext
;
44 ScMenuFloatingWindow::MenuItemData::MenuItemData() :
45 mbEnabled(true), mbSeparator(false),
46 mpAction(static_cast<ScCheckListMenuWindow::Action
*>(NULL
)),
47 mpSubMenuWin(static_cast<ScMenuFloatingWindow
*>(NULL
))
51 ScMenuFloatingWindow::SubMenuItemData::SubMenuItemData(ScMenuFloatingWindow
* pParent
) :
53 mnMenuPos(MENU_NOT_SELECTED
),
56 maTimer
.SetTimeoutHdl( LINK(this, ScMenuFloatingWindow::SubMenuItemData
, TimeoutHdl
) );
57 maTimer
.SetTimeout(mpParent
->GetSettings().GetMouseSettings().GetMenuDelay());
60 void ScMenuFloatingWindow::SubMenuItemData::reset()
63 mnMenuPos
= MENU_NOT_SELECTED
;
67 IMPL_LINK_NOARG_TYPED(ScMenuFloatingWindow::SubMenuItemData
, TimeoutHdl
, Timer
*, void)
69 mpParent
->handleMenuTimeout(this);
72 size_t ScMenuFloatingWindow::MENU_NOT_SELECTED
= 999;
74 ScMenuFloatingWindow::ScMenuFloatingWindow(vcl::Window
* pParent
, ScDocument
* pDoc
, sal_uInt16 nMenuStackLevel
) :
75 PopupMenuFloatingWindow(pParent
),
78 maName("ScMenuFloatingWindow"),
79 mnSelectedMenu(MENU_NOT_SELECTED
),
80 mnClickedMenu(MENU_NOT_SELECTED
),
82 mpParentMenu(dynamic_cast<ScMenuFloatingWindow
*>(pParent
))
84 SetMenuStackLevel(nMenuStackLevel
);
85 SetText(OUString("ScMenuFloatingWindow"));
87 const StyleSettings
& rStyle
= GetSettings().GetStyleSettings();
89 sal_Int32 nScaleFactor
= GetDPIScaleFactor();
90 const sal_uInt16 nPopupFontHeight
= 12 * nScaleFactor
;
91 maLabelFont
= rStyle
.GetLabelFont();
92 maLabelFont
.SetHeight(nPopupFontHeight
);
95 ScMenuFloatingWindow::~ScMenuFloatingWindow()
100 void ScMenuFloatingWindow::dispose()
103 for (auto i
= maMenuItems
.begin(); i
!= maMenuItems
.end(); ++i
)
104 i
->mpSubMenuWin
.disposeAndClear();
105 mpParentMenu
.clear();
106 PopupMenuFloatingWindow::dispose();
109 void ScMenuFloatingWindow::PopupModeEnd()
114 void ScMenuFloatingWindow::MouseMove(const MouseEvent
& rMEvt
)
116 const Point
& rPos
= rMEvt
.GetPosPixel();
117 size_t nSelectedMenu
= getEnclosingMenuItem(rPos
);
118 setSelectedMenuItem(nSelectedMenu
, true, false);
120 Window::MouseMove(rMEvt
);
123 void ScMenuFloatingWindow::MouseButtonDown(const MouseEvent
& rMEvt
)
125 const Point
& rPos
= rMEvt
.GetPosPixel();
126 mnClickedMenu
= getEnclosingMenuItem(rPos
);
127 Window::MouseButtonDown(rMEvt
);
130 void ScMenuFloatingWindow::MouseButtonUp(const MouseEvent
& rMEvt
)
132 executeMenuItem(mnClickedMenu
);
133 mnClickedMenu
= MENU_NOT_SELECTED
;
134 Window::MouseButtonUp(rMEvt
);
137 void ScMenuFloatingWindow::KeyInput(const KeyEvent
& rKEvt
)
139 if (maMenuItems
.empty())
141 Window::KeyInput(rKEvt
);
145 const vcl::KeyCode
& rKeyCode
= rKEvt
.GetKeyCode();
146 bool bHandled
= true;
147 size_t nSelectedMenu
= mnSelectedMenu
;
148 size_t nLastMenuPos
= maMenuItems
.size() - 1;
149 switch (rKeyCode
.GetCode())
153 if (nLastMenuPos
== 0)
154 // There is only one menu item. Do nothing.
157 size_t nOldPos
= nSelectedMenu
;
159 if (nSelectedMenu
== MENU_NOT_SELECTED
|| nSelectedMenu
== 0)
160 nSelectedMenu
= nLastMenuPos
;
164 // Loop until a non-separator menu item is found.
165 while (nSelectedMenu
!= nOldPos
)
167 if (maMenuItems
[nSelectedMenu
].mbSeparator
)
172 nSelectedMenu
= nLastMenuPos
;
178 setSelectedMenuItem(nSelectedMenu
, false, false);
183 if (nLastMenuPos
== 0)
184 // There is only one menu item. Do nothing.
187 size_t nOldPos
= nSelectedMenu
;
189 if (nSelectedMenu
== MENU_NOT_SELECTED
|| nSelectedMenu
== nLastMenuPos
)
194 // Loop until a non-separator menu item is found.
195 while (nSelectedMenu
!= nOldPos
)
197 if (maMenuItems
[nSelectedMenu
].mbSeparator
)
199 if (nSelectedMenu
== nLastMenuPos
)
208 setSelectedMenuItem(nSelectedMenu
, false, false);
213 mpParentMenu
->endSubMenu(this);
217 if (mnSelectedMenu
>= maMenuItems
.size() || mnSelectedMenu
== MENU_NOT_SELECTED
)
220 const MenuItemData
& rMenu
= maMenuItems
[mnSelectedMenu
];
221 if (!rMenu
.mbEnabled
|| !rMenu
.mpSubMenuWin
)
224 maOpenTimer
.mnMenuPos
= mnSelectedMenu
;
225 maOpenTimer
.mpSubMenu
= rMenu
.mpSubMenuWin
.get();
230 if (nSelectedMenu
!= MENU_NOT_SELECTED
)
231 executeMenuItem(nSelectedMenu
);
238 Window::KeyInput(rKEvt
);
241 void ScMenuFloatingWindow::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
& /*rRect*/)
243 const StyleSettings
& rStyle
= GetSettings().GetStyleSettings();
245 SetFont(maLabelFont
);
247 Color aBackColor
= rStyle
.GetMenuColor();
248 Color aBorderColor
= rStyle
.GetShadowColor();
250 Rectangle
aCtrlRect(Point(0, 0), GetOutputSizePixel());
253 bool bNativeDrawn
= true;
254 if (rRenderContext
.IsNativeControlSupported(CTRL_MENU_POPUP
, PART_ENTIRE_CONTROL
))
256 rRenderContext
.SetClipRegion();
257 bNativeDrawn
= rRenderContext
.DrawNativeControl(CTRL_MENU_POPUP
, PART_ENTIRE_CONTROL
, aCtrlRect
,
258 ControlState::ENABLED
, ImplControlValue(), OUString());
261 bNativeDrawn
= false;
265 rRenderContext
.SetFillColor(aBackColor
);
266 rRenderContext
.SetLineColor(aBorderColor
);
267 rRenderContext
.DrawRect(aCtrlRect
);
271 rRenderContext
.SetTextColor(rStyle
.GetMenuTextColor());
272 drawAllMenuItems(rRenderContext
);
275 Reference
<XAccessible
> ScMenuFloatingWindow::CreateAccessible()
277 if (!mxAccessible
.is())
279 Reference
<XAccessible
> xAccParent
= mpParentMenu
?
280 mpParentMenu
->GetAccessible() : GetAccessibleParentWindow()->GetAccessible();
282 mxAccessible
.set(new ScAccessibleFilterMenu(xAccParent
, this, maName
, 999));
283 ScAccessibleFilterMenu
* p
= static_cast<ScAccessibleFilterMenu
*>(
286 vector
<MenuItemData
>::const_iterator itr
, itrBeg
= maMenuItems
.begin(), itrEnd
= maMenuItems
.end();
287 for (itr
= itrBeg
; itr
!= itrEnd
; ++itr
)
289 size_t nPos
= ::std::distance(itrBeg
, itr
);
290 p
->appendMenuItem(itr
->maText
, itr
->mbEnabled
, nPos
);
297 void ScMenuFloatingWindow::addMenuItem(const OUString
& rText
, bool bEnabled
, Action
* pAction
)
300 aItem
.maText
= rText
;
301 aItem
.mbEnabled
= bEnabled
;
302 aItem
.mpAction
.reset(pAction
);
303 maMenuItems
.push_back(aItem
);
306 void ScMenuFloatingWindow::addSeparator()
309 aItem
.mbSeparator
= true;
310 maMenuItems
.push_back(aItem
);
313 ScMenuFloatingWindow
* ScMenuFloatingWindow::addSubMenuItem(const OUString
& rText
, bool bEnabled
)
316 aItem
.maText
= rText
;
317 aItem
.mbEnabled
= bEnabled
;
318 aItem
.mpSubMenuWin
.reset(VclPtr
<ScMenuFloatingWindow
>::Create(this, mpDoc
, GetMenuStackLevel()+1));
319 aItem
.mpSubMenuWin
->setName(rText
);
320 maMenuItems
.push_back(aItem
);
321 return aItem
.mpSubMenuWin
.get();
324 void ScMenuFloatingWindow::handlePopupEnd()
326 clearSelectedMenuItem();
329 Size
ScMenuFloatingWindow::getMenuSize() const
331 if (maMenuItems
.empty())
334 vector
<MenuItemData
>::const_iterator itr
= maMenuItems
.begin(), itrEnd
= maMenuItems
.end();
336 for (; itr
!= itrEnd
; ++itr
)
338 if (itr
->mbSeparator
)
341 nTextWidth
= ::std::max(GetTextWidth(itr
->maText
), nTextWidth
);
344 size_t nLastPos
= maMenuItems
.size()-1;
347 getMenuItemPosSize(nLastPos
, aPos
, aSize
);
348 aPos
.X() += nTextWidth
+ 15;
349 aPos
.Y() += aSize
.Height() + 5;
350 return Size(aPos
.X(), aPos
.Y());
353 void ScMenuFloatingWindow::drawMenuItem(vcl::RenderContext
& rRenderContext
, size_t nPos
)
355 if (nPos
>= maMenuItems
.size())
360 getMenuItemPosSize(nPos
, aPos
, aSize
);
362 DecorationView
aDecoView(&rRenderContext
);
364 long nYOffset
= (aSize
.Height() - maLabelFont
.GetHeight())/2;
365 rRenderContext
. DrawCtrlText(Point(aPos
.X()+nXOffset
, aPos
.Y() + nYOffset
), maMenuItems
[nPos
].maText
, 0,
366 maMenuItems
[nPos
].maText
.getLength(),
367 maMenuItems
[nPos
].mbEnabled
? DrawTextFlags::Mnemonic
: DrawTextFlags::Disable
);
369 if (maMenuItems
[nPos
].mpSubMenuWin
)
371 long nFontHeight
= maLabelFont
.GetHeight();
372 Point aMarkerPos
= aPos
;
373 aMarkerPos
.Y() += aSize
.Height() / 2 - nFontHeight
/ 4 + 1;
374 aMarkerPos
.X() += aSize
.Width() - nFontHeight
+ nFontHeight
/ 4;
375 Size
aMarkerSize(nFontHeight
/ 2, nFontHeight
/ 2);
376 aDecoView
.DrawSymbol(Rectangle(aMarkerPos
, aMarkerSize
), SymbolType::SPIN_RIGHT
, GetTextColor());
380 void ScMenuFloatingWindow::drawSeparator(vcl::RenderContext
& rRenderContext
, size_t nPos
)
384 getMenuItemPosSize(nPos
, aPos
, aSize
);
385 Rectangle
aRegion(aPos
,aSize
);
387 if (rRenderContext
.IsNativeControlSupported(CTRL_MENU_POPUP
, PART_ENTIRE_CONTROL
))
389 rRenderContext
.Push(PushFlags::CLIPREGION
);
390 rRenderContext
.IntersectClipRegion(aRegion
);
391 Rectangle
aCtrlRect(Point(0,0), GetOutputSizePixel());
392 rRenderContext
.DrawNativeControl(CTRL_MENU_POPUP
, PART_ENTIRE_CONTROL
, aCtrlRect
,
393 ControlState::ENABLED
, ImplControlValue(), OUString());
395 rRenderContext
.Pop();
398 bool bNativeDrawn
= false;
399 if (rRenderContext
.IsNativeControlSupported(CTRL_MENU_POPUP
, PART_MENU_SEPARATOR
))
401 ControlState nState
= ControlState::NONE
;
402 const MenuItemData
& rData
= maMenuItems
[nPos
];
404 nState
|= ControlState::ENABLED
;
406 bNativeDrawn
= rRenderContext
.DrawNativeControl(CTRL_MENU_POPUP
, PART_MENU_SEPARATOR
,
407 aRegion
, nState
, ImplControlValue(), OUString());
412 const StyleSettings
& rStyle
= rRenderContext
.GetSettings().GetStyleSettings();
413 Point aTmpPos
= aPos
;
414 aTmpPos
.Y() += aSize
.Height() / 2;
415 rRenderContext
.SetLineColor(rStyle
.GetShadowColor());
416 rRenderContext
.DrawLine(aTmpPos
, Point(aSize
.Width() + aTmpPos
.X(), aTmpPos
.Y()));
418 rRenderContext
.SetLineColor(rStyle
.GetLightColor());
419 rRenderContext
.DrawLine(aTmpPos
, Point(aSize
.Width() + aTmpPos
.X(), aTmpPos
.Y()));
420 rRenderContext
.SetLineColor();
424 void ScMenuFloatingWindow::drawAllMenuItems(vcl::RenderContext
& rRenderContext
)
426 size_t n
= maMenuItems
.size();
428 for (size_t i
= 0; i
< n
; ++i
)
430 if (maMenuItems
[i
].mbSeparator
)
433 drawSeparator(rRenderContext
, i
);
438 highlightMenuItem(rRenderContext
, i
, i
== mnSelectedMenu
);
443 void ScMenuFloatingWindow::executeMenuItem(size_t nPos
)
445 if (nPos
>= maMenuItems
.size())
448 if (!maMenuItems
[nPos
].mpAction
)
449 // no action is defined.
452 maMenuItems
[nPos
].mpAction
->execute();
453 terminateAllPopupMenus();
456 void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos
, bool bSubMenuTimer
, bool bEnsureSubMenu
)
458 if (mnSelectedMenu
== nPos
)
464 // Dismiss any child popup menu windows.
465 if (mnSelectedMenu
< maMenuItems
.size() &&
466 maMenuItems
[mnSelectedMenu
].mpSubMenuWin
&&
467 maMenuItems
[mnSelectedMenu
].mpSubMenuWin
->IsVisible())
469 maMenuItems
[mnSelectedMenu
].mpSubMenuWin
->ensureSubMenuNotVisible();
472 // The popup is not visible, yet a menu item is selected. The request
473 // most likely comes from the accessible object. Make sure this
474 // window, as well as all its parent windows are visible.
475 if (!IsVisible() && mpParentMenu
)
476 mpParentMenu
->ensureSubMenuVisible(this);
479 selectMenuItem(mnSelectedMenu
, false, bSubMenuTimer
);
480 selectMenuItem(nPos
, true, bSubMenuTimer
);
481 mnSelectedMenu
= nPos
;
483 fireMenuHighlightedEvent();
486 void ScMenuFloatingWindow::handleMenuTimeout(SubMenuItemData
* pTimer
)
488 if (pTimer
== &maOpenTimer
)
490 // Close any open submenu immediately.
491 if (maCloseTimer
.mpSubMenu
)
493 maCloseTimer
.mpSubMenu
->EndPopupMode();
494 maCloseTimer
.mpSubMenu
= NULL
;
495 maCloseTimer
.maTimer
.Stop();
498 launchSubMenu(false);
500 else if (pTimer
== &maCloseTimer
)
503 if (maCloseTimer
.mpSubMenu
)
505 maOpenTimer
.mpSubMenu
= NULL
;
507 maCloseTimer
.mpSubMenu
->EndPopupMode();
508 maCloseTimer
.mpSubMenu
= NULL
;
511 maOpenTimer
.mnMenuPos
= MENU_NOT_SELECTED
;
516 void ScMenuFloatingWindow::queueLaunchSubMenu(size_t nPos
, ScMenuFloatingWindow
* pMenu
)
521 // Set the submenu on launch queue.
522 if (maOpenTimer
.mpSubMenu
)
524 if (maOpenTimer
.mpSubMenu
== pMenu
)
526 if (pMenu
== maCloseTimer
.mpSubMenu
)
527 maCloseTimer
.reset();
531 // new submenu is being requested.
535 maOpenTimer
.mpSubMenu
= pMenu
;
536 maOpenTimer
.mnMenuPos
= nPos
;
537 maOpenTimer
.maTimer
.Start();
540 void ScMenuFloatingWindow::queueCloseSubMenu()
542 if (!maOpenTimer
.mpSubMenu
)
543 // There is no submenu to close.
546 // Stop any submenu on queue for opening.
547 maOpenTimer
.maTimer
.Stop();
549 maCloseTimer
.mpSubMenu
= maOpenTimer
.mpSubMenu
;
550 maCloseTimer
.mnMenuPos
= maOpenTimer
.mnMenuPos
;
551 maCloseTimer
.maTimer
.Start();
554 void ScMenuFloatingWindow::launchSubMenu(bool bSetMenuPos
)
558 getMenuItemPosSize(maOpenTimer
.mnMenuPos
, aPos
, aSize
);
559 ScMenuFloatingWindow
* pSubMenu
= maOpenTimer
.mpSubMenu
;
564 FloatWinPopupFlags nOldFlags
= GetPopupModeFlags();
565 SetPopupModeFlags(nOldFlags
| FloatWinPopupFlags::NoAppFocusClose
);
566 pSubMenu
->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
567 pSubMenu
->StartPopupMode(
568 Rectangle(aPos
,aSize
), (FloatWinPopupFlags::Right
| FloatWinPopupFlags::GrabFocus
));
569 pSubMenu
->AddPopupModeWindow(this);
571 pSubMenu
->setSelectedMenuItem(0, false, false); // select menu item after the popup becomes fully visible.
572 SetPopupModeFlags(nOldFlags
);
575 void ScMenuFloatingWindow::endSubMenu(ScMenuFloatingWindow
* pSubMenu
)
580 pSubMenu
->EndPopupMode();
583 size_t nMenuPos
= getSubMenuPos(pSubMenu
);
584 if (nMenuPos
!= MENU_NOT_SELECTED
)
586 mnSelectedMenu
= nMenuPos
;
588 fireMenuHighlightedEvent();
592 void ScMenuFloatingWindow::fillMenuItemsToAccessible(ScAccessibleFilterMenu
* pAccMenu
) const
594 vector
<MenuItemData
>::const_iterator itr
, itrBeg
= maMenuItems
.begin(), itrEnd
= maMenuItems
.end();
595 for (itr
= itrBeg
; itr
!= itrEnd
; ++itr
)
597 size_t nPos
= ::std::distance(itrBeg
, itr
);
598 pAccMenu
->appendMenuItem(itr
->maText
, itr
->mbEnabled
, nPos
);
602 void ScMenuFloatingWindow::resizeToFitMenuItems()
604 SetOutputSizePixel(getMenuSize());
607 void ScMenuFloatingWindow::selectMenuItem(size_t nPos
, bool bSelected
, bool bSubMenuTimer
)
609 if (nPos
>= maMenuItems
.size() || nPos
== MENU_NOT_SELECTED
)
615 if (!maMenuItems
[nPos
].mbEnabled
)
626 mpParentMenu
->setSubMenuFocused(this);
630 if (maMenuItems
[nPos
].mpSubMenuWin
)
632 ScMenuFloatingWindow
* pSubMenu
= maMenuItems
[nPos
].mpSubMenuWin
.get();
633 queueLaunchSubMenu(nPos
, pSubMenu
);
641 void ScMenuFloatingWindow::clearSelectedMenuItem()
643 selectMenuItem(mnSelectedMenu
, false, false);
644 mnSelectedMenu
= MENU_NOT_SELECTED
;
647 ScMenuFloatingWindow
* ScMenuFloatingWindow::getSubMenuWindow(size_t nPos
) const
649 if (maMenuItems
.size() <= nPos
)
652 return maMenuItems
[nPos
].mpSubMenuWin
.get();
655 bool ScMenuFloatingWindow::isMenuItemSelected(size_t nPos
) const
657 return nPos
== mnSelectedMenu
;
660 void ScMenuFloatingWindow::setName(const OUString
& rName
)
665 void ScMenuFloatingWindow::highlightMenuItem(vcl::RenderContext
& rRenderContext
, size_t nPos
, bool bSelected
)
667 if (nPos
== MENU_NOT_SELECTED
)
670 const StyleSettings
& rStyle
= rRenderContext
.GetSettings().GetStyleSettings();
671 Color aBackColor
= rStyle
.GetMenuColor();
672 rRenderContext
.SetFillColor(aBackColor
);
673 rRenderContext
.SetLineColor(aBackColor
);
677 getMenuItemPosSize(nPos
, aPos
, aSize
);
678 Rectangle
aRegion(aPos
,aSize
);
680 if (rRenderContext
.IsNativeControlSupported(CTRL_MENU_POPUP
, PART_ENTIRE_CONTROL
))
682 rRenderContext
.Push(PushFlags::CLIPREGION
);
683 rRenderContext
.IntersectClipRegion(Rectangle(aPos
, aSize
));
684 Rectangle
aCtrlRect(Point(0,0), GetOutputSizePixel());
685 rRenderContext
.DrawNativeControl(CTRL_MENU_POPUP
, PART_ENTIRE_CONTROL
, aCtrlRect
, ControlState::ENABLED
,
686 ImplControlValue(), OUString());
687 rRenderContext
.Pop();
690 bool bNativeDrawn
= true;
691 if (rRenderContext
.IsNativeControlSupported(CTRL_MENU_POPUP
, PART_MENU_ITEM
))
693 ControlState nState
= bSelected
? ControlState::SELECTED
: ControlState::NONE
;
694 if (maMenuItems
[nPos
].mbEnabled
)
695 nState
|= ControlState::ENABLED
;
696 bNativeDrawn
= rRenderContext
.DrawNativeControl(CTRL_MENU_POPUP
, PART_MENU_ITEM
,
697 aRegion
, nState
, ImplControlValue(), OUString());
700 bNativeDrawn
= false;
706 aBackColor
= rStyle
.GetMenuHighlightColor();
707 rRenderContext
.SetFillColor(aBackColor
);
708 rRenderContext
.SetLineColor(aBackColor
);
710 rRenderContext
.DrawRect(Rectangle(aPos
,aSize
));
713 Color aTextColor
= bSelected
? rStyle
.GetMenuHighlightTextColor() : rStyle
.GetMenuTextColor();
714 rRenderContext
.SetTextColor(aTextColor
);
715 drawMenuItem(rRenderContext
, nPos
);
718 void ScMenuFloatingWindow::getMenuItemPosSize(size_t nPos
, Point
& rPos
, Size
& rSize
) const
720 size_t nCount
= maMenuItems
.size();
724 const sal_uInt16 nLeftMargin
= 5;
725 const sal_uInt16 nTopMargin
= 5;
726 const sal_uInt16 nMenuItemHeight
= static_cast<sal_uInt16
>(maLabelFont
.GetHeight()*1.8);
727 const sal_uInt16 nSepHeight
= static_cast<sal_uInt16
>(maLabelFont
.GetHeight()*0.8);
729 Point
aPos1(nLeftMargin
, nTopMargin
);
731 for (size_t i
= 0; i
< nPos
; ++i
)
732 rPos
.Y() += maMenuItems
[i
].mbSeparator
? nSepHeight
: nMenuItemHeight
;
734 Size aWndSize
= GetSizePixel();
735 sal_uInt16 nH
= maMenuItems
[nPos
].mbSeparator
? nSepHeight
: nMenuItemHeight
;
736 rSize
= Size(aWndSize
.Width() - nLeftMargin
*2, nH
);
739 size_t ScMenuFloatingWindow::getEnclosingMenuItem(const Point
& rPos
) const
741 size_t n
= maMenuItems
.size();
742 for (size_t i
= 0; i
< n
; ++i
)
746 getMenuItemPosSize(i
, aPos
, aSize
);
747 Rectangle
aRect(aPos
, aSize
);
748 if (aRect
.IsInside(rPos
))
749 return maMenuItems
[i
].mbSeparator
? MENU_NOT_SELECTED
: i
;
751 return MENU_NOT_SELECTED
;
754 size_t ScMenuFloatingWindow::getSubMenuPos(ScMenuFloatingWindow
* pSubMenu
)
756 size_t n
= maMenuItems
.size();
757 for (size_t i
= 0; i
< n
; ++i
)
759 if (maMenuItems
[i
].mpSubMenuWin
.get() == pSubMenu
)
762 return MENU_NOT_SELECTED
;
765 void ScMenuFloatingWindow::fireMenuHighlightedEvent()
767 if (mnSelectedMenu
== MENU_NOT_SELECTED
)
770 if (!mxAccessible
.is())
773 Reference
<XAccessibleContext
> xAccCxt
= mxAccessible
->getAccessibleContext();
777 Reference
<XAccessible
> xAccMenu
= xAccCxt
->getAccessibleChild(mnSelectedMenu
);
781 VclAccessibleEvent
aEvent(VCLEVENT_MENU_HIGHLIGHT
, xAccMenu
);
782 FireVclEvent(&aEvent
);
785 void ScMenuFloatingWindow::setSubMenuFocused(ScMenuFloatingWindow
* pSubMenu
)
787 maCloseTimer
.reset();
788 size_t nMenuPos
= getSubMenuPos(pSubMenu
);
789 if (mnSelectedMenu
!= nMenuPos
)
791 mnSelectedMenu
= nMenuPos
;
796 void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow
* pSubMenu
)
799 mpParentMenu
->ensureSubMenuVisible(this);
801 if (pSubMenu
->IsVisible())
804 // Find the menu position of the submenu.
805 size_t nMenuPos
= getSubMenuPos(pSubMenu
);
806 if (nMenuPos
!= MENU_NOT_SELECTED
)
808 setSelectedMenuItem(nMenuPos
, false, false);
812 getMenuItemPosSize(nMenuPos
, aPos
, aSize
);
814 FloatWinPopupFlags nOldFlags
= GetPopupModeFlags();
815 SetPopupModeFlags(nOldFlags
| FloatWinPopupFlags::NoAppFocusClose
);
816 pSubMenu
->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
817 pSubMenu
->StartPopupMode(
818 Rectangle(aPos
,aSize
), (FloatWinPopupFlags::Right
| FloatWinPopupFlags::GrabFocus
));
819 pSubMenu
->AddPopupModeWindow(this);
820 SetPopupModeFlags(nOldFlags
);
824 void ScMenuFloatingWindow::ensureSubMenuNotVisible()
826 if (mnSelectedMenu
<= maMenuItems
.size() &&
827 maMenuItems
[mnSelectedMenu
].mpSubMenuWin
&&
828 maMenuItems
[mnSelectedMenu
].mpSubMenuWin
->IsVisible())
830 maMenuItems
[mnSelectedMenu
].mpSubMenuWin
->ensureSubMenuNotVisible();
836 void ScMenuFloatingWindow::terminateAllPopupMenus()
840 mpParentMenu
->terminateAllPopupMenus();
843 ScCheckListMenuWindow::Config::Config() :
844 mbAllowEmptySet(true), mbRTL(false)
848 ScCheckListMenuWindow::Member::Member()
856 ScCheckListMenuWindow::CancelButton::CancelButton(ScCheckListMenuWindow
* pParent
) :
857 ::CancelButton(pParent
), mpParent(pParent
) {}
859 ScCheckListMenuWindow::CancelButton::~CancelButton()
864 void ScCheckListMenuWindow::CancelButton::dispose()
867 ::CancelButton::dispose();
870 void ScCheckListMenuWindow::CancelButton::Click()
872 mpParent
->EndPopupMode();
873 ::CancelButton::Click();
876 ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window
* pParent
, ScDocument
* pDoc
) :
877 ScMenuFloatingWindow(pParent
, pDoc
),
878 maEdSearch(VclPtr
<Edit
>::Create(this)),
879 maChecks(VclPtr
<ScCheckListBox
>::Create(this, WB_HASBUTTONS
| WB_HASLINES
| WB_HASLINESATROOT
| WB_HASBUTTONSATROOT
) ),
880 maChkToggleAll(VclPtr
<TriStateBox
>::Create(this, 0)),
881 maBtnSelectSingle(VclPtr
<ImageButton
>::Create(this, 0)),
882 maBtnUnselectSingle(VclPtr
<ImageButton
>::Create(this, 0)),
883 maBtnOk(VclPtr
<OKButton
>::Create(this)),
884 maBtnCancel(VclPtr
<CancelButton
>::Create(this)),
886 mpExtendedData(NULL
),
888 mpPopupEndAction(NULL
),
890 mePrevToggleAllState(TRISTATE_INDET
)
892 sal_Int32 nScaleFactor
= GetDPIScaleFactor();
894 maWndSize
= Size(200 * nScaleFactor
, 330 * nScaleFactor
);
896 maTabStopCtrls
.reserve(8);
897 maTabStopCtrls
.push_back(this);
898 maTabStopCtrls
.push_back(maEdSearch
.get());
899 maTabStopCtrls
.push_back(maChecks
.get());
900 maTabStopCtrls
.push_back(maChkToggleAll
.get());
901 maTabStopCtrls
.push_back(maBtnSelectSingle
.get());
902 maTabStopCtrls
.push_back(maBtnUnselectSingle
.get());
903 maTabStopCtrls
.push_back(maBtnOk
.get());
904 maTabStopCtrls
.push_back(maBtnCancel
.get());
906 // Enable type-ahead search in the check list box.
907 maChecks
->SetStyle(maChecks
->GetStyle() | WB_QUICK_SEARCH
);
910 ScCheckListMenuWindow::~ScCheckListMenuWindow()
915 void ScCheckListMenuWindow::dispose()
917 maEdSearch
.disposeAndClear();
918 maChecks
.disposeAndClear();
919 maChkToggleAll
.disposeAndClear();
920 maBtnSelectSingle
.disposeAndClear();
921 maBtnUnselectSingle
.disposeAndClear();
922 maBtnOk
.disposeAndClear();
923 maBtnCancel
.disposeAndClear();
924 maTabStopCtrls
.clear();
925 ScMenuFloatingWindow::dispose();
928 void ScCheckListMenuWindow::getSectionPosSize(
929 Point
& rPos
, Size
& rSize
, SectionType eType
) const
931 sal_Int32 nScaleFactor
= GetDPIScaleFactor();
933 // constant parameters.
934 const long nSearchBoxMargin
= 10 *nScaleFactor
;
935 const long nListBoxMargin
= 5 * nScaleFactor
; // horizontal distance from the side of the dialog to the listbox border.
936 const long nListBoxInnerPadding
= 5 * nScaleFactor
;
937 const long nTopMargin
= 5 * nScaleFactor
;
938 const long nMenuHeight
= maMenuSize
.getHeight();
939 const long nSingleItemBtnAreaHeight
= 32 * nScaleFactor
; // height of the middle area below the list box where the single-action buttons are.
940 const long nBottomBtnAreaHeight
= 50 * nScaleFactor
; // height of the bottom area where the OK and Cancel buttons are.
941 const long nBtnWidth
= 90 * nScaleFactor
;
942 const long nLabelHeight
= getLabelFont().GetHeight();
943 const long nBtnHeight
= nLabelHeight
* 2;
944 const long nBottomMargin
= 10 * nScaleFactor
;
945 const long nMenuListMargin
= 5 * nScaleFactor
;
946 const long nSearchBoxHeight
= nLabelHeight
* 2;
948 // parameters calculated from constants.
949 const long nListBoxWidth
= maWndSize
.Width() - nListBoxMargin
*2;
950 const long nListBoxHeight
= maWndSize
.Height() - nTopMargin
- nMenuHeight
-
951 nMenuListMargin
- nSearchBoxHeight
- nSearchBoxMargin
- nSingleItemBtnAreaHeight
- nBottomBtnAreaHeight
;
953 const long nSingleBtnAreaY
= nTopMargin
+ nMenuHeight
+ nListBoxHeight
+ nMenuListMargin
+ nSearchBoxHeight
+ nSearchBoxMargin
- 1;
965 rPos
= Point(nSearchBoxMargin
, nTopMargin
+ nMenuHeight
+ nMenuListMargin
);
966 rSize
= Size(maWndSize
.Width() - 2*nSearchBoxMargin
, nSearchBoxHeight
);
969 case LISTBOX_AREA_OUTER
:
971 rPos
= Point(nListBoxMargin
, nTopMargin
+ nMenuHeight
+ nMenuListMargin
+ nSearchBoxHeight
+ nSearchBoxMargin
);
972 rSize
= Size(nListBoxWidth
, nListBoxHeight
);
975 case LISTBOX_AREA_INNER
:
977 rPos
= Point(nListBoxMargin
, nTopMargin
+ nMenuHeight
+ nMenuListMargin
+ nSearchBoxHeight
+ nSearchBoxMargin
);
978 rPos
.X() += nListBoxInnerPadding
;
979 rPos
.Y() += nListBoxInnerPadding
;
981 rSize
= Size(nListBoxWidth
, nListBoxHeight
);
982 rSize
.Width() -= nListBoxInnerPadding
*2;
983 rSize
.Height() -= nListBoxInnerPadding
*2;
986 case SINGLE_BTN_AREA
:
988 rPos
= Point(nListBoxMargin
, nSingleBtnAreaY
);
989 rSize
= Size(nListBoxWidth
, nSingleItemBtnAreaHeight
);
992 case CHECK_TOGGLE_ALL
:
994 long h
= std::min(maChkToggleAll
->CalcMinimumSize().Height(), 26L);
995 rPos
= Point(nListBoxMargin
, nSingleBtnAreaY
);
997 rPos
.Y() += (nSingleItemBtnAreaHeight
- h
)/2;
1001 case BTN_SINGLE_SELECT
:
1003 long h
= 26 * nScaleFactor
;
1004 rPos
= Point(nListBoxMargin
, nSingleBtnAreaY
);
1005 rPos
.X() += nListBoxWidth
- h
- 10 - h
- 10;
1006 rPos
.Y() += (nSingleItemBtnAreaHeight
- h
)/2;
1010 case BTN_SINGLE_UNSELECT
:
1012 long h
= 26 * nScaleFactor
;
1013 rPos
= Point(nListBoxMargin
, nSingleBtnAreaY
);
1014 rPos
.X() += nListBoxWidth
- h
- 10;
1015 rPos
.Y() += (nSingleItemBtnAreaHeight
- h
)/2;
1021 long x
= (maWndSize
.Width() - nBtnWidth
*2)/3;
1022 long y
= maWndSize
.Height() - nBottomMargin
- nBtnHeight
;
1024 rSize
= Size(nBtnWidth
, nBtnHeight
);
1029 long x
= (maWndSize
.Width() - nBtnWidth
*2)/3*2 + nBtnWidth
;
1030 long y
= maWndSize
.Height() - nBottomMargin
- nBtnHeight
;
1032 rSize
= Size(nBtnWidth
, nBtnHeight
);
1040 void ScCheckListMenuWindow::packWindow()
1042 maMenuSize
= getMenuSize();
1044 if (maWndSize
.Width() < maMenuSize
.Width())
1045 // Widen the window to fit the menu items.
1046 maWndSize
.Width() = maMenuSize
.Width();
1048 // Set proper window height based on the number of menu items.
1049 if (maWndSize
.Height() < maMenuSize
.Height()*2.8)
1050 maWndSize
.Height() = maMenuSize
.Height()*2.8;
1052 // TODO: Make sure the window height never exceeds the height of the
1053 // screen. Also do adjustment based on the number of check box items.
1055 SetOutputSizePixel(maWndSize
);
1057 const StyleSettings
& rStyle
= GetSettings().GetStyleSettings();
1061 getSectionPosSize(aPos
, aSize
, WHOLE
);
1062 SetOutputSizePixel(aSize
);
1064 getSectionPosSize(aPos
, aSize
, BTN_OK
);
1065 maBtnOk
->SetPosSizePixel(aPos
, aSize
);
1066 maBtnOk
->SetFont(getLabelFont());
1067 maBtnOk
->SetClickHdl( LINK(this, ScCheckListMenuWindow
, ButtonHdl
) );
1070 getSectionPosSize(aPos
, aSize
, BTN_CANCEL
);
1071 maBtnCancel
->SetPosSizePixel(aPos
, aSize
);
1072 maBtnCancel
->SetFont(getLabelFont());
1073 maBtnCancel
->Show();
1075 getSectionPosSize(aPos
, aSize
, EDIT_SEARCH
);
1076 maEdSearch
->SetPosSizePixel(aPos
, aSize
);
1077 maEdSearch
->SetFont(getLabelFont());
1078 maEdSearch
->SetControlBackground(rStyle
.GetFieldColor());
1079 maEdSearch
->SetPlaceholderText(SC_STRLOAD(RID_POPUP_FILTER
, STR_EDIT_SEARCH_ITEMS
));
1080 maEdSearch
->SetModifyHdl( LINK(this, ScCheckListMenuWindow
, EdModifyHdl
) );
1083 getSectionPosSize(aPos
, aSize
, LISTBOX_AREA_INNER
);
1084 maChecks
->SetPosSizePixel(aPos
, aSize
);
1085 maChecks
->SetFont(getLabelFont());
1086 maChecks
->SetCheckButtonHdl( LINK(this, ScCheckListMenuWindow
, CheckHdl
) );
1089 getSectionPosSize(aPos
, aSize
, CHECK_TOGGLE_ALL
);
1090 maChkToggleAll
->SetPosSizePixel(aPos
, aSize
);
1091 maChkToggleAll
->SetFont(getLabelFont());
1092 maChkToggleAll
->SetText(SC_STRLOAD(RID_POPUP_FILTER
, STR_BTN_TOGGLE_ALL
));
1093 maChkToggleAll
->SetTextColor(rStyle
.GetMenuTextColor());
1094 maChkToggleAll
->SetControlBackground(rStyle
.GetMenuColor());
1095 maChkToggleAll
->SetClickHdl( LINK(this, ScCheckListMenuWindow
, TriStateHdl
) );
1096 maChkToggleAll
->Show();
1098 sal_Int32 nScaleFactor
= GetDPIScaleFactor();
1100 Image
aSingleSelect(ScResId(RID_IMG_SELECT_CURRENT
));
1101 if (nScaleFactor
!= 1)
1103 BitmapEx aBitmap
= aSingleSelect
.GetBitmapEx();
1104 aBitmap
.Scale(nScaleFactor
, nScaleFactor
, BmpScaleFlag::Fast
);
1105 aSingleSelect
= Image(aBitmap
);
1108 getSectionPosSize(aPos
, aSize
, BTN_SINGLE_SELECT
);
1109 maBtnSelectSingle
->SetPosSizePixel(aPos
, aSize
);
1110 maBtnSelectSingle
->SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER
, STR_BTN_SELECT_CURRENT
));
1111 maBtnSelectSingle
->SetModeImage(aSingleSelect
);
1112 maBtnSelectSingle
->SetClickHdl( LINK(this, ScCheckListMenuWindow
, ButtonHdl
) );
1113 maBtnSelectSingle
->Show();
1115 Image
aSingleUnselect(ScResId(RID_IMG_UNSELECT_CURRENT
));
1116 if (nScaleFactor
!= 1)
1118 BitmapEx aBitmap
= aSingleUnselect
.GetBitmapEx();
1119 aBitmap
.Scale(nScaleFactor
, nScaleFactor
, BmpScaleFlag::Fast
);
1120 aSingleUnselect
= Image(aBitmap
);
1123 getSectionPosSize(aPos
, aSize
, BTN_SINGLE_UNSELECT
);
1124 maBtnUnselectSingle
->SetPosSizePixel(aPos
, aSize
);
1125 maBtnUnselectSingle
->SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER
, STR_BTN_UNSELECT_CURRENT
));
1126 maBtnUnselectSingle
->SetModeImage(aSingleUnselect
);
1127 maBtnUnselectSingle
->SetClickHdl( LINK(this, ScCheckListMenuWindow
, ButtonHdl
) );
1128 maBtnUnselectSingle
->Show();
1131 void ScCheckListMenuWindow::setAllMemberState(bool bSet
)
1133 size_t n
= maMembers
.size();
1135 for (size_t i
= 0; i
< n
; ++i
) {
1136 aLabel
= maMembers
[i
].maName
;
1137 if (aLabel
.isEmpty())
1138 aLabel
= ScGlobal::GetRscString(STR_EMPTYDATA
);
1139 maChecks
->ShowCheckEntry( aLabel
, maMembers
[i
].mpParent
, true, bSet
);
1142 if (!maConfig
.mbAllowEmptySet
)
1143 // We need to have at least one member selected.
1144 maBtnOk
->Enable(maChecks
->GetCheckedEntryCount() != 0);
1147 void ScCheckListMenuWindow::selectCurrentMemberOnly(bool bSet
)
1149 setAllMemberState(!bSet
);
1150 SvTreeListEntry
* pEntry
= maChecks
->GetCurEntry();
1153 maChecks
->CheckEntry(pEntry
, bSet
);
1156 void ScCheckListMenuWindow::cycleFocus(bool bReverse
)
1158 maTabStopCtrls
[mnCurTabStop
]->SetFakeFocus(false);
1159 maTabStopCtrls
[mnCurTabStop
]->LoseFocus();
1160 if (mnCurTabStop
== 0)
1161 clearSelectedMenuItem();
1165 if (mnCurTabStop
> 0)
1168 mnCurTabStop
= maTabStopCtrls
.size() - 1;
1173 if (mnCurTabStop
>= maTabStopCtrls
.size())
1176 maTabStopCtrls
[mnCurTabStop
]->SetFakeFocus(true);
1177 maTabStopCtrls
[mnCurTabStop
]->GrabFocus();
1180 IMPL_LINK( ScCheckListMenuWindow
, ButtonHdl
, Button
*, pBtn
)
1182 if (pBtn
== maBtnOk
.get())
1184 else if (pBtn
== maBtnSelectSingle
.get())
1186 selectCurrentMemberOnly(true);
1187 CheckHdl(maChecks
.get());
1189 else if (pBtn
== maBtnUnselectSingle
.get())
1191 selectCurrentMemberOnly(false);
1192 CheckHdl(maChecks
.get());
1197 IMPL_LINK_NOARG(ScCheckListMenuWindow
, TriStateHdl
)
1199 switch (mePrevToggleAllState
)
1201 case TRISTATE_FALSE
:
1202 maChkToggleAll
->SetState(TRISTATE_TRUE
);
1203 setAllMemberState(true);
1206 maChkToggleAll
->SetState(TRISTATE_FALSE
);
1207 setAllMemberState(false);
1209 case TRISTATE_INDET
:
1211 maChkToggleAll
->SetState(TRISTATE_TRUE
);
1212 setAllMemberState(true);
1216 mePrevToggleAllState
= maChkToggleAll
->GetState();
1220 IMPL_LINK_NOARG(ScCheckListMenuWindow
, EdModifyHdl
)
1222 OUString aSearchText
= maEdSearch
->GetText();
1223 aSearchText
= aSearchText
.toAsciiLowerCase();
1224 bool bSearchTextEmpty
= aSearchText
.isEmpty();
1225 size_t n
= maMembers
.size();
1226 size_t nSelCount
= 0;
1227 OUString aLabelDisp
;
1229 for (size_t i
= 0; i
< n
; ++i
)
1231 aLabelDisp
= maMembers
[i
].maName
;
1233 if ( aLabelDisp
.isEmpty() )
1234 aLabelDisp
= ScGlobal::GetRscString( STR_EMPTYDATA
);
1236 if ( bSearchTextEmpty
)
1238 maChecks
->ShowCheckEntry( aLabelDisp
, maMembers
[i
].mpParent
, true, maMembers
[i
].mbVisible
);
1239 if ( maMembers
[i
].mbVisible
)
1244 if ( aLabelDisp
.toAsciiLowerCase().indexOf( aSearchText
) != -1 )
1246 maChecks
->ShowCheckEntry( aLabelDisp
, maMembers
[i
].mpParent
, true, true );
1250 maChecks
->ShowCheckEntry( aLabelDisp
, maMembers
[i
].mpParent
, false, false );
1253 if ( nSelCount
== n
)
1254 maChkToggleAll
->SetState( TRISTATE_TRUE
);
1255 else if ( nSelCount
== 0 )
1256 maChkToggleAll
->SetState( TRISTATE_FALSE
);
1258 maChkToggleAll
->SetState( TRISTATE_INDET
);
1260 if ( !maConfig
.mbAllowEmptySet
)
1261 maBtnOk
->Enable( nSelCount
!= 0);
1266 IMPL_LINK( ScCheckListMenuWindow
, CheckHdl
, SvTreeListBox
*, pChecks
)
1268 if (pChecks
!= maChecks
.get())
1270 SvTreeListEntry
* pEntry
= pChecks
->GetHdlEntry();
1272 maChecks
->CheckEntry( pEntry
, ( pChecks
->GetCheckButtonState( pEntry
) == SV_BUTTON_CHECKED
) );
1273 size_t nNumChecked
= maChecks
->GetCheckedEntryCount();
1274 if (nNumChecked
== maMembers
.size())
1275 // all members visible
1276 maChkToggleAll
->SetState(TRISTATE_TRUE
);
1277 else if (nNumChecked
== 0)
1278 // no members visible
1279 maChkToggleAll
->SetState(TRISTATE_FALSE
);
1281 maChkToggleAll
->SetState(TRISTATE_INDET
);
1283 if (!maConfig
.mbAllowEmptySet
)
1284 // We need to have at least one member selected.
1285 maBtnOk
->Enable(nNumChecked
!= 0);
1287 mePrevToggleAllState
= maChkToggleAll
->GetState();
1291 void ScCheckListMenuWindow::MouseMove(const MouseEvent
& rMEvt
)
1293 ScMenuFloatingWindow::MouseMove(rMEvt
);
1295 size_t nSelectedMenu
= getSelectedMenuItem();
1296 if (nSelectedMenu
== MENU_NOT_SELECTED
)
1297 queueCloseSubMenu();
1300 bool ScCheckListMenuWindow::Notify(NotifyEvent
& rNEvt
)
1302 if (rNEvt
.GetType() == MouseNotifyEvent::KEYUP
)
1304 const KeyEvent
* pKeyEvent
= rNEvt
.GetKeyEvent();
1305 const vcl::KeyCode
& rCode
= pKeyEvent
->GetKeyCode();
1306 bool bShift
= rCode
.IsShift();
1307 if (rCode
.GetCode() == KEY_TAB
)
1313 return ScMenuFloatingWindow::Notify(rNEvt
);
1316 void ScCheckListMenuWindow::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
& rRect
)
1318 ScMenuFloatingWindow::Paint(rRenderContext
, rRect
);
1320 const StyleSettings
& rStyle
= GetSettings().GetStyleSettings();
1321 Color aMemberBackColor
= rStyle
.GetFieldColor();
1322 Color aBorderColor
= rStyle
.GetShadowColor();
1326 getSectionPosSize(aPos
, aSize
, LISTBOX_AREA_OUTER
);
1328 // Member list box background
1329 rRenderContext
.SetFillColor(aMemberBackColor
);
1330 rRenderContext
.SetLineColor(aBorderColor
);
1331 rRenderContext
.DrawRect(Rectangle(aPos
,aSize
));
1333 // Single-action button box
1334 getSectionPosSize(aPos
, aSize
, SINGLE_BTN_AREA
);
1335 rRenderContext
.SetFillColor(rStyle
.GetMenuColor());
1336 rRenderContext
.DrawRect(Rectangle(aPos
,aSize
));
1339 vcl::Window
* ScCheckListMenuWindow::GetPreferredKeyInputWindow()
1341 return maTabStopCtrls
[mnCurTabStop
];
1344 Reference
<XAccessible
> ScCheckListMenuWindow::CreateAccessible()
1346 if (!mxAccessible
.is())
1348 mxAccessible
.set(new ScAccessibleFilterTopWindow(
1349 GetAccessibleParentWindow()->GetAccessible(), this, getName()));
1350 ScAccessibleFilterTopWindow
* pAccTop
= static_cast<ScAccessibleFilterTopWindow
*>(mxAccessible
.get());
1351 fillMenuItemsToAccessible(pAccTop
);
1353 pAccTop
->setAccessibleChild(
1354 maEdSearch
->CreateAccessible(), ScAccessibleFilterTopWindow::EDIT_SEARCH_BOX
);
1355 pAccTop
->setAccessibleChild(
1356 maChecks
->CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX
);
1357 pAccTop
->setAccessibleChild(
1358 maChkToggleAll
->CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL
);
1359 pAccTop
->setAccessibleChild(
1360 maBtnSelectSingle
->CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_ON_BTN
);
1361 pAccTop
->setAccessibleChild(
1362 maBtnUnselectSingle
->CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_OFF_BTN
);
1363 pAccTop
->setAccessibleChild(
1364 maBtnOk
->CreateAccessible(), ScAccessibleFilterTopWindow::OK_BTN
);
1365 pAccTop
->setAccessibleChild(
1366 maBtnCancel
->CreateAccessible(), ScAccessibleFilterTopWindow::CANCEL_BTN
);
1369 return mxAccessible
;
1372 void ScCheckListMenuWindow::setMemberSize(size_t n
)
1374 maMembers
.reserve(n
);
1377 void ScCheckListMenuWindow::addDateMember(const OUString
& rsName
, double nVal
, bool bVisible
)
1379 ScDocument
* pDoc
= getDoc();
1380 SvNumberFormatter
* pFormatter
= pDoc
->GetFormatTable();
1382 // Convert the numeric date value to a date object.
1383 Date aDate
= *(pFormatter
->GetNullDate());
1384 aDate
+= static_cast<long>(rtl::math::approxFloor(nVal
));
1386 sal_uInt16 nYear
= aDate
.GetYear();
1387 sal_uInt16 nMonth
= aDate
.GetMonth();
1388 sal_uInt16 nDay
= aDate
.GetDay();
1390 // Get the localized month name list.
1391 CalendarWrapper
* pCalendar
= ScGlobal::GetCalendar();
1392 uno::Sequence
<i18n::CalendarItem2
> aMonths
= pCalendar
->getMonths();
1393 if (aMonths
.getLength() < nMonth
)
1396 OUString aYearName
= OUString::number(nYear
);
1397 OUString aMonthName
= aMonths
[nMonth
-1].FullName
;
1398 OUString aDayName
= OUString::number(nDay
);
1400 maChecks
->SetUpdateMode(false);
1402 SvTreeListEntry
* pYearEntry
= maChecks
->FindEntry(NULL
, aYearName
);
1405 pYearEntry
= maChecks
->InsertEntry(aYearName
, NULL
, true);
1407 aMemYear
.maName
= aYearName
;
1408 aMemYear
.maRealName
= rsName
;
1409 aMemYear
.mbDate
= true;
1410 aMemYear
.mbLeaf
= false;
1411 aMemYear
.mbVisible
= bVisible
;
1412 aMemYear
.mpParent
= NULL
;
1413 maMembers
.push_back(aMemYear
);
1416 SvTreeListEntry
* pMonthEntry
= maChecks
->FindEntry(pYearEntry
, aMonthName
);
1419 pMonthEntry
= maChecks
->InsertEntry(aMonthName
, pYearEntry
, true);
1421 aMemMonth
.maName
= aMonthName
;
1422 aMemMonth
.maRealName
= rsName
;
1423 aMemMonth
.mbDate
= true;
1424 aMemMonth
.mbLeaf
= false;
1425 aMemMonth
.mbVisible
= bVisible
;
1426 aMemMonth
.mpParent
= pYearEntry
;
1427 maMembers
.push_back(aMemMonth
);
1430 SvTreeListEntry
* pDayEntry
= maChecks
->FindEntry(pMonthEntry
, aDayName
);
1433 maChecks
->InsertEntry(aDayName
, pMonthEntry
, false);
1435 aMemDay
.maName
= aDayName
;
1436 aMemDay
.maRealName
= rsName
;
1437 aMemDay
.mbDate
= true;
1438 aMemDay
.mbLeaf
= true;
1439 aMemDay
.mbVisible
= bVisible
;
1440 aMemDay
.mpParent
= pMonthEntry
;
1441 maMembers
.push_back(aMemDay
);
1444 maChecks
->SetUpdateMode(true);
1447 void ScCheckListMenuWindow::addMember(const OUString
& rName
, bool bVisible
)
1450 aMember
.maName
= rName
;
1451 aMember
.mbDate
= false;
1452 aMember
.mbLeaf
= true;
1453 aMember
.mbVisible
= bVisible
;
1454 aMember
.mpParent
= NULL
;
1455 maMembers
.push_back(aMember
);
1458 ScCheckListBox::ScCheckListBox( vcl::Window
* pParent
, WinBits nWinStyle
)
1459 : SvTreeListBox( pParent
, nWinStyle
), mpCheckButton( NULL
)
1464 SvTreeListEntry
* ScCheckListBox::FindEntry( SvTreeListEntry
* pParent
, const OUString
& sNode
)
1466 sal_uInt16 nRootPos
= 0;
1467 SvTreeListEntry
* pEntry
= pParent
? FirstChild( pParent
) : GetEntry( nRootPos
);
1470 if ( sNode
.equals(GetEntryText( pEntry
)) )
1473 pEntry
= pParent
? NextSibling( pEntry
) : GetEntry( ++nRootPos
);
1478 void ScCheckListBox::Init()
1480 mpCheckButton
= new SvLBoxButtonData( this );
1481 EnableCheckButton( mpCheckButton
);
1482 SetNodeDefaultImages();
1485 bool ScCheckListBox::IsChecked( const OUString
& sName
, SvTreeListEntry
* pParent
)
1487 SvTreeListEntry
* pEntry
= FindEntry( pParent
, sName
);
1488 if ( pEntry
&& GetCheckButtonState( pEntry
) == SV_BUTTON_CHECKED
)
1493 void ScCheckListBox::CheckEntry( const OUString
& sName
, SvTreeListEntry
* pParent
, bool bCheck
)
1495 SvTreeListEntry
* pEntry
= FindEntry( pParent
, sName
);
1497 CheckEntry( pEntry
, bCheck
);
1500 // Recursively check all children of pParent
1501 void ScCheckListBox::CheckAllChildren( SvTreeListEntry
* pParent
, bool bCheck
)
1505 SetCheckButtonState(
1506 pParent
, bCheck
? SvButtonState( SV_BUTTON_CHECKED
) :
1507 SvButtonState( SV_BUTTON_UNCHECKED
) );
1509 SvTreeListEntry
* pEntry
= pParent
? FirstChild( pParent
) : First();
1512 CheckAllChildren( pEntry
, bCheck
);
1513 pEntry
= NextSibling( pEntry
);
1517 void ScCheckListBox::CheckEntry( SvTreeListEntry
* pParent
, bool bCheck
)
1519 // recursively check all items below pParent
1520 CheckAllChildren( pParent
, bCheck
);
1521 // checking pParent can affect ancestors, e.g. if ancestor is unchecked and pParent is
1522 // now checked then the ancestor needs to be checked also
1523 SvTreeListEntry
* pAncestor
= GetParent(pParent
);
1528 // if any first level children checked then ancestor
1529 // needs to be checked, similarly if no first level children
1530 // checked then ancestor needs to be unchecked
1531 SvTreeListEntry
* pChild
= FirstChild( pAncestor
);
1532 bool bChildChecked
= false;
1536 if ( GetCheckButtonState( pChild
) == SV_BUTTON_CHECKED
)
1538 bChildChecked
= true;
1541 pChild
= NextSibling( pChild
);
1543 SetCheckButtonState(
1544 pAncestor
, bChildChecked
? SvButtonState( SV_BUTTON_CHECKED
) :
1545 SvButtonState( SV_BUTTON_UNCHECKED
) );
1546 pAncestor
= GetParent(pAncestor
);
1551 void ScCheckListBox::ShowCheckEntry( const OUString
& sName
, SvTreeListEntry
* pParent
, bool bShow
, bool bCheck
)
1553 SvTreeListEntry
* pEntry
= FindEntry( pParent
, sName
);
1558 pEntry
= InsertEntry(
1559 sName
, NULL
, false, TREELIST_APPEND
, NULL
,
1560 SvLBoxButtonKind_enabledCheckbox
);
1562 SetCheckButtonState(
1563 pEntry
, bCheck
? SV_BUTTON_CHECKED
: SV_BUTTON_UNCHECKED
);
1566 CheckEntry( pEntry
, bCheck
);
1569 RemoveParentKeepChildren( pEntry
);
1572 SvTreeListEntry
* ScCheckListBox::CountCheckedEntries( SvTreeListEntry
* pParent
, sal_uLong
& nCount
) const
1574 if ( pParent
&& GetCheckButtonState( pParent
) == SV_BUTTON_CHECKED
)
1576 // Iterate over the children
1577 SvTreeListEntry
* pEntry
= pParent
? FirstChild( pParent
) : First();
1580 CountCheckedEntries( pEntry
, nCount
);
1581 pEntry
= NextSibling( pEntry
);
1586 sal_uInt16
ScCheckListBox::GetCheckedEntryCount() const
1588 sal_uLong nCount
= 0;
1589 CountCheckedEntries( NULL
, nCount
);
1593 void ScCheckListBox::ExpandChildren( SvTreeListEntry
* pParent
)
1597 // Iterate over the children
1598 SvTreeListEntry
* pEntry
= pParent
? FirstChild( pParent
) : First();
1601 ExpandChildren( pEntry
);
1602 pEntry
= NextSibling( pEntry
);
1606 void ScCheckListBox::KeyInput( const KeyEvent
& rKEvt
)
1608 const vcl::KeyCode
& rKey
= rKEvt
.GetKeyCode();
1610 if ( rKey
.GetCode() == KEY_RETURN
|| rKey
.GetCode() == KEY_SPACE
)
1612 SvTreeListEntry
* pEntry
= GetCurEntry();
1615 bool bCheck
= ( GetCheckButtonState( pEntry
) == SV_BUTTON_CHECKED
);
1616 CheckEntry( pEntry
, !bCheck
);
1617 if ( bCheck
!= ( GetCheckButtonState( pEntry
) == SV_BUTTON_CHECKED
) )
1621 else if ( GetEntryCount() )
1622 SvTreeListBox::KeyInput( rKEvt
);
1625 void ScCheckListMenuWindow::initMembers()
1627 size_t n
= maMembers
.size();
1628 size_t nVisMemCount
= 0;
1630 maChecks
->SetUpdateMode(false);
1631 maChecks
->GetModel()->EnableInvalidate(false);
1633 for (size_t i
= 0; i
< n
; ++i
)
1635 if (maMembers
[i
].mbDate
)
1637 maChecks
->CheckEntry(maMembers
[i
].maName
, maMembers
[i
].mpParent
, maMembers
[i
].mbVisible
);
1638 // Expand first node of checked dates
1639 if (!maMembers
[i
].mpParent
&& maChecks
->IsChecked(maMembers
[i
].maName
, maMembers
[i
].mpParent
))
1641 SvTreeListEntry
* pEntry
= maChecks
->FindEntry(NULL
, maMembers
[i
].maName
);
1643 maChecks
->Expand(pEntry
);
1648 OUString aLabel
= maMembers
[i
].maName
;
1649 if (aLabel
.isEmpty())
1650 aLabel
= ScGlobal::GetRscString(STR_EMPTYDATA
);
1651 SvTreeListEntry
* pEntry
= maChecks
->InsertEntry(
1652 aLabel
, NULL
, false, TREELIST_APPEND
, NULL
,
1653 SvLBoxButtonKind_enabledCheckbox
);
1655 maChecks
->SetCheckButtonState(
1656 pEntry
, maMembers
[i
].mbVisible
? SV_BUTTON_CHECKED
: SV_BUTTON_UNCHECKED
);
1659 if (maMembers
[i
].mbVisible
)
1662 if (nVisMemCount
== n
)
1664 // all members visible
1665 maChkToggleAll
->SetState(TRISTATE_TRUE
);
1666 mePrevToggleAllState
= TRISTATE_TRUE
;
1668 else if (nVisMemCount
== 0)
1670 // no members visible
1671 maChkToggleAll
->SetState(TRISTATE_FALSE
);
1672 mePrevToggleAllState
= TRISTATE_FALSE
;
1676 maChkToggleAll
->SetState(TRISTATE_INDET
);
1677 mePrevToggleAllState
= TRISTATE_INDET
;
1680 maChecks
->GetModel()->EnableInvalidate(true);
1681 maChecks
->SetUpdateMode(true);
1684 void ScCheckListMenuWindow::setConfig(const Config
& rConfig
)
1689 bool ScCheckListMenuWindow::isAllSelected() const
1691 return maChkToggleAll
->IsChecked();
1694 void ScCheckListMenuWindow::getResult(ResultType
& rResult
)
1697 size_t n
= maMembers
.size();
1698 for (size_t i
= 0; i
< n
; ++i
)
1700 if ( maMembers
[i
].mbLeaf
)
1702 OUString aLabel
= maMembers
[i
].maName
;
1703 if (aLabel
.isEmpty())
1704 aLabel
= ScGlobal::GetRscString(STR_EMPTYDATA
);
1705 bool bState
= maChecks
->IsChecked( aLabel
, maMembers
[i
].mpParent
);
1707 if ( maMembers
[i
].mbDate
)
1708 sName
= maMembers
[i
].maRealName
;
1710 sName
= maMembers
[i
].maName
;
1711 aResult
.insert(ResultType::value_type(sName
, bState
));
1714 rResult
.swap(aResult
);
1717 void ScCheckListMenuWindow::launch(const Rectangle
& rRect
)
1720 if (!maConfig
.mbAllowEmptySet
)
1721 // We need to have at least one member selected.
1722 maBtnOk
->Enable(maChecks
->GetCheckedEntryCount() != 0);
1724 Rectangle
aRect(rRect
);
1727 // In RTL mode, the logical "left" is visual "right".
1728 long nLeft
= aRect
.Left() - aRect
.GetWidth();
1729 aRect
.Left() = nLeft
;
1731 else if (maWndSize
.Width() < aRect
.GetWidth())
1733 // Target rectangle (i.e. cell width) is wider than the window.
1734 // Simulate right-aligned launch by modifying the target rectangle
1736 long nDiff
= aRect
.GetWidth() - maWndSize
.Width();
1737 aRect
.Left() += nDiff
;
1740 StartPopupMode(aRect
, (FloatWinPopupFlags::Down
| FloatWinPopupFlags::GrabFocus
));
1741 cycleFocus(); // Set initial focus to the check list box.
1744 void ScCheckListMenuWindow::close(bool bOK
)
1746 if (bOK
&& mpOKAction
.get())
1747 mpOKAction
->execute();
1752 void ScCheckListMenuWindow::setExtendedData(ExtendedData
* p
)
1754 mpExtendedData
.reset(p
);
1757 ScCheckListMenuWindow::ExtendedData
* ScCheckListMenuWindow::getExtendedData()
1759 return mpExtendedData
.get();
1762 void ScCheckListMenuWindow::setOKAction(Action
* p
)
1764 mpOKAction
.reset(p
);
1767 void ScCheckListMenuWindow::setPopupEndAction(Action
* p
)
1769 mpPopupEndAction
.reset(p
);
1772 void ScCheckListMenuWindow::handlePopupEnd()
1774 clearSelectedMenuItem();
1775 if (mpPopupEndAction
)
1776 mpPopupEndAction
->execute();
1779 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */