fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / ui / cctrl / checklistmenu.cxx
blob8dca698976025110347874ce42ffc0e281048f68
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"
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;
42 using ::std::vector;
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) :
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_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),
76 maOpenTimer(this),
77 maCloseTimer(this),
78 maName("ScMenuFloatingWindow"),
79 mnSelectedMenu(MENU_NOT_SELECTED),
80 mnClickedMenu(MENU_NOT_SELECTED),
81 mpDoc(pDoc),
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()
97 disposeOnce();
100 void ScMenuFloatingWindow::dispose()
102 EndPopupMode();
103 for (auto i = maMenuItems.begin(); i != maMenuItems.end(); ++i)
104 i->mpSubMenuWin.disposeAndClear();
105 mpParentMenu.clear();
106 PopupMenuFloatingWindow::dispose();
109 void ScMenuFloatingWindow::PopupModeEnd()
111 handlePopupEnd();
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);
142 return;
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())
151 case KEY_UP:
153 if (nLastMenuPos == 0)
154 // There is only one menu item. Do nothing.
155 break;
157 size_t nOldPos = nSelectedMenu;
159 if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == 0)
160 nSelectedMenu = nLastMenuPos;
161 else
162 --nSelectedMenu;
164 // Loop until a non-separator menu item is found.
165 while (nSelectedMenu != nOldPos)
167 if (maMenuItems[nSelectedMenu].mbSeparator)
169 if (nSelectedMenu)
170 --nSelectedMenu;
171 else
172 nSelectedMenu = nLastMenuPos;
174 else
175 break;
178 setSelectedMenuItem(nSelectedMenu, false, false);
180 break;
181 case KEY_DOWN:
183 if (nLastMenuPos == 0)
184 // There is only one menu item. Do nothing.
185 break;
187 size_t nOldPos = nSelectedMenu;
189 if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == nLastMenuPos)
190 nSelectedMenu = 0;
191 else
192 ++nSelectedMenu;
194 // Loop until a non-separator menu item is found.
195 while (nSelectedMenu != nOldPos)
197 if (maMenuItems[nSelectedMenu].mbSeparator)
199 if (nSelectedMenu == nLastMenuPos)
200 nSelectedMenu = 0;
201 else
202 ++nSelectedMenu;
204 else
205 break;
208 setSelectedMenuItem(nSelectedMenu, false, false);
210 break;
211 case KEY_LEFT:
212 if (mpParentMenu)
213 mpParentMenu->endSubMenu(this);
214 break;
215 case KEY_RIGHT:
217 if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
218 break;
220 const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
221 if (!rMenu.mbEnabled || !rMenu.mpSubMenuWin)
222 break;
224 maOpenTimer.mnMenuPos = mnSelectedMenu;
225 maOpenTimer.mpSubMenu = rMenu.mpSubMenuWin.get();
226 launchSubMenu(true);
228 break;
229 case KEY_RETURN:
230 if (nSelectedMenu != MENU_NOT_SELECTED)
231 executeMenuItem(nSelectedMenu);
232 break;
233 default:
234 bHandled = false;
237 if (!bHandled)
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());
252 // Window background
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());
260 else
261 bNativeDrawn = false;
263 if (!bNativeDrawn)
265 rRenderContext.SetFillColor(aBackColor);
266 rRenderContext.SetLineColor(aBorderColor);
267 rRenderContext.DrawRect(aCtrlRect);
270 // Menu items
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*>(
284 mxAccessible.get());
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);
294 return mxAccessible;
297 void ScMenuFloatingWindow::addMenuItem(const OUString& rText, bool bEnabled, Action* pAction)
299 MenuItemData aItem;
300 aItem.maText = rText;
301 aItem.mbEnabled = bEnabled;
302 aItem.mpAction.reset(pAction);
303 maMenuItems.push_back(aItem);
306 void ScMenuFloatingWindow::addSeparator()
308 MenuItemData aItem;
309 aItem.mbSeparator = true;
310 maMenuItems.push_back(aItem);
313 ScMenuFloatingWindow* ScMenuFloatingWindow::addSubMenuItem(const OUString& rText, bool bEnabled)
315 MenuItemData aItem;
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())
332 return Size();
334 vector<MenuItemData>::const_iterator itr = maMenuItems.begin(), itrEnd = maMenuItems.end();
335 long nTextWidth = 0;
336 for (; itr != itrEnd; ++itr)
338 if (itr->mbSeparator)
339 continue;
341 nTextWidth = ::std::max(GetTextWidth(itr->maText), nTextWidth);
344 size_t nLastPos = maMenuItems.size()-1;
345 Point aPos;
346 Size aSize;
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())
356 return;
358 Point aPos;
359 Size aSize;
360 getMenuItemPosSize(nPos, aPos, aSize);
362 DecorationView aDecoView(&rRenderContext);
363 long nXOffset = 5;
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)
382 Point aPos;
383 Size aSize;
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];
403 if (rData.mbEnabled)
404 nState |= ControlState::ENABLED;
406 bNativeDrawn = rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_MENU_SEPARATOR,
407 aRegion, nState, ImplControlValue(), OUString());
410 if (!bNativeDrawn)
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()));
417 ++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)
432 // Separator
433 drawSeparator(rRenderContext, i);
435 else
437 // Normal menu item
438 highlightMenuItem(rRenderContext, i, i == mnSelectedMenu);
443 void ScMenuFloatingWindow::executeMenuItem(size_t nPos)
445 if (nPos >= maMenuItems.size())
446 return;
448 if (!maMenuItems[nPos].mpAction)
449 // no action is defined.
450 return;
452 maMenuItems[nPos].mpAction->execute();
453 terminateAllPopupMenus();
456 void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu)
458 if (mnSelectedMenu == nPos)
459 // nothing to do.
460 return;
462 if (bEnsureSubMenu)
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)
502 // end submenu.
503 if (maCloseTimer.mpSubMenu)
505 maOpenTimer.mpSubMenu = NULL;
507 maCloseTimer.mpSubMenu->EndPopupMode();
508 maCloseTimer.mpSubMenu = NULL;
510 Invalidate();
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 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);
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 mnSelectedMenu = nMenuPos;
587 Invalidate();
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 Invalidate();
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 void ScMenuFloatingWindow::highlightMenuItem(vcl::RenderContext& rRenderContext, size_t nPos, bool bSelected)
667 if (nPos == MENU_NOT_SELECTED)
668 return;
670 const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
671 Color aBackColor = rStyle.GetMenuColor();
672 rRenderContext.SetFillColor(aBackColor);
673 rRenderContext.SetLineColor(aBackColor);
675 Point aPos;
676 Size aSize;
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());
699 else
700 bNativeDrawn = false;
702 if (!bNativeDrawn)
704 if (bSelected)
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();
721 if (nPos >= nCount)
722 return;
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);
730 rPos = aPos1;
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)
744 Point aPos;
745 Size aSize;
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)
760 return i;
762 return MENU_NOT_SELECTED;
765 void ScMenuFloatingWindow::fireMenuHighlightedEvent()
767 if (mnSelectedMenu == MENU_NOT_SELECTED)
768 return;
770 if (!mxAccessible.is())
771 return;
773 Reference<XAccessibleContext> xAccCxt = mxAccessible->getAccessibleContext();
774 if (!xAccCxt.is())
775 return;
777 Reference<XAccessible> xAccMenu = xAccCxt->getAccessibleChild(mnSelectedMenu);
778 if (!xAccMenu.is())
779 return;
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;
792 Invalidate();
796 void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu)
798 if (mpParentMenu)
799 mpParentMenu->ensureSubMenuVisible(this);
801 if (pSubMenu->IsVisible())
802 return;
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);
810 Point aPos;
811 Size aSize;
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();
833 EndPopupMode();
836 void ScMenuFloatingWindow::terminateAllPopupMenus()
838 EndPopupMode();
839 if (mpParentMenu)
840 mpParentMenu->terminateAllPopupMenus();
843 ScCheckListMenuWindow::Config::Config() :
844 mbAllowEmptySet(true), mbRTL(false)
848 ScCheckListMenuWindow::Member::Member()
849 : mbVisible(true)
850 , mbDate(false)
851 , mbLeaf(false)
852 , mpParent(NULL)
856 ScCheckListMenuWindow::CancelButton::CancelButton(ScCheckListMenuWindow* pParent) :
857 ::CancelButton(pParent), mpParent(pParent) {}
859 ScCheckListMenuWindow::CancelButton::~CancelButton()
861 disposeOnce();
864 void ScCheckListMenuWindow::CancelButton::dispose()
866 mpParent.clear();
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)),
885 mnCurTabStop(0),
886 mpExtendedData(NULL),
887 mpOKAction(NULL),
888 mpPopupEndAction(NULL),
889 maWndSize(),
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()
912 disposeOnce();
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;
955 switch (eType)
957 case WHOLE:
959 rPos = Point(0, 0);
960 rSize = maWndSize;
962 break;
963 case EDIT_SEARCH:
965 rPos = Point(nSearchBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
966 rSize = Size(maWndSize.Width() - 2*nSearchBoxMargin, nSearchBoxHeight);
968 break;
969 case LISTBOX_AREA_OUTER:
971 rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
972 rSize = Size(nListBoxWidth, nListBoxHeight);
974 break;
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;
985 break;
986 case SINGLE_BTN_AREA:
988 rPos = Point(nListBoxMargin, nSingleBtnAreaY);
989 rSize = Size(nListBoxWidth, nSingleItemBtnAreaHeight);
991 break;
992 case CHECK_TOGGLE_ALL:
994 long h = std::min(maChkToggleAll->CalcMinimumSize().Height(), 26L);
995 rPos = Point(nListBoxMargin, nSingleBtnAreaY);
996 rPos.X() += 5;
997 rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
998 rSize = Size(70, h);
1000 break;
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;
1007 rSize = Size(h, h);
1009 break;
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;
1016 rSize = Size(h, h);
1018 break;
1019 case BTN_OK:
1021 long x = (maWndSize.Width() - nBtnWidth*2)/3;
1022 long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
1023 rPos = Point(x, y);
1024 rSize = Size(nBtnWidth, nBtnHeight);
1026 break;
1027 case BTN_CANCEL:
1029 long x = (maWndSize.Width() - nBtnWidth*2)/3*2 + nBtnWidth;
1030 long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
1031 rPos = Point(x, y);
1032 rSize = Size(nBtnWidth, nBtnHeight);
1034 break;
1035 default:
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();
1059 Point aPos;
1060 Size aSize;
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) );
1068 maBtnOk->Show();
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) );
1081 maEdSearch->Show();
1083 getSectionPosSize(aPos, aSize, LISTBOX_AREA_INNER);
1084 maChecks->SetPosSizePixel(aPos, aSize);
1085 maChecks->SetFont(getLabelFont());
1086 maChecks->SetCheckButtonHdl( LINK(this, ScCheckListMenuWindow, CheckHdl) );
1087 maChecks->Show();
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();
1134 OUString aLabel;
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();
1151 if (!pEntry)
1152 return;
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();
1163 if (bReverse)
1165 if (mnCurTabStop > 0)
1166 --mnCurTabStop;
1167 else
1168 mnCurTabStop = maTabStopCtrls.size() - 1;
1170 else
1172 ++mnCurTabStop;
1173 if (mnCurTabStop >= maTabStopCtrls.size())
1174 mnCurTabStop = 0;
1176 maTabStopCtrls[mnCurTabStop]->SetFakeFocus(true);
1177 maTabStopCtrls[mnCurTabStop]->GrabFocus();
1180 IMPL_LINK( ScCheckListMenuWindow, ButtonHdl, Button*, pBtn )
1182 if (pBtn == maBtnOk.get())
1183 close(true);
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());
1194 return 0;
1197 IMPL_LINK_NOARG(ScCheckListMenuWindow, TriStateHdl)
1199 switch (mePrevToggleAllState)
1201 case TRISTATE_FALSE:
1202 maChkToggleAll->SetState(TRISTATE_TRUE);
1203 setAllMemberState(true);
1204 break;
1205 case TRISTATE_TRUE:
1206 maChkToggleAll->SetState(TRISTATE_FALSE);
1207 setAllMemberState(false);
1208 break;
1209 case TRISTATE_INDET:
1210 default:
1211 maChkToggleAll->SetState(TRISTATE_TRUE);
1212 setAllMemberState(true);
1213 break;
1216 mePrevToggleAllState = maChkToggleAll->GetState();
1217 return 0;
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 )
1240 ++nSelCount;
1241 continue;
1244 if ( aLabelDisp.toAsciiLowerCase().indexOf( aSearchText ) != -1 )
1246 maChecks->ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, true, true );
1247 ++nSelCount;
1249 else
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 );
1257 else
1258 maChkToggleAll->SetState( TRISTATE_INDET );
1260 if ( !maConfig.mbAllowEmptySet )
1261 maBtnOk->Enable( nSelCount != 0);
1263 return 0;
1266 IMPL_LINK( ScCheckListMenuWindow, CheckHdl, SvTreeListBox*, pChecks )
1268 if (pChecks != maChecks.get())
1269 return 0;
1270 SvTreeListEntry* pEntry = pChecks->GetHdlEntry();
1271 if ( pEntry )
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);
1280 else
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();
1288 return 0;
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)
1309 cycleFocus(bShift);
1310 return true;
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();
1324 Point aPos;
1325 Size aSize;
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)
1394 return;
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);
1403 if (!pYearEntry)
1405 pYearEntry = maChecks->InsertEntry(aYearName, NULL, true);
1406 Member aMemYear;
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);
1417 if (!pMonthEntry)
1419 pMonthEntry = maChecks->InsertEntry(aMonthName, pYearEntry, true);
1420 Member aMemMonth;
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);
1431 if (!pDayEntry)
1433 maChecks->InsertEntry(aDayName, pMonthEntry, false);
1434 Member aMemDay;
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)
1449 Member aMember;
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 )
1461 Init();
1464 SvTreeListEntry* ScCheckListBox::FindEntry( SvTreeListEntry* pParent, const OUString& sNode )
1466 sal_uInt16 nRootPos = 0;
1467 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : GetEntry( nRootPos );
1468 while ( pEntry )
1470 if ( sNode.equals(GetEntryText( pEntry )) )
1471 return pEntry;
1473 pEntry = pParent ? NextSibling( pEntry ) : GetEntry( ++nRootPos );
1475 return NULL;
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)
1489 return true;
1490 return false;
1493 void ScCheckListBox::CheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bCheck )
1495 SvTreeListEntry* pEntry = FindEntry( pParent, sName );
1496 if ( pEntry )
1497 CheckEntry( pEntry, bCheck );
1500 // Recursively check all children of pParent
1501 void ScCheckListBox::CheckAllChildren( SvTreeListEntry* pParent, bool bCheck )
1503 if ( pParent )
1505 SetCheckButtonState(
1506 pParent, bCheck ? SvButtonState( SV_BUTTON_CHECKED ) :
1507 SvButtonState( SV_BUTTON_UNCHECKED ) );
1509 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1510 while ( pEntry )
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);
1524 if ( pAncestor )
1526 while ( pAncestor )
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;
1534 while ( pChild )
1536 if ( GetCheckButtonState( pChild ) == SV_BUTTON_CHECKED )
1538 bChildChecked = true;
1539 break;
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 );
1554 if ( bShow )
1556 if ( !pEntry )
1558 pEntry = InsertEntry(
1559 sName, NULL, false, TREELIST_APPEND, NULL,
1560 SvLBoxButtonKind_enabledCheckbox);
1562 SetCheckButtonState(
1563 pEntry, bCheck ? SV_BUTTON_CHECKED : SV_BUTTON_UNCHECKED);
1565 else
1566 CheckEntry( pEntry, bCheck );
1568 else if ( pEntry )
1569 RemoveParentKeepChildren( pEntry );
1572 SvTreeListEntry* ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
1574 if ( pParent && GetCheckButtonState( pParent ) == SV_BUTTON_CHECKED )
1575 nCount++;
1576 // Iterate over the children
1577 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1578 while ( pEntry )
1580 CountCheckedEntries( pEntry, nCount );
1581 pEntry = NextSibling( pEntry );
1583 return NULL;
1586 sal_uInt16 ScCheckListBox::GetCheckedEntryCount() const
1588 sal_uLong nCount = 0;
1589 CountCheckedEntries( NULL, nCount );
1590 return nCount;
1593 void ScCheckListBox::ExpandChildren( SvTreeListEntry* pParent )
1595 if ( pParent )
1596 Expand( pParent );
1597 // Iterate over the children
1598 SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1599 while ( pEntry )
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();
1613 if ( pEntry )
1615 bool bCheck = ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED );
1616 CheckEntry( pEntry, !bCheck );
1617 if ( bCheck != ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) )
1618 CheckButtonHdl();
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);
1642 if (pEntry)
1643 maChecks->Expand(pEntry);
1646 else
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)
1660 ++nVisMemCount;
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;
1674 else
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)
1686 maConfig = rConfig;
1689 bool ScCheckListMenuWindow::isAllSelected() const
1691 return maChkToggleAll->IsChecked();
1694 void ScCheckListMenuWindow::getResult(ResultType& rResult)
1696 ResultType aResult;
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 );
1706 OUString sName;
1707 if ( maMembers[i].mbDate )
1708 sName = maMembers[i].maRealName;
1709 else
1710 sName = maMembers[i].maName;
1711 aResult.insert(ResultType::value_type(sName, bState));
1714 rResult.swap(aResult);
1717 void ScCheckListMenuWindow::launch(const Rectangle& rRect)
1719 packWindow();
1720 if (!maConfig.mbAllowEmptySet)
1721 // We need to have at least one member selected.
1722 maBtnOk->Enable(maChecks->GetCheckedEntryCount() != 0);
1724 Rectangle aRect(rRect);
1725 if (maConfig.mbRTL)
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
1735 // size.
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();
1749 EndPopupMode();
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: */