Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / ui / cctrl / checklistmenu.cxx
blob2cde8eb453ea40f31b4041062fa9f07de9b97906
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
24 #include <vcl/decoview.hxx>
25 #include <tools/wintypes.hxx>
27 #include "AccessibleFilterMenu.hxx"
28 #include "AccessibleFilterTopWindow.hxx"
30 #include <com/sun/star/accessibility/XAccessible.hpp>
31 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
32 #include "svtools/fmtfield.hxx"
33 #include "document.hxx"
35 using ::com::sun::star::uno::Reference;
36 using ::com::sun::star::accessibility::XAccessible;
37 using ::com::sun::star::accessibility::XAccessibleContext;
38 using ::std::vector;
39 using ::boost::unordered_map;
40 using ::std::auto_ptr;
42 ScMenuFloatingWindow::MenuItemData::MenuItemData() :
43 mbEnabled(true), mbSeparator(false),
44 mpAction(static_cast<ScCheckListMenuWindow::Action*>(NULL)),
45 mpSubMenuWin(static_cast<ScMenuFloatingWindow*>(NULL))
49 // ----------------------------------------------------------------------------
51 ScMenuFloatingWindow::SubMenuItemData::SubMenuItemData(ScMenuFloatingWindow* pParent) :
52 mpSubMenu(NULL),
53 mnMenuPos(MENU_NOT_SELECTED),
54 mpParent(pParent)
56 maTimer.SetTimeoutHdl( LINK(this, ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl) );
57 maTimer.SetTimeout(mpParent->GetSettings().GetMouseSettings().GetMenuDelay());
60 void ScMenuFloatingWindow::SubMenuItemData::reset()
62 mpSubMenu = NULL;
63 mnMenuPos = MENU_NOT_SELECTED;
64 maTimer.Stop();
67 IMPL_LINK_NOARG(ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl)
69 mpParent->handleMenuTimeout(this);
70 return 0;
73 // ----------------------------------------------------------------------------
75 size_t ScMenuFloatingWindow::MENU_NOT_SELECTED = 999;
77 ScMenuFloatingWindow::ScMenuFloatingWindow(Window* pParent, ScDocument* pDoc, sal_uInt16 nMenuStackLevel) :
78 PopupMenuFloatingWindow(pParent),
79 maOpenTimer(this),
80 maCloseTimer(this),
81 maName("ScMenuFloatingWindow"),
82 mnSelectedMenu(MENU_NOT_SELECTED),
83 mnClickedMenu(MENU_NOT_SELECTED),
84 mpDoc(pDoc),
85 mpParentMenu(dynamic_cast<ScMenuFloatingWindow*>(pParent))
87 SetMenuStackLevel(nMenuStackLevel);
89 // TODO: How do we get the right font to use here ?
90 const sal_uInt16 nPopupFontHeight = 12;
91 const StyleSettings& rStyle = GetSettings().GetStyleSettings();
92 maLabelFont = rStyle.GetLabelFont();
93 maLabelFont.SetHeight(nPopupFontHeight);
94 SetFont(maLabelFont);
96 SetText( OUString("ScMenuFloatingWindow") );
99 ScMenuFloatingWindow::~ScMenuFloatingWindow()
101 EndPopupMode();
104 void ScMenuFloatingWindow::PopupModeEnd()
106 handlePopupEnd();
109 void ScMenuFloatingWindow::MouseMove(const MouseEvent& rMEvt)
111 const Point& rPos = rMEvt.GetPosPixel();
112 size_t nSelectedMenu = getEnclosingMenuItem(rPos);
113 setSelectedMenuItem(nSelectedMenu, true, false);
115 Window::MouseMove(rMEvt);
118 void ScMenuFloatingWindow::MouseButtonDown(const MouseEvent& rMEvt)
120 const Point& rPos = rMEvt.GetPosPixel();
121 mnClickedMenu = getEnclosingMenuItem(rPos);
122 Window::MouseButtonDown(rMEvt);
125 void ScMenuFloatingWindow::MouseButtonUp(const MouseEvent& rMEvt)
127 executeMenuItem(mnClickedMenu);
128 mnClickedMenu = MENU_NOT_SELECTED;
129 Window::MouseButtonUp(rMEvt);
132 void ScMenuFloatingWindow::KeyInput(const KeyEvent& rKEvt)
134 if (maMenuItems.empty())
136 Window::KeyInput(rKEvt);
137 return;
140 const KeyCode& rKeyCode = rKEvt.GetKeyCode();
141 bool bHandled = true;
142 size_t nSelectedMenu = mnSelectedMenu;
143 size_t nLastMenuPos = maMenuItems.size() - 1;
144 switch (rKeyCode.GetCode())
146 case KEY_UP:
148 if (nLastMenuPos == 0)
149 // There is only one menu item. Do nothing.
150 break;
152 size_t nOldPos = nSelectedMenu;
154 if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == 0)
155 nSelectedMenu = nLastMenuPos;
156 else
157 --nSelectedMenu;
159 // Loop until a non-separator menu item is found.
160 while (nSelectedMenu != nOldPos)
162 if (maMenuItems[nSelectedMenu].mbSeparator)
164 if (nSelectedMenu)
165 --nSelectedMenu;
166 else
167 nSelectedMenu = nLastMenuPos;
169 else
170 break;
173 setSelectedMenuItem(nSelectedMenu, false, false);
175 break;
176 case KEY_DOWN:
178 if (nLastMenuPos == 0)
179 // There is only one menu item. Do nothing.
180 break;
182 size_t nOldPos = nSelectedMenu;
184 if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == nLastMenuPos)
185 nSelectedMenu = 0;
186 else
187 ++nSelectedMenu;
189 // Loop until a non-separator menu item is found.
190 while (nSelectedMenu != nOldPos)
192 if (maMenuItems[nSelectedMenu].mbSeparator)
194 if (nSelectedMenu == nLastMenuPos)
195 nSelectedMenu = 0;
196 else
197 ++nSelectedMenu;
199 else
200 break;
203 setSelectedMenuItem(nSelectedMenu, false, false);
205 break;
206 case KEY_LEFT:
207 if (mpParentMenu)
208 mpParentMenu->endSubMenu(this);
209 break;
210 case KEY_RIGHT:
212 if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
213 break;
215 const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
216 if (!rMenu.mbEnabled || !rMenu.mpSubMenuWin)
217 break;
219 maOpenTimer.mnMenuPos = mnSelectedMenu;
220 maOpenTimer.mpSubMenu = rMenu.mpSubMenuWin.get();
221 launchSubMenu(true);
223 break;
224 case KEY_RETURN:
225 if (nSelectedMenu != MENU_NOT_SELECTED)
226 executeMenuItem(nSelectedMenu);
227 break;
228 default:
229 bHandled = false;
232 if (!bHandled)
233 Window::KeyInput(rKEvt);
236 void ScMenuFloatingWindow::Paint(const Rectangle& /*rRect*/)
238 const StyleSettings& rStyle = GetSettings().GetStyleSettings();
239 Color aBackColor = rStyle.GetMenuColor();
240 Color aBorderColor = rStyle.GetShadowColor();
242 Rectangle aCtrlRect(Point(0, 0), GetOutputSizePixel());
244 // Window background
245 bool bNativeDrawn = true;
246 if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
248 SetClipRegion();
249 bNativeDrawn = DrawNativeControl(
250 CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
251 ImplControlValue(), OUString());
253 else
254 bNativeDrawn = false;
256 if (!bNativeDrawn)
258 SetFillColor(aBackColor);
259 SetLineColor(aBorderColor);
260 DrawRect(aCtrlRect);
263 // Menu items
264 SetTextColor(rStyle.GetMenuTextColor());
265 drawAllMenuItems();
268 Reference<XAccessible> ScMenuFloatingWindow::CreateAccessible()
270 if (!mxAccessible.is())
272 Reference<XAccessible> xAccParent = mpParentMenu ?
273 mpParentMenu->GetAccessible() : GetAccessibleParentWindow()->GetAccessible();
275 mxAccessible.set(new ScAccessibleFilterMenu(xAccParent, this, maName, 999));
276 ScAccessibleFilterMenu* p = static_cast<ScAccessibleFilterMenu*>(
277 mxAccessible.get());
279 vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
280 for (itr = itrBeg; itr != itrEnd; ++itr)
282 size_t nPos = ::std::distance(itrBeg, itr);
283 p->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
287 return mxAccessible;
290 void ScMenuFloatingWindow::addMenuItem(const OUString& rText, bool bEnabled, Action* pAction)
292 MenuItemData aItem;
293 aItem.maText = rText;
294 aItem.mbEnabled = bEnabled;
295 aItem.mpAction.reset(pAction);
296 maMenuItems.push_back(aItem);
299 void ScMenuFloatingWindow::addSeparator()
301 MenuItemData aItem;
302 aItem.mbSeparator = true;
303 maMenuItems.push_back(aItem);
306 ScMenuFloatingWindow* ScMenuFloatingWindow::addSubMenuItem(const OUString& rText, bool bEnabled)
308 MenuItemData aItem;
309 aItem.maText = rText;
310 aItem.mbEnabled = bEnabled;
311 aItem.mpSubMenuWin.reset(new ScMenuFloatingWindow(this, mpDoc, GetMenuStackLevel()+1));
312 aItem.mpSubMenuWin->setName(rText);
313 maMenuItems.push_back(aItem);
314 return aItem.mpSubMenuWin.get();
317 void ScMenuFloatingWindow::handlePopupEnd()
319 clearSelectedMenuItem();
322 Size ScMenuFloatingWindow::getMenuSize() const
324 if (maMenuItems.empty())
325 return Size();
327 vector<MenuItemData>::const_iterator itr = maMenuItems.begin(), itrEnd = maMenuItems.end();
328 long nTextWidth = 0;
329 for (; itr != itrEnd; ++itr)
331 if (itr->mbSeparator)
332 continue;
334 nTextWidth = ::std::max(GetTextWidth(itr->maText), nTextWidth);
337 size_t nLastPos = maMenuItems.size()-1;
338 Point aPos;
339 Size aSize;
340 getMenuItemPosSize(nLastPos, aPos, aSize);
341 aPos.X() += nTextWidth + 15;
342 aPos.Y() += aSize.Height() + 5;
343 return Size(aPos.X(), aPos.Y());
346 void ScMenuFloatingWindow::drawMenuItem(size_t nPos)
348 if (nPos >= maMenuItems.size())
349 return;
351 Point aPos;
352 Size aSize;
353 getMenuItemPosSize(nPos, aPos, aSize);
355 DecorationView aDecoView(this);
356 long nXOffset = 5;
357 long nYOffset = (aSize.Height() - maLabelFont.GetHeight())/2;
358 DrawCtrlText(Point(aPos.X()+nXOffset, aPos.Y() + nYOffset), maMenuItems[nPos].maText, 0, STRING_LEN,
359 maMenuItems[nPos].mbEnabled ? TEXT_DRAW_MNEMONIC : TEXT_DRAW_DISABLE);
361 if (maMenuItems[nPos].mpSubMenuWin)
363 long nFontHeight = maLabelFont.GetHeight();
364 Point aMarkerPos = aPos;
365 aMarkerPos.Y() += aSize.Height()/2 - nFontHeight/4 + 1;
366 aMarkerPos.X() += aSize.Width() - nFontHeight + nFontHeight/4;
367 Size aMarkerSize(nFontHeight/2, nFontHeight/2);
368 aDecoView.DrawSymbol(Rectangle(aMarkerPos, aMarkerSize),
369 SYMBOL_SPIN_RIGHT, GetTextColor(), 0);
373 void ScMenuFloatingWindow::drawSeparator(size_t nPos)
375 Point aPos;
376 Size aSize;
377 getMenuItemPosSize(nPos, aPos, aSize);
378 Rectangle aRegion(aPos,aSize);
380 if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
382 Push(PUSH_CLIPREGION);
383 IntersectClipRegion(aRegion);
384 Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
385 DrawNativeControl(
386 CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
387 ImplControlValue(), OUString());
389 Pop();
392 bool bNativeDrawn = false;
393 if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_SEPARATOR))
395 ControlState nState = 0;
396 const MenuItemData& rData = maMenuItems[nPos];
397 if (rData.mbEnabled)
398 nState |= CTRL_STATE_ENABLED;
400 bNativeDrawn = DrawNativeControl(
401 CTRL_MENU_POPUP, PART_MENU_SEPARATOR,
402 aRegion, nState, ImplControlValue(), OUString());
405 if (!bNativeDrawn)
407 const StyleSettings& rStyle = GetSettings().GetStyleSettings();
408 Point aTmpPos = aPos;
409 aTmpPos.Y() += aSize.Height()/2;
410 SetLineColor(rStyle.GetShadowColor());
411 DrawLine(aTmpPos, Point(aSize.Width()+aTmpPos.X(), aTmpPos.Y()));
412 ++aTmpPos.Y();
413 SetLineColor(rStyle.GetLightColor());
414 DrawLine(aTmpPos, Point(aSize.Width()+aTmpPos.X(), aTmpPos.Y()));
415 SetLineColor();
419 void ScMenuFloatingWindow::drawAllMenuItems()
421 size_t n = maMenuItems.size();
422 for (size_t i = 0; i < n; ++i)
424 if (maMenuItems[i].mbSeparator)
425 // Separator
426 drawSeparator(i);
427 else
428 // Normal menu item
429 highlightMenuItem(i, i == mnSelectedMenu);
433 const Font& ScMenuFloatingWindow::getLabelFont() const
435 return maLabelFont;
438 void ScMenuFloatingWindow::executeMenuItem(size_t nPos)
440 if (nPos >= maMenuItems.size())
441 return;
443 if (!maMenuItems[nPos].mpAction)
444 // no action is defined.
445 return;
447 maMenuItems[nPos].mpAction->execute();
448 terminateAllPopupMenus();
451 void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu)
453 if (mnSelectedMenu == nPos)
454 // nothing to do.
455 return;
457 if (bEnsureSubMenu)
459 // Dismiss any child popup menu windows.
460 if (mnSelectedMenu < maMenuItems.size() &&
461 maMenuItems[mnSelectedMenu].mpSubMenuWin &&
462 maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
464 maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
467 // The popup is not visible, yet a menu item is selected. The request
468 // most likely comes from the accessible object. Make sure this
469 // window, as well as all its parent windows are visible.
470 if (!IsVisible() && mpParentMenu)
471 mpParentMenu->ensureSubMenuVisible(this);
474 selectMenuItem(mnSelectedMenu, false, bSubMenuTimer);
475 selectMenuItem(nPos, true, bSubMenuTimer);
476 mnSelectedMenu = nPos;
478 fireMenuHighlightedEvent();
481 size_t ScMenuFloatingWindow::getSelectedMenuItem() const
483 return mnSelectedMenu;
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)
502 // end submenu.
503 if (maCloseTimer.mpSubMenu)
505 maOpenTimer.mpSubMenu = NULL;
507 maCloseTimer.mpSubMenu->EndPopupMode();
508 maCloseTimer.mpSubMenu = NULL;
510 highlightMenuItem(maOpenTimer.mnMenuPos, false);
511 maOpenTimer.mnMenuPos = MENU_NOT_SELECTED;
516 void ScMenuFloatingWindow::queueLaunchSubMenu(size_t nPos, ScMenuFloatingWindow* pMenu)
518 if (!pMenu)
519 return;
521 // Set the submenu on launch queue.
522 if (maOpenTimer.mpSubMenu)
524 if (maOpenTimer.mpSubMenu == pMenu)
526 if (pMenu == maCloseTimer.mpSubMenu)
527 maCloseTimer.reset();
528 return;
531 // new submenu is being requested.
532 queueCloseSubMenu();
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.
544 return;
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)
556 Point aPos;
557 Size aSize;
558 getMenuItemPosSize(maOpenTimer.mnMenuPos, aPos, aSize);
559 ScMenuFloatingWindow* pSubMenu = maOpenTimer.mpSubMenu;
561 if (!pSubMenu)
562 return;
564 sal_uInt32 nOldFlags = GetPopupModeFlags();
565 SetPopupModeFlags(nOldFlags | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE);
566 pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
567 pSubMenu->StartPopupMode(
568 Rectangle(aPos,aSize), (FLOATWIN_POPUPMODE_RIGHT | FLOATWIN_POPUPMODE_GRABFOCUS));
569 pSubMenu->AddPopupModeWindow(this);
570 if (bSetMenuPos)
571 pSubMenu->setSelectedMenuItem(0, false, false); // select menu item after the popup becomes fully visible.
572 SetPopupModeFlags(nOldFlags);
575 void ScMenuFloatingWindow::endSubMenu(ScMenuFloatingWindow* pSubMenu)
577 if (!pSubMenu)
578 return;
580 pSubMenu->EndPopupMode();
581 maOpenTimer.reset();
583 size_t nMenuPos = getSubMenuPos(pSubMenu);
584 if (nMenuPos != MENU_NOT_SELECTED)
586 highlightMenuItem(nMenuPos, true);
587 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)
611 queueCloseSubMenu();
612 return;
615 if (!maMenuItems[nPos].mbEnabled)
617 queueCloseSubMenu();
618 return;
621 highlightMenuItem(nPos, bSelected);
623 if (bSelected)
625 if (mpParentMenu)
626 mpParentMenu->setSubMenuFocused(this);
628 if (bSubMenuTimer)
630 if (maMenuItems[nPos].mpSubMenuWin)
632 ScMenuFloatingWindow* pSubMenu = maMenuItems[nPos].mpSubMenuWin.get();
633 queueLaunchSubMenu(nPos, pSubMenu);
635 else
636 queueCloseSubMenu();
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)
650 return NULL;
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)
662 maName = rName;
665 const OUString& ScMenuFloatingWindow::getName() const
667 return maName;
670 void ScMenuFloatingWindow::highlightMenuItem(size_t nPos, bool bSelected)
672 if (nPos == MENU_NOT_SELECTED)
673 return;
675 const StyleSettings& rStyle = GetSettings().GetStyleSettings();
676 Color aBackColor = rStyle.GetMenuColor();
677 SetFillColor(aBackColor);
678 SetLineColor(aBackColor);
680 Point aPos;
681 Size aSize;
682 getMenuItemPosSize(nPos, aPos, aSize);
683 Rectangle aRegion(aPos,aSize);
685 if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
687 Push(PUSH_CLIPREGION);
688 IntersectClipRegion(Rectangle(aPos, aSize));
689 Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
690 DrawNativeControl(
691 CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
692 ImplControlValue(), OUString());
694 Pop();
697 bool bNativeDrawn = true;
698 if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM))
700 ControlState nState = bSelected ? CTRL_STATE_SELECTED : 0;
701 if (maMenuItems[nPos].mbEnabled)
702 nState |= CTRL_STATE_ENABLED;
703 bNativeDrawn = DrawNativeControl(
704 CTRL_MENU_POPUP, PART_MENU_ITEM, aRegion, nState, ImplControlValue(), OUString());
706 else
707 bNativeDrawn = false;
709 if (!bNativeDrawn)
711 if (bSelected)
713 aBackColor = rStyle.GetMenuHighlightColor();
714 SetFillColor(aBackColor);
715 SetLineColor(aBackColor);
717 DrawRect(Rectangle(aPos,aSize));
720 Color aTextColor = bSelected ? rStyle.GetMenuHighlightTextColor() : rStyle.GetMenuTextColor();
721 SetTextColor(aTextColor);
722 drawMenuItem(nPos);
725 void ScMenuFloatingWindow::getMenuItemPosSize(size_t nPos, Point& rPos, Size& rSize) const
727 size_t nCount = maMenuItems.size();
728 if (nPos >= nCount)
729 return;
731 const sal_uInt16 nLeftMargin = 5;
732 const sal_uInt16 nTopMargin = 5;
733 const sal_uInt16 nMenuItemHeight = static_cast<sal_uInt16>(maLabelFont.GetHeight()*1.8);
734 const sal_uInt16 nSepHeight = static_cast<sal_uInt16>(maLabelFont.GetHeight()*0.8);
736 Point aPos1(nLeftMargin, nTopMargin);
737 rPos = aPos1;
738 for (size_t i = 0; i < nPos; ++i)
739 rPos.Y() += maMenuItems[i].mbSeparator ? nSepHeight : nMenuItemHeight;
741 Size aWndSize = GetSizePixel();
742 sal_uInt16 nH = maMenuItems[nPos].mbSeparator ? nSepHeight : nMenuItemHeight;
743 rSize = Size(aWndSize.Width() - nLeftMargin*2, nH);
746 ScMenuFloatingWindow* ScMenuFloatingWindow::getParentMenuWindow() const
748 return mpParentMenu;
751 size_t ScMenuFloatingWindow::getEnclosingMenuItem(const Point& rPos) const
753 size_t n = maMenuItems.size();
754 for (size_t i = 0; i < n; ++i)
756 Point aPos;
757 Size aSize;
758 getMenuItemPosSize(i, aPos, aSize);
759 Rectangle aRect(aPos, aSize);
760 if (aRect.IsInside(rPos))
761 return maMenuItems[i].mbSeparator ? MENU_NOT_SELECTED : i;
763 return MENU_NOT_SELECTED;
766 size_t ScMenuFloatingWindow::getSubMenuPos(ScMenuFloatingWindow* pSubMenu)
768 size_t n = maMenuItems.size();
769 for (size_t i = 0; i < n; ++i)
771 if (maMenuItems[i].mpSubMenuWin.get() == pSubMenu)
772 return i;
774 return MENU_NOT_SELECTED;
777 void ScMenuFloatingWindow::fireMenuHighlightedEvent()
779 if (mnSelectedMenu == MENU_NOT_SELECTED)
780 return;
782 if (!mxAccessible.is())
783 return;
785 Reference<XAccessibleContext> xAccCxt = mxAccessible->getAccessibleContext();
786 if (!xAccCxt.is())
787 return;
789 Reference<XAccessible> xAccMenu = xAccCxt->getAccessibleChild(mnSelectedMenu);
790 if (!xAccMenu.is())
791 return;
793 VclAccessibleEvent aEvent(VCLEVENT_MENU_HIGHLIGHT, xAccMenu);
794 FireVclEvent(&aEvent);
797 void ScMenuFloatingWindow::setSubMenuFocused(ScMenuFloatingWindow* pSubMenu)
799 maCloseTimer.reset();
800 size_t nMenuPos = getSubMenuPos(pSubMenu);
801 if (mnSelectedMenu != nMenuPos)
803 highlightMenuItem(nMenuPos, true);
804 mnSelectedMenu = nMenuPos;
808 void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu)
810 if (mpParentMenu)
811 mpParentMenu->ensureSubMenuVisible(this);
813 if (pSubMenu->IsVisible())
814 return;
816 // Find the menu position of the submenu.
817 size_t nMenuPos = getSubMenuPos(pSubMenu);
818 if (nMenuPos != MENU_NOT_SELECTED)
820 setSelectedMenuItem(nMenuPos, false, false);
822 Point aPos;
823 Size aSize;
824 getMenuItemPosSize(nMenuPos, aPos, aSize);
826 sal_uInt32 nOldFlags = GetPopupModeFlags();
827 SetPopupModeFlags(nOldFlags | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE);
828 pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
829 pSubMenu->StartPopupMode(
830 Rectangle(aPos,aSize), (FLOATWIN_POPUPMODE_RIGHT | FLOATWIN_POPUPMODE_GRABFOCUS));
831 pSubMenu->AddPopupModeWindow(this);
832 SetPopupModeFlags(nOldFlags);
836 void ScMenuFloatingWindow::ensureSubMenuNotVisible()
838 if (mnSelectedMenu <= maMenuItems.size() &&
839 maMenuItems[mnSelectedMenu].mpSubMenuWin &&
840 maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
842 maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
845 EndPopupMode();
848 void ScMenuFloatingWindow::terminateAllPopupMenus()
850 EndPopupMode();
851 if (mpParentMenu)
852 mpParentMenu->terminateAllPopupMenus();
855 ScDocument* ScMenuFloatingWindow::getDoc()
857 return mpDoc;
860 // ============================================================================
862 ScCheckListMenuWindow::Config::Config() :
863 mbAllowEmptySet(true), mbRTL(false)
867 ScCheckListMenuWindow::Member::Member() :
868 mbVisible(true), mbDate(false), mbLeaf(false)
872 // ----------------------------------------------------------------------------
874 ScCheckListMenuWindow::CancelButton::CancelButton(ScCheckListMenuWindow* pParent) :
875 ::CancelButton(pParent), mpParent(pParent) {}
877 void ScCheckListMenuWindow::CancelButton::Click()
879 mpParent->EndPopupMode();
880 ::CancelButton::Click();
883 // ----------------------------------------------------------------------------
885 ScCheckListMenuWindow::ScCheckListMenuWindow(Window* pParent, ScDocument* pDoc) :
886 ScMenuFloatingWindow(pParent, pDoc),
887 maChecks(this, WB_HASBUTTONS | WB_HASLINES | WB_HASLINESATROOT | WB_HASBUTTONSATROOT ),
888 maChkToggleAll(this, 0),
889 maBtnSelectSingle (this, 0),
890 maBtnUnselectSingle(this, 0),
891 maBtnOk(this),
892 maBtnCancel(this),
893 mnCurTabStop(0),
894 mpExtendedData(NULL),
895 mpOKAction(NULL),
896 mpPopupEndAction(NULL),
897 maWndSize(200, 330),
898 mePrevToggleAllState(STATE_DONTKNOW)
900 maTabStopCtrls.reserve(7);
901 maTabStopCtrls.push_back(this);
902 maTabStopCtrls.push_back(&maChecks);
903 maTabStopCtrls.push_back(&maChkToggleAll);
904 maTabStopCtrls.push_back(&maBtnSelectSingle);
905 maTabStopCtrls.push_back(&maBtnUnselectSingle);
906 maTabStopCtrls.push_back(&maBtnOk);
907 maTabStopCtrls.push_back(&maBtnCancel);
909 // Enable type-ahead search in the check list box.
910 maChecks.SetStyle(maChecks.GetStyle() | WB_QUICK_SEARCH);
913 ScCheckListMenuWindow::~ScCheckListMenuWindow()
917 void ScCheckListMenuWindow::getSectionPosSize(
918 Point& rPos, Size& rSize, SectionType eType) const
920 // constant parameters.
921 const long nListBoxMargin = 5; // horizontal distance from the side of the dialog to the listbox border.
922 const long nListBoxInnerPadding = 5;
923 const long nTopMargin = 5;
924 const long nMenuHeight = maMenuSize.getHeight();
925 const long nSingleItemBtnAreaHeight = 32; // height of the middle area below the list box where the single-action buttons are.
926 const long nBottomBtnAreaHeight = 50; // height of the bottom area where the OK and Cancel buttons are.
927 const long nBtnWidth = 90;
928 const long nLabelHeight = getLabelFont().GetHeight();
929 const long nBtnHeight = nLabelHeight*2;
930 const long nBottomMargin = 10;
931 const long nMenuListMargin = 5;
933 // parameters calculated from constants.
934 const long nListBoxWidth = maWndSize.Width() - nListBoxMargin*2;
935 const long nListBoxHeight = maWndSize.Height() - nTopMargin - nMenuHeight -
936 nMenuListMargin - nSingleItemBtnAreaHeight - nBottomBtnAreaHeight;
938 const long nSingleBtnAreaY = nTopMargin + nMenuHeight + nListBoxHeight + nMenuListMargin - 1;
940 switch (eType)
942 case WHOLE:
944 rPos = Point(0, 0);
945 rSize = maWndSize;
947 break;
948 case LISTBOX_AREA_OUTER:
950 rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
951 rSize = Size(nListBoxWidth, nListBoxHeight);
953 break;
954 case LISTBOX_AREA_INNER:
956 rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
957 rPos.X() += nListBoxInnerPadding;
958 rPos.Y() += nListBoxInnerPadding;
960 rSize = Size(nListBoxWidth, nListBoxHeight);
961 rSize.Width() -= nListBoxInnerPadding*2;
962 rSize.Height() -= nListBoxInnerPadding*2;
964 break;
965 case SINGLE_BTN_AREA:
967 rPos = Point(nListBoxMargin, nSingleBtnAreaY);
968 rSize = Size(nListBoxWidth, nSingleItemBtnAreaHeight);
970 break;
971 case CHECK_TOGGLE_ALL:
973 long h = std::min(maChkToggleAll.CalcMinimumSize().Height(), 26L);
974 rPos = Point(nListBoxMargin, nSingleBtnAreaY);
975 rPos.X() += 5;
976 rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
977 rSize = Size(70, h);
979 break;
980 case BTN_SINGLE_SELECT:
982 long h = 26;
983 rPos = Point(nListBoxMargin, nSingleBtnAreaY);
984 rPos.X() += nListBoxWidth - h - 10 - h - 10;
985 rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
986 rSize = Size(h, h);
988 break;
989 case BTN_SINGLE_UNSELECT:
991 long h = 26;
992 rPos = Point(nListBoxMargin, nSingleBtnAreaY);
993 rPos.X() += nListBoxWidth - h - 10;
994 rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
995 rSize = Size(h, h);
997 break;
998 case BTN_OK:
1000 long x = (maWndSize.Width() - nBtnWidth*2)/3;
1001 long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
1002 rPos = Point(x, y);
1003 rSize = Size(nBtnWidth, nBtnHeight);
1005 break;
1006 case BTN_CANCEL:
1008 long x = (maWndSize.Width() - nBtnWidth*2)/3*2 + nBtnWidth;
1009 long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
1010 rPos = Point(x, y);
1011 rSize = Size(nBtnWidth, nBtnHeight);
1013 break;
1014 default:
1019 void ScCheckListMenuWindow::packWindow()
1021 maMenuSize = getMenuSize();
1023 if (maWndSize.Width() < maMenuSize.Width())
1024 // Widen the window to fit the menu items.
1025 maWndSize.Width() = maMenuSize.Width();
1027 // Set proper window height based on the number of menu items.
1028 if (maWndSize.Height() < maMenuSize.Height()*2.8)
1029 maWndSize.Height() = maMenuSize.Height()*2.8;
1031 // TODO: Make sure the window height never exceeds the height of the
1032 // screen. Also do adjustment based on the number of check box items.
1034 SetOutputSizePixel(maWndSize);
1036 const StyleSettings& rStyle = GetSettings().GetStyleSettings();
1038 Point aPos;
1039 Size aSize;
1040 getSectionPosSize(aPos, aSize, WHOLE);
1041 SetOutputSizePixel(aSize);
1043 getSectionPosSize(aPos, aSize, BTN_OK);
1044 maBtnOk.SetPosSizePixel(aPos, aSize);
1045 maBtnOk.SetFont(getLabelFont());
1046 maBtnOk.SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
1047 maBtnOk.Show();
1049 getSectionPosSize(aPos, aSize, BTN_CANCEL);
1050 maBtnCancel.SetPosSizePixel(aPos, aSize);
1051 maBtnCancel.SetFont(getLabelFont());
1052 maBtnCancel.Show();
1054 getSectionPosSize(aPos, aSize, LISTBOX_AREA_INNER);
1055 maChecks.SetPosSizePixel(aPos, aSize);
1056 maChecks.SetFont(getLabelFont());
1057 maChecks.SetCheckButtonHdl( LINK(this, ScCheckListMenuWindow, CheckHdl) );
1058 maChecks.Show();
1060 getSectionPosSize(aPos, aSize, CHECK_TOGGLE_ALL);
1061 maChkToggleAll.SetPosSizePixel(aPos, aSize);
1062 maChkToggleAll.SetFont(getLabelFont());
1063 maChkToggleAll.SetText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_TOGGLE_ALL));
1064 maChkToggleAll.SetTextColor(rStyle.GetMenuTextColor());
1065 maChkToggleAll.SetControlBackground(rStyle.GetMenuColor());
1066 maChkToggleAll.SetClickHdl( LINK(this, ScCheckListMenuWindow, TriStateHdl) );
1067 maChkToggleAll.Show();
1069 getSectionPosSize(aPos, aSize, BTN_SINGLE_SELECT);
1070 maBtnSelectSingle.SetPosSizePixel(aPos, aSize);
1071 maBtnSelectSingle.SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_SELECT_CURRENT));
1072 maBtnSelectSingle.SetModeImage(Image(ScResId(RID_IMG_SELECT_CURRENT)));
1073 maBtnSelectSingle.SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
1074 maBtnSelectSingle.Show();
1076 getSectionPosSize(aPos, aSize, BTN_SINGLE_UNSELECT);
1077 maBtnUnselectSingle.SetPosSizePixel(aPos, aSize);
1078 maBtnUnselectSingle.SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_UNSELECT_CURRENT));
1079 maBtnUnselectSingle.SetModeImage(Image(ScResId(RID_IMG_UNSELECT_CURRENT)));
1080 maBtnUnselectSingle.SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
1081 maBtnUnselectSingle.Show();
1084 void ScCheckListMenuWindow::setAllMemberState(bool bSet)
1086 size_t n = maMembers.size();
1087 for (size_t i = 0; i < n; ++i)
1088 maChecks.CheckEntry( maMembers[i].maName, maMembers[i].mpParent, bSet);
1090 if (!maConfig.mbAllowEmptySet)
1091 // We need to have at least one member selected.
1092 maBtnOk.Enable(maChecks.GetCheckedEntryCount() != 0);
1095 void ScCheckListMenuWindow::selectCurrentMemberOnly(bool bSet)
1097 setAllMemberState(!bSet);
1098 SvTreeListEntry* pEntry = maChecks.GetCurEntry();
1099 maChecks.CheckEntry(pEntry, bSet );
1102 void ScCheckListMenuWindow::cycleFocus(bool bReverse)
1104 maTabStopCtrls[mnCurTabStop]->SetFakeFocus(false);
1105 maTabStopCtrls[mnCurTabStop]->LoseFocus();
1106 if (mnCurTabStop == 0)
1107 clearSelectedMenuItem();
1109 if (bReverse)
1111 if (mnCurTabStop > 0)
1112 --mnCurTabStop;
1113 else
1114 mnCurTabStop = maTabStopCtrls.size() - 1;
1116 else
1118 ++mnCurTabStop;
1119 if (mnCurTabStop >= maTabStopCtrls.size())
1120 mnCurTabStop = 0;
1122 maTabStopCtrls[mnCurTabStop]->SetFakeFocus(true);
1123 maTabStopCtrls[mnCurTabStop]->GrabFocus();
1126 IMPL_LINK( ScCheckListMenuWindow, ButtonHdl, Button*, pBtn )
1128 if (pBtn == &maBtnOk)
1129 close(true);
1130 else if (pBtn == &maBtnSelectSingle)
1132 selectCurrentMemberOnly(true);
1133 CheckHdl(&maChecks);
1135 else if (pBtn == &maBtnUnselectSingle)
1137 selectCurrentMemberOnly(false);
1138 CheckHdl(&maChecks);
1140 return 0;
1143 IMPL_LINK_NOARG(ScCheckListMenuWindow, TriStateHdl)
1145 switch (mePrevToggleAllState)
1147 case STATE_NOCHECK:
1148 maChkToggleAll.SetState(STATE_CHECK);
1149 setAllMemberState(true);
1150 break;
1151 case STATE_CHECK:
1152 maChkToggleAll.SetState(STATE_NOCHECK);
1153 setAllMemberState(false);
1154 break;
1155 case STATE_DONTKNOW:
1156 default:
1157 maChkToggleAll.SetState(STATE_CHECK);
1158 setAllMemberState(true);
1159 break;
1162 mePrevToggleAllState = maChkToggleAll.GetState();
1163 return 0;
1166 IMPL_LINK( ScCheckListMenuWindow, CheckHdl, SvTreeListBox*, pChecks )
1168 if (pChecks != &maChecks)
1169 return 0;
1170 SvTreeListEntry* pEntry = pChecks->GetHdlEntry();
1171 if ( pEntry )
1172 maChecks.CheckEntry( pEntry, ( pChecks->GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) );
1173 size_t nNumChecked = maChecks.GetCheckedEntryCount();
1174 if (nNumChecked == maMembers.size())
1175 // all members visible
1176 maChkToggleAll.SetState(STATE_CHECK);
1177 else if (nNumChecked == 0)
1178 // no members visible
1179 maChkToggleAll.SetState(STATE_NOCHECK);
1180 else
1181 maChkToggleAll.SetState(STATE_DONTKNOW);
1183 if (!maConfig.mbAllowEmptySet)
1184 // We need to have at least one member selected.
1185 maBtnOk.Enable(nNumChecked != 0);
1187 mePrevToggleAllState = maChkToggleAll.GetState();
1188 return 0;
1191 void ScCheckListMenuWindow::MouseMove(const MouseEvent& rMEvt)
1193 ScMenuFloatingWindow::MouseMove(rMEvt);
1195 size_t nSelectedMenu = getSelectedMenuItem();
1196 if (nSelectedMenu == MENU_NOT_SELECTED)
1197 queueCloseSubMenu();
1200 long ScCheckListMenuWindow::Notify(NotifyEvent& rNEvt)
1202 switch (rNEvt.GetType())
1204 case EVENT_KEYUP:
1206 const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
1207 const KeyCode& rCode = pKeyEvent->GetKeyCode();
1208 bool bShift = rCode.IsShift();
1209 if (rCode.GetCode() == KEY_TAB)
1211 cycleFocus(bShift);
1212 return true;
1215 break;
1217 return ScMenuFloatingWindow::Notify(rNEvt);
1220 void ScCheckListMenuWindow::Paint(const Rectangle& rRect)
1222 ScMenuFloatingWindow::Paint(rRect);
1224 const StyleSettings& rStyle = GetSettings().GetStyleSettings();
1225 Color aMemberBackColor = rStyle.GetFieldColor();
1226 Color aBorderColor = rStyle.GetShadowColor();
1228 Point aPos;
1229 Size aSize;
1230 getSectionPosSize(aPos, aSize, LISTBOX_AREA_OUTER);
1232 // Member list box background
1233 SetFillColor(aMemberBackColor);
1234 SetLineColor(aBorderColor);
1235 DrawRect(Rectangle(aPos,aSize));
1237 // Single-action button box
1238 getSectionPosSize(aPos, aSize, SINGLE_BTN_AREA);
1239 SetFillColor(rStyle.GetMenuColor());
1240 DrawRect(Rectangle(aPos,aSize));
1243 Window* ScCheckListMenuWindow::GetPreferredKeyInputWindow()
1245 return maTabStopCtrls[mnCurTabStop];
1248 Reference<XAccessible> ScCheckListMenuWindow::CreateAccessible()
1250 if (!mxAccessible.is())
1252 mxAccessible.set(new ScAccessibleFilterTopWindow(
1253 GetAccessibleParentWindow()->GetAccessible(), this, getName()));
1254 ScAccessibleFilterTopWindow* pAccTop = static_cast<ScAccessibleFilterTopWindow*>(mxAccessible.get());
1255 fillMenuItemsToAccessible(pAccTop);
1257 pAccTop->setAccessibleChild(
1258 maChecks.CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX);
1259 pAccTop->setAccessibleChild(
1260 maChkToggleAll.CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL);
1261 pAccTop->setAccessibleChild(
1262 maBtnSelectSingle.CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_ON_BTN);
1263 pAccTop->setAccessibleChild(
1264 maBtnUnselectSingle.CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_OFF_BTN);
1265 pAccTop->setAccessibleChild(
1266 maBtnOk.CreateAccessible(), ScAccessibleFilterTopWindow::OK_BTN);
1267 pAccTop->setAccessibleChild(
1268 maBtnCancel.CreateAccessible(), ScAccessibleFilterTopWindow::CANCEL_BTN);
1271 return mxAccessible;
1274 void ScCheckListMenuWindow::setMemberSize(size_t n)
1276 maMembers.reserve(n);
1279 void ScCheckListMenuWindow::addDateMember(const OUString& rsName, double nVal, bool bVisible)
1281 ScDocument* pDoc = getDoc();
1282 if ( pDoc )
1284 SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
1285 OUString rsDate;
1286 if ( pFormatter )
1288 OUString sFormat("YYYY/MMMM/DD");
1289 Color* pColor = NULL;
1290 pFormatter->GetPreviewString(sFormat,
1291 nVal,
1292 rsDate,
1293 &pColor,
1294 ScGlobal::eLnge );
1296 maChecks.SetUpdateMode(false);
1297 sal_Int32 nIndex = 0;
1298 OUString sParent;
1299 SvTreeListEntry* pParent = NULL;
1300 int count = 0;
1303 OUString sPart = rsDate.getToken( 0, '/', nIndex );
1304 bool bLeaf = ( ++count == 3 );
1305 SvTreeListEntry* pChild = maChecks.FindEntry( pParent, sPart );
1306 if ( !pChild )
1308 if ( bLeaf )
1309 pChild = maChecks.SvTreeListBox::InsertEntry( sPart, pParent, sal_False, LISTBOX_APPEND, NULL, SvLBoxButtonKind_enabledCheckbox );
1310 else
1311 pChild = maChecks.SvTreeListBox::InsertEntry( sPart, pParent, sal_True, LISTBOX_APPEND, NULL, SvLBoxButtonKind_enabledCheckbox );
1312 Member aMember;
1313 aMember.maName = sPart;
1314 aMember.maRealName = rsName;
1315 aMember.mbDate = true;
1316 aMember.mbLeaf = bLeaf;
1317 aMember.mbVisible = bVisible;
1318 aMember.mpParent = pParent;
1319 maMembers.push_back(aMember);
1321 sParent = sPart;
1322 pParent = pChild;
1323 } while ( nIndex >= 0 );
1324 maChecks.SetUpdateMode(true);
1328 void ScCheckListMenuWindow::addMember(const OUString& rName, bool bVisible)
1330 Member aMember;
1331 aMember.maName = rName;
1332 aMember.mbDate = false;
1333 aMember.mbLeaf = true;
1334 aMember.mbVisible = bVisible;
1335 aMember.mpParent = NULL;
1336 maMembers.push_back(aMember);
1339 ScCheckListBox::ScCheckListBox( Window* pParent, WinBits nWinStyle )
1340 : SvTreeListBox( pParent, nWinStyle ), mpCheckButton( NULL )
1342 Init();
1345 SvTreeListEntry* ScCheckListBox::FindEntry( SvTreeListEntry* pParent, const OUString& sNode )
1347 sal_uInt16 nRootPos = 0;
1348 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : GetEntry( nRootPos );
1349 while ( pEntry )
1351 if ( sNode.equals(GetEntryText( pEntry )) )
1352 return pEntry;
1354 pEntry = pParent ? NextSibling( pEntry ) : GetEntry( ++nRootPos );
1356 return NULL;
1359 void ScCheckListBox::Init()
1361 mpCheckButton = new SvLBoxButtonData( this );
1362 EnableCheckButton( mpCheckButton );
1363 SetNodeDefaultImages();
1366 sal_Bool ScCheckListBox::IsChecked( OUString& sName, SvTreeListEntry* pParent )
1368 SvTreeListEntry* pEntry = FindEntry( pParent, sName );
1369 if ( pEntry && GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED)
1370 return sal_True;
1371 return sal_False;
1374 void ScCheckListBox::CheckEntry( OUString& sName, SvTreeListEntry* pParent, sal_Bool bCheck )
1376 SvTreeListEntry* pEntry = FindEntry( pParent, sName );
1377 if ( pEntry )
1378 CheckEntry( pEntry, bCheck );
1381 // Recursively check all children of pParent
1382 void ScCheckListBox::CheckAllChildren( SvTreeListEntry* pParent, sal_Bool bCheck )
1384 if ( pParent )
1386 SetCheckButtonState(
1387 pParent, bCheck ? SvButtonState( SV_BUTTON_CHECKED ) :
1388 SvButtonState( SV_BUTTON_UNCHECKED ) );
1390 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1391 while ( pEntry )
1393 CheckAllChildren( pEntry, bCheck );
1394 pEntry = NextSibling( pEntry );
1398 void ScCheckListBox::CheckEntry( SvTreeListEntry* pParent, sal_Bool bCheck )
1400 // recursively check all items below pParent
1401 CheckAllChildren( pParent, bCheck );
1402 // checking pParent can affect ancestors, e.g. if ancestor is unchecked and pParent is
1403 // now checked then the ancestor needs to be checked also
1404 SvTreeListEntry* pAncestor = GetParent(pParent);
1405 if ( pAncestor )
1407 while ( pAncestor )
1409 // if any first level children checked then ancestor
1410 // needs to be checked, similarly if no first level children
1411 // checked then ancestor needs to be unchecked
1412 SvTreeListEntry* pChild = FirstChild( pAncestor );
1413 bool bChildChecked = false;
1415 while ( pChild )
1417 if ( GetCheckButtonState( pChild ) == SV_BUTTON_CHECKED )
1419 bChildChecked = true;
1420 break;
1422 pChild = NextSibling( pChild );
1424 SetCheckButtonState(
1425 pAncestor, bChildChecked ? SvButtonState( SV_BUTTON_CHECKED ) :
1426 SvButtonState( SV_BUTTON_UNCHECKED ) );
1427 pAncestor = GetParent(pAncestor);
1432 SvTreeListEntry* ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
1434 if ( pParent && GetCheckButtonState( pParent ) == SV_BUTTON_CHECKED )
1435 nCount++;
1436 // Iterate over the children
1437 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1438 while ( pEntry )
1440 CountCheckedEntries( pEntry, nCount );
1441 pEntry = NextSibling( pEntry );
1443 return NULL;
1446 sal_uInt16 ScCheckListBox::GetCheckedEntryCount() const
1448 sal_uLong nCount = 0;
1449 CountCheckedEntries( NULL, nCount );
1450 return nCount;
1453 void ScCheckListBox::ExpandChildren( SvTreeListEntry* pParent )
1455 if ( pParent )
1456 Expand( pParent );
1457 // Iterate over the children
1458 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1459 while ( pEntry )
1461 ExpandChildren( pEntry );
1462 pEntry = NextSibling( pEntry );
1466 void ScCheckListBox::KeyInput( const KeyEvent& rKEvt )
1468 const KeyCode& rKey = rKEvt.GetKeyCode();
1470 if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
1472 SvTreeListEntry* pEntry = GetCurEntry();
1474 if ( pEntry )
1476 sal_Bool bCheck = ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED );
1477 CheckEntry( pEntry, !bCheck );
1478 if ( bCheck != ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) )
1479 CheckButtonHdl();
1482 else if ( GetEntryCount() )
1483 SvTreeListBox::KeyInput( rKEvt );
1486 void ScCheckListMenuWindow::initMembers()
1488 size_t n = maMembers.size();
1489 size_t nVisMemCount = 0;
1490 maChecks.SetUpdateMode(false);
1491 for (size_t i = 0; i < n; ++i)
1493 if ( !maMembers[ i ].mbDate )
1495 maChecks.InsertEntry(maMembers[i].maName, NULL, sal_False, LISTBOX_APPEND, NULL,
1496 SvLBoxButtonKind_enabledCheckbox );
1499 maChecks.CheckEntry( maMembers[i].maName, maMembers[i].mpParent, maMembers[i].mbVisible);
1500 // Expand first node of checked dates
1501 if ( maMembers[ i ].mpParent == NULL && maChecks.IsChecked( maMembers[i].maName, maMembers[i].mpParent ) )
1502 maChecks.Expand( maChecks.FindEntry( NULL, maMembers[ i ].maName ) );
1504 if (maMembers[i].mbVisible)
1505 ++nVisMemCount;
1507 if (nVisMemCount == n)
1509 // all members visible
1510 maChkToggleAll.SetState(STATE_CHECK);
1511 mePrevToggleAllState = STATE_CHECK;
1513 else if (nVisMemCount == 0)
1515 // no members visible
1516 maChkToggleAll.SetState(STATE_NOCHECK);
1517 mePrevToggleAllState = STATE_NOCHECK;
1519 else
1521 maChkToggleAll.SetState(STATE_DONTKNOW);
1522 mePrevToggleAllState = STATE_DONTKNOW;
1524 maChecks.SetUpdateMode(true);
1527 void ScCheckListMenuWindow::setConfig(const Config& rConfig)
1529 maConfig = rConfig;
1532 bool ScCheckListMenuWindow::isAllSelected() const
1534 return maChkToggleAll.IsChecked();
1537 void ScCheckListMenuWindow::getResult(ResultType& rResult)
1539 ResultType aResult;
1540 size_t n = maMembers.size();
1541 for (size_t i = 0; i < n; ++i)
1543 if ( maMembers[i].mbLeaf )
1545 bool bState = maChecks.IsChecked( maMembers[i].maName, maMembers[i].mpParent );
1546 OUString sName;
1547 if ( maMembers[i].mbDate )
1548 sName = maMembers[i].maRealName;
1549 else
1550 sName = maMembers[i].maName;
1551 aResult.insert(ResultType::value_type(sName, bState));
1554 rResult.swap(aResult);
1557 void ScCheckListMenuWindow::launch(const Rectangle& rRect)
1559 packWindow();
1560 if (!maConfig.mbAllowEmptySet)
1561 // We need to have at least one member selected.
1562 maBtnOk.Enable(maChecks.GetCheckedEntryCount() != 0);
1564 Rectangle aRect(rRect);
1565 if (maConfig.mbRTL)
1567 // In RTL mode, the logical "left" is visual "right".
1568 long nLeft = aRect.Left() - aRect.GetWidth();
1569 aRect.Left() = nLeft;
1571 else if (maWndSize.Width() < aRect.GetWidth())
1573 // Target rectangle (i.e. cell width) is wider than the window.
1574 // Simulate right-aligned launch by modifying the target rectangle
1575 // size.
1576 long nDiff = aRect.GetWidth() - maWndSize.Width();
1577 aRect.Left() += nDiff;
1580 StartPopupMode(aRect, (FLOATWIN_POPUPMODE_DOWN | FLOATWIN_POPUPMODE_GRABFOCUS));
1581 cycleFocus(); // Set initial focus to the check list box.
1584 void ScCheckListMenuWindow::close(bool bOK)
1586 if (bOK && mpOKAction.get())
1587 mpOKAction->execute();
1589 EndPopupMode();
1592 void ScCheckListMenuWindow::setExtendedData(ExtendedData* p)
1594 mpExtendedData.reset(p);
1597 ScCheckListMenuWindow::ExtendedData* ScCheckListMenuWindow::getExtendedData()
1599 return mpExtendedData.get();
1602 void ScCheckListMenuWindow::setOKAction(Action* p)
1604 mpOKAction.reset(p);
1607 void ScCheckListMenuWindow::setPopupEndAction(Action* p)
1609 mpPopupEndAction.reset(p);
1612 void ScCheckListMenuWindow::handlePopupEnd()
1614 clearSelectedMenuItem();
1615 if (mpPopupEndAction)
1616 mpPopupEndAction->execute();
1619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */