xref: /trunk/main/sc/source/ui/cctrl/dpcontrol.cxx (revision b3f79822)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 // INCLUDE ---------------------------------------------------------------
28 
29 #include "dpcontrol.hxx"
30 #include "dpcontrol.hrc"
31 
32 #include <vcl/outdev.hxx>
33 #include <vcl/settings.hxx>
34 #include <tools/wintypes.hxx>
35 #include <vcl/decoview.hxx>
36 #include "strload.hxx"
37 #include "global.hxx"
38 #include "scitems.hxx"
39 #include "document.hxx"
40 #include "docpool.hxx"
41 #include "patattr.hxx"
42 
43 #include "AccessibleFilterMenu.hxx"
44 #include "AccessibleFilterTopWindow.hxx"
45 
46 #include <com/sun/star/accessibility/XAccessible.hpp>
47 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
48 
49 using ::com::sun::star::uno::Reference;
50 using ::com::sun::star::accessibility::XAccessible;
51 using ::com::sun::star::accessibility::XAccessibleContext;
52 using ::rtl::OUString;
53 using ::rtl::OUStringHash;
54 using ::std::vector;
55 using ::std::hash_map;
56 using ::std::auto_ptr;
57 
ScDPFieldButton(OutputDevice * pOutDev,const StyleSettings * pStyle,const Fraction * pZoomX,const Fraction * pZoomY,ScDocument * pDoc)58 ScDPFieldButton::ScDPFieldButton(OutputDevice* pOutDev, const StyleSettings* pStyle, const Fraction* pZoomX, const Fraction* pZoomY, ScDocument* pDoc) :
59     mpDoc(pDoc),
60     mpOutDev(pOutDev),
61     mpStyle(pStyle),
62     mbBaseButton(true),
63     mbPopupButton(false),
64     mbHasHiddenMember(false),
65     mbPopupPressed(false),
66     mbPopupLeft(false)
67 {
68     if (pZoomX)
69         maZoomX = *pZoomX;
70     else
71         maZoomX = Fraction(1, 1);
72 
73     if (pZoomY)
74         maZoomY = *pZoomY;
75     else
76         maZoomY = Fraction(1, 1);
77 }
78 
~ScDPFieldButton()79 ScDPFieldButton::~ScDPFieldButton()
80 {
81 }
82 
setText(const OUString & rText)83 void ScDPFieldButton::setText(const OUString& rText)
84 {
85     maText = rText;
86 }
87 
setBoundingBox(const Point & rPos,const Size & rSize,bool bLayoutRTL)88 void ScDPFieldButton::setBoundingBox(const Point& rPos, const Size& rSize, bool bLayoutRTL)
89 {
90     maPos = rPos;
91     maSize = rSize;
92     if (bLayoutRTL)
93     {
94         // rPos is the logical-left position, adjust maPos to visual-left (inside the cell border)
95         maPos.X() -= maSize.Width() - 1;
96     }
97 }
98 
setDrawBaseButton(bool b)99 void ScDPFieldButton::setDrawBaseButton(bool b)
100 {
101     mbBaseButton = b;
102 }
103 
setDrawPopupButton(bool b)104 void ScDPFieldButton::setDrawPopupButton(bool b)
105 {
106     mbPopupButton = b;
107 }
108 
setHasHiddenMember(bool b)109 void ScDPFieldButton::setHasHiddenMember(bool b)
110 {
111     mbHasHiddenMember = b;
112 }
113 
setPopupPressed(bool b)114 void ScDPFieldButton::setPopupPressed(bool b)
115 {
116     mbPopupPressed = b;
117 }
118 
setPopupLeft(bool b)119 void ScDPFieldButton::setPopupLeft(bool b)
120 {
121     mbPopupLeft = b;
122 }
123 
draw()124 void ScDPFieldButton::draw()
125 {
126     const long nMargin = 2;
127     bool bOldMapEnablaed = mpOutDev->IsMapModeEnabled();
128     mpOutDev->EnableMapMode(false);
129 
130     if (mbBaseButton)
131     {
132         // Background
133         Rectangle aRect(maPos, maSize);
134         mpOutDev->SetLineColor(mpStyle->GetFaceColor());
135         mpOutDev->SetFillColor(mpStyle->GetFaceColor());
136         mpOutDev->DrawRect(aRect);
137 
138         // Border lines
139         mpOutDev->SetLineColor(mpStyle->GetLightColor());
140         mpOutDev->DrawLine(Point(maPos), Point(maPos.X(), maPos.Y()+maSize.Height()-1));
141         mpOutDev->DrawLine(Point(maPos), Point(maPos.X()+maSize.Width()-1, maPos.Y()));
142 
143         mpOutDev->SetLineColor(mpStyle->GetShadowColor());
144         mpOutDev->DrawLine(Point(maPos.X(), maPos.Y()+maSize.Height()-1),
145                            Point(maPos.X()+maSize.Width()-1, maPos.Y()+maSize.Height()-1));
146         mpOutDev->DrawLine(Point(maPos.X()+maSize.Width()-1, maPos.Y()),
147                            Point(maPos.X()+maSize.Width()-1, maPos.Y()+maSize.Height()-1));
148 
149         // Field name.
150         // Get the font and size the same way as in scenario selection (lcl_DrawOneFrame in gridwin4.cxx)
151         Font aTextFont( mpStyle->GetAppFont() );
152         if ( mpDoc )
153         {
154             //  use ScPatternAttr::GetFont only for font size
155             Font aAttrFont;
156             static_cast<const ScPatternAttr&>(mpDoc->GetPool()->GetDefaultItem(ATTR_PATTERN)).
157                 GetFont( aAttrFont, SC_AUTOCOL_BLACK, mpOutDev, &maZoomY );
158             aTextFont.SetSize( aAttrFont.GetSize() );
159         }
160         mpOutDev->SetFont(aTextFont);
161         mpOutDev->SetTextColor(mpStyle->GetButtonTextColor());
162 
163         Point aTextPos = maPos;
164         long nTHeight = mpOutDev->GetTextHeight();
165         aTextPos.setX(maPos.getX() + nMargin);
166         aTextPos.setY(maPos.getY() + (maSize.Height()-nTHeight)/2);
167 
168         mpOutDev->Push(PUSH_CLIPREGION);
169         mpOutDev->IntersectClipRegion(aRect);
170         mpOutDev->DrawText(aTextPos, maText);
171         mpOutDev->Pop();
172     }
173 
174     if (mbPopupButton)
175         drawPopupButton();
176 
177     mpOutDev->EnableMapMode(bOldMapEnablaed);
178 }
179 
getPopupBoundingBox(Point & rPos,Size & rSize) const180 void ScDPFieldButton::getPopupBoundingBox(Point& rPos, Size& rSize) const
181 {
182     long nW = maSize.getWidth() / 2;
183     long nH = maSize.getHeight();
184     if (nW > 18)
185         nW = 18;
186     if (nH > 18)
187         nH = 18;
188 
189     // #i114944# AutoFilter button is left-aligned in RTL.
190     // DataPilot button is always right-aligned for now, so text output isn't affected.
191     if (mbPopupLeft)
192         rPos.setX(maPos.getX());
193     else
194         rPos.setX(maPos.getX() + maSize.getWidth() - nW);
195     rPos.setY(maPos.getY() + maSize.getHeight() - nH);
196     rSize.setWidth(nW);
197     rSize.setHeight(nH);
198 }
199 
drawPopupButton()200 void ScDPFieldButton::drawPopupButton()
201 {
202     Point aPos;
203     Size aSize;
204     getPopupBoundingBox(aPos, aSize);
205 
206     // Background & outer black border
207     mpOutDev->SetLineColor(COL_BLACK);
208     mpOutDev->SetFillColor(mpStyle->GetFaceColor());
209     mpOutDev->DrawRect(Rectangle(aPos, aSize));
210 
211     if (!mbPopupPressed)
212     {
213         // border lines
214         mpOutDev->SetLineColor(mpStyle->GetLightColor());
215         mpOutDev->DrawLine(Point(aPos.X()+1, aPos.Y()+1), Point(aPos.X()+1, aPos.Y()+aSize.Height()-2));
216         mpOutDev->DrawLine(Point(aPos.X()+1, aPos.Y()+1), Point(aPos.X()+aSize.Width()-2, aPos.Y()+1));
217 
218         mpOutDev->SetLineColor(mpStyle->GetShadowColor());
219         mpOutDev->DrawLine(Point(aPos.X()+1, aPos.Y()+aSize.Height()-2),
220                            Point(aPos.X()+aSize.Width()-2, aPos.Y()+aSize.Height()-2));
221         mpOutDev->DrawLine(Point(aPos.X()+aSize.Width()-2, aPos.Y()+1),
222                            Point(aPos.X()+aSize.Width()-2, aPos.Y()+aSize.Height()-2));
223     }
224 
225     // the arrowhead
226     Color aArrowColor = mbHasHiddenMember ? mpStyle->GetHighlightLinkColor() : mpStyle->GetButtonTextColor();
227     mpOutDev->SetLineColor(aArrowColor);
228     mpOutDev->SetFillColor(aArrowColor);
229     Point aCenter(aPos.X() + (aSize.Width() >> 1), aPos.Y() + (aSize.Height() >> 1));
230     Point aPos1, aPos2;
231     aPos1.X() = aCenter.X() - 4;
232     aPos2.X() = aCenter.X() + 4;
233     aPos1.Y() = aCenter.Y() - 3;
234     aPos2.Y() = aCenter.Y() - 3;
235 
236     if (mbPopupPressed)
237     {
238         aPos1.X() += 1;
239         aPos2.X() += 1;
240         aPos1.Y() += 1;
241         aPos2.Y() += 1;
242     }
243 
244     do
245     {
246         ++aPos1.X();
247         --aPos2.X();
248         ++aPos1.Y();
249         ++aPos2.Y();
250         mpOutDev->DrawLine(aPos1, aPos2);
251     }
252     while (aPos1 != aPos2);
253 
254     if (mbHasHiddenMember)
255     {
256         // tiny little box to display in presence of hidden member(s).
257         Point aBoxPos(aPos.X() + aSize.Width() - 5, aPos.Y() + aSize.Height() - 5);
258         if (mbPopupPressed)
259         {
260             aBoxPos.X() += 1;
261             aBoxPos.Y() += 1;
262         }
263         Size aBoxSize(3, 3);
264         mpOutDev->DrawRect(Rectangle(aBoxPos, aBoxSize));
265     }
266 }
267 
268 // ============================================================================
269 
MenuItemData()270 ScMenuFloatingWindow::MenuItemData::MenuItemData() :
271     mbEnabled(true),
272     mpAction(static_cast<ScDPFieldPopupWindow::Action*>(NULL)),
273     mpSubMenuWin(static_cast<ScMenuFloatingWindow*>(NULL))
274 {
275 }
276 
277 // ----------------------------------------------------------------------------
278 
SubMenuItemData(ScMenuFloatingWindow * pParent)279 ScMenuFloatingWindow::SubMenuItemData::SubMenuItemData(ScMenuFloatingWindow* pParent) :
280     mpSubMenu(NULL),
281     mnMenuPos(MENU_NOT_SELECTED),
282     mpParent(pParent)
283 {
284     maTimer.SetTimeoutHdl( LINK(this, ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl) );
285     maTimer.SetTimeout(mpParent->GetSettings().GetMouseSettings().GetMenuDelay());
286 }
287 
reset()288 void ScMenuFloatingWindow::SubMenuItemData::reset()
289 {
290     mpSubMenu = NULL;
291     mnMenuPos = MENU_NOT_SELECTED;
292     maTimer.Stop();
293 }
294 
IMPL_LINK(ScMenuFloatingWindow::SubMenuItemData,TimeoutHdl,void *,EMPTYARG)295 IMPL_LINK( ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl, void*, EMPTYARG )
296 {
297     mpParent->handleMenuTimeout(this);
298     return 0;
299 }
300 
301 // ----------------------------------------------------------------------------
302 
303 size_t ScMenuFloatingWindow::MENU_NOT_SELECTED = 999;
304 
ScMenuFloatingWindow(Window * pParent,ScDocument * pDoc,sal_uInt16 nMenuStackLevel)305 ScMenuFloatingWindow::ScMenuFloatingWindow(Window* pParent, ScDocument* pDoc, sal_uInt16 nMenuStackLevel) :
306     PopupMenuFloatingWindow(pParent),
307     maOpenTimer(this),
308     maCloseTimer(this),
309     maName(OUString::createFromAscii("ScMenuFloatingWindow")),
310     mnSelectedMenu(MENU_NOT_SELECTED),
311     mnClickedMenu(MENU_NOT_SELECTED),
312     mpDoc(pDoc),
313     mpParentMenu(dynamic_cast<ScMenuFloatingWindow*>(pParent)),
314     mpActiveSubMenu(NULL)
315 {
316     SetMenuStackLevel(nMenuStackLevel);
317 
318     // TODO: How do we get the right font to use here ?
319     const sal_uInt16 nPopupFontHeight = 12;
320     const StyleSettings& rStyle = GetSettings().GetStyleSettings();
321     maLabelFont = rStyle.GetLabelFont();
322     maLabelFont.SetHeight(nPopupFontHeight);
323     SetFont(maLabelFont);
324 
325     SetText(OUString::createFromAscii("ScMenuFloatingWindow"));
326     SetPopupModeEndHdl( LINK(this, ScMenuFloatingWindow, PopupEndHdl) );
327 }
328 
~ScMenuFloatingWindow()329 ScMenuFloatingWindow::~ScMenuFloatingWindow()
330 {
331     EndPopupMode();
332 }
333 
MouseMove(const MouseEvent & rMEvt)334 void ScMenuFloatingWindow::MouseMove(const MouseEvent& rMEvt)
335 {
336     const Point& rPos = rMEvt.GetPosPixel();
337     size_t nSelectedMenu = getEnclosingMenuItem(rPos);
338     setSelectedMenuItem(nSelectedMenu, true, false);
339 
340     Window::MouseMove(rMEvt);
341 }
342 
MouseButtonDown(const MouseEvent & rMEvt)343 void ScMenuFloatingWindow::MouseButtonDown(const MouseEvent& rMEvt)
344 {
345     const Point& rPos = rMEvt.GetPosPixel();
346     mnClickedMenu = getEnclosingMenuItem(rPos);
347     Window::MouseButtonDown(rMEvt);
348 }
349 
MouseButtonUp(const MouseEvent & rMEvt)350 void ScMenuFloatingWindow::MouseButtonUp(const MouseEvent& rMEvt)
351 {
352     executeMenuItem(mnClickedMenu);
353     mnClickedMenu = MENU_NOT_SELECTED;
354     Window::MouseButtonUp(rMEvt);
355 }
356 
KeyInput(const KeyEvent & rKEvt)357 void ScMenuFloatingWindow::KeyInput(const KeyEvent& rKEvt)
358 {
359     const KeyCode& rKeyCode = rKEvt.GetKeyCode();
360     bool bHandled = true;
361     size_t nSelectedMenu = mnSelectedMenu;
362     size_t nLastMenuPos = maMenuItems.size() - 1;
363     switch (rKeyCode.GetCode())
364     {
365         case KEY_UP:
366             if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == 0)
367                 nSelectedMenu = nLastMenuPos;
368             else
369                 --nSelectedMenu;
370             setSelectedMenuItem(nSelectedMenu, false, false);
371         break;
372         case KEY_DOWN:
373             if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == nLastMenuPos)
374                 nSelectedMenu = 0;
375             else
376                 ++nSelectedMenu;
377             setSelectedMenuItem(nSelectedMenu, false, false);
378         break;
379         case KEY_LEFT:
380             if (mpParentMenu)
381                 mpParentMenu->endSubMenu(this);
382         break;
383         case KEY_RIGHT:
384         {
385             if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
386                 break;
387 
388             const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
389             if (!rMenu.mbEnabled || !rMenu.mpSubMenuWin)
390                 break;
391 
392             maOpenTimer.mnMenuPos = mnSelectedMenu;
393             maOpenTimer.mpSubMenu = rMenu.mpSubMenuWin.get();
394             launchSubMenu(true);
395         }
396         break;
397         case KEY_RETURN:
398             if (nSelectedMenu != MENU_NOT_SELECTED)
399                 executeMenuItem(nSelectedMenu);
400         break;
401         default:
402             bHandled = false;
403     }
404 
405     if (!bHandled)
406         Window::KeyInput(rKEvt);
407 }
408 
Paint(const Rectangle &)409 void ScMenuFloatingWindow::Paint(const Rectangle& /*rRect*/)
410 {
411     const StyleSettings& rStyle = GetSettings().GetStyleSettings();
412     Color aBackColor = rStyle.GetMenuColor();
413     Color aBorderColor = rStyle.GetShadowColor();
414 
415     Rectangle aCtrlRect(Point(0, 0), GetOutputSizePixel());
416 
417     // Window background
418     bool bNativeDrawn = true;
419     if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
420     {
421         SetClipRegion();
422         bNativeDrawn = DrawNativeControl(
423             CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
424             ImplControlValue(), OUString());
425     }
426     else
427         bNativeDrawn = false;
428 
429     if (!bNativeDrawn)
430     {
431         SetFillColor(aBackColor);
432         SetLineColor(aBorderColor);
433         DrawRect(aCtrlRect);
434     }
435 
436     // Menu items
437     SetTextColor(rStyle.GetMenuTextColor());
438     drawAllMenuItems();
439 }
440 
CreateAccessible()441 Reference<XAccessible> ScMenuFloatingWindow::CreateAccessible()
442 {
443     if (!mxAccessible.is())
444     {
445         Reference<XAccessible> xAccParent = mpParentMenu ?
446             mpParentMenu->GetAccessible() : GetAccessibleParentWindow()->GetAccessible();
447 
448         mxAccessible.set(new ScAccessibleFilterMenu(xAccParent, this, maName, 999, getDoc()));
449         ScAccessibleFilterMenu* p = static_cast<ScAccessibleFilterMenu*>(
450             mxAccessible.get());
451 
452         vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
453         for (itr = itrBeg; itr != itrEnd; ++itr)
454         {
455             size_t nPos = ::std::distance(itrBeg, itr);
456             p->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
457         }
458     }
459 
460     return mxAccessible;
461 }
462 
addMenuItem(const OUString & rText,bool bEnabled,Action * pAction)463 void ScMenuFloatingWindow::addMenuItem(const OUString& rText, bool bEnabled, Action* pAction)
464 {
465     MenuItemData aItem;
466     aItem.maText = rText;
467     aItem.mbEnabled = bEnabled;
468     aItem.mpAction.reset(pAction);
469     maMenuItems.push_back(aItem);
470 }
471 
addSubMenuItem(const OUString & rText,bool bEnabled)472 ScMenuFloatingWindow* ScMenuFloatingWindow::addSubMenuItem(const OUString& rText, bool bEnabled)
473 {
474     MenuItemData aItem;
475     aItem.maText = rText;
476     aItem.mbEnabled = bEnabled;
477     aItem.mpSubMenuWin.reset(new ScMenuFloatingWindow(this, mpDoc, GetMenuStackLevel()+1));
478     aItem.mpSubMenuWin->setName(rText);
479     maMenuItems.push_back(aItem);
480     return aItem.mpSubMenuWin.get();
481 }
482 
drawMenuItem(size_t nPos)483 void ScMenuFloatingWindow::drawMenuItem(size_t nPos)
484 {
485     if (nPos >= maMenuItems.size())
486         return;
487 
488     Point aPos;
489     Size aSize;
490     getMenuItemPosSize(nPos, aPos, aSize);
491 
492     DecorationView aDecoView(this);
493     long nXOffset = 5;
494     long nYOffset = (aSize.Height() - maLabelFont.GetHeight())/2;
495     DrawCtrlText(Point(aPos.X()+nXOffset, aPos.Y() + nYOffset), maMenuItems[nPos].maText, 0, STRING_LEN,
496                  maMenuItems[nPos].mbEnabled ? TEXT_DRAW_MNEMONIC : TEXT_DRAW_DISABLE);
497 
498     if (maMenuItems[nPos].mpSubMenuWin)
499     {
500         long nFontHeight = maLabelFont.GetHeight();
501         Point aMarkerPos = aPos;
502         aMarkerPos.Y() += aSize.Height()/2 - nFontHeight/4 + 1;
503         aMarkerPos.X() += aSize.Width() - nFontHeight + nFontHeight/4;
504         Size aMarkerSize(nFontHeight/2, nFontHeight/2);
505         aDecoView.DrawSymbol(Rectangle(aMarkerPos, aMarkerSize),
506                              SYMBOL_SPIN_RIGHT, GetTextColor(), 0);
507     }
508 }
509 
drawAllMenuItems()510 void ScMenuFloatingWindow::drawAllMenuItems()
511 {
512     size_t n = maMenuItems.size();
513     for (size_t i = 0; i < n; ++i)
514         highlightMenuItem(i, i == mnSelectedMenu);
515 }
516 
getLabelFont() const517 const Font& ScMenuFloatingWindow::getLabelFont() const
518 {
519     return maLabelFont;
520 }
521 
executeMenuItem(size_t nPos)522 void ScMenuFloatingWindow::executeMenuItem(size_t nPos)
523 {
524     if (nPos >= maMenuItems.size())
525         return;
526 
527     if (!maMenuItems[nPos].mpAction)
528         // no action is defined.
529         return;
530 
531     maMenuItems[nPos].mpAction->execute();
532     terminateAllPopupMenus();
533 }
534 
setSelectedMenuItem(size_t nPos,bool bSubMenuTimer,bool bEnsureSubMenu)535 void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu)
536 {
537     if (mnSelectedMenu == nPos)
538         // nothing to do.
539         return;
540 
541     if (bEnsureSubMenu)
542     {
543         // Dismiss any child popup menu windows.
544         if (mnSelectedMenu < maMenuItems.size() &&
545             maMenuItems[mnSelectedMenu].mpSubMenuWin &&
546             maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
547         {
548             maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
549         }
550 
551         // The popup is not visible, yet a menu item is selected.  The request
552         // most likely comes from the accessible object.  Make sure this
553         // window, as well as all its parent windows are visible.
554         if (!IsVisible() && mpParentMenu)
555             mpParentMenu->ensureSubMenuVisible(this);
556     }
557 
558     selectMenuItem(mnSelectedMenu, false, bSubMenuTimer);
559     selectMenuItem(nPos, true, bSubMenuTimer);
560     mnSelectedMenu = nPos;
561 
562     fireMenuHighlightedEvent();
563 }
564 
getSelectedMenuItem() const565 size_t ScMenuFloatingWindow::getSelectedMenuItem() const
566 {
567     return mnSelectedMenu;
568 }
569 
handleMenuTimeout(SubMenuItemData * pTimer)570 void ScMenuFloatingWindow::handleMenuTimeout(SubMenuItemData* pTimer)
571 {
572     if (pTimer == &maOpenTimer)
573     {
574         // Close any open submenu immediately.
575         if (maCloseTimer.mpSubMenu)
576         {
577             maCloseTimer.mpSubMenu->EndPopupMode();
578             maCloseTimer.mpSubMenu = NULL;
579             maCloseTimer.maTimer.Stop();
580         }
581 
582         launchSubMenu(false);
583     }
584     else if (pTimer == &maCloseTimer)
585     {
586         // end submenu.
587         if (maCloseTimer.mpSubMenu)
588         {
589             maOpenTimer.mpSubMenu = NULL;
590 
591             maCloseTimer.mpSubMenu->EndPopupMode();
592             maCloseTimer.mpSubMenu = NULL;
593 
594             highlightMenuItem(maOpenTimer.mnMenuPos, false);
595             maOpenTimer.mnMenuPos = MENU_NOT_SELECTED;
596         }
597     }
598 }
599 
queueLaunchSubMenu(size_t nPos,ScMenuFloatingWindow * pMenu)600 void ScMenuFloatingWindow::queueLaunchSubMenu(size_t nPos, ScMenuFloatingWindow* pMenu)
601 {
602     if (!pMenu)
603         return;
604 
605     // Set the submenu on launch queue.
606     if (maOpenTimer.mpSubMenu)
607     {
608         if (maOpenTimer.mpSubMenu == pMenu)
609         {
610             if (pMenu == maCloseTimer.mpSubMenu)
611                 maCloseTimer.reset();
612             return;
613         }
614 
615         // new submenu is being requested.
616         queueCloseSubMenu();
617     }
618 
619     maOpenTimer.mpSubMenu = pMenu;
620     maOpenTimer.mnMenuPos = nPos;
621     maOpenTimer.maTimer.Start();
622 }
623 
queueCloseSubMenu()624 void ScMenuFloatingWindow::queueCloseSubMenu()
625 {
626     if (!maOpenTimer.mpSubMenu)
627         // There is no submenu to close.
628         return;
629 
630     // Stop any submenu on queue for opening.
631     maOpenTimer.maTimer.Stop();
632 
633     maCloseTimer.mpSubMenu = maOpenTimer.mpSubMenu;
634     maCloseTimer.mnMenuPos = maOpenTimer.mnMenuPos;
635     maCloseTimer.maTimer.Start();
636 }
637 
launchSubMenu(bool bSetMenuPos)638 void ScMenuFloatingWindow::launchSubMenu(bool bSetMenuPos)
639 {
640     Point aPos;
641     Size aSize;
642     getMenuItemPosSize(maOpenTimer.mnMenuPos, aPos, aSize);
643     ScMenuFloatingWindow* pSubMenu = maOpenTimer.mpSubMenu;
644 
645     if (!pSubMenu)
646         return;
647 
648     sal_uInt32 nOldFlags = GetPopupModeFlags();
649     SetPopupModeFlags(nOldFlags | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE);
650     pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
651     pSubMenu->StartPopupMode(
652         Rectangle(aPos,aSize), (FLOATWIN_POPUPMODE_RIGHT | FLOATWIN_POPUPMODE_GRABFOCUS));
653     pSubMenu->AddPopupModeWindow(this);
654     if (bSetMenuPos)
655         pSubMenu->setSelectedMenuItem(0, false, false); // select menu item after the popup becomes fully visible.
656     SetPopupModeFlags(nOldFlags);
657 }
658 
endSubMenu(ScMenuFloatingWindow * pSubMenu)659 void ScMenuFloatingWindow::endSubMenu(ScMenuFloatingWindow* pSubMenu)
660 {
661     if (!pSubMenu)
662         return;
663 
664     pSubMenu->EndPopupMode();
665     maOpenTimer.reset();
666 
667     size_t nMenuPos = getSubMenuPos(pSubMenu);
668     if (nMenuPos != MENU_NOT_SELECTED)
669     {
670         highlightMenuItem(nMenuPos, true);
671         mnSelectedMenu = nMenuPos;
672         fireMenuHighlightedEvent();
673     }
674 }
675 
fillMenuItemsToAccessible(ScAccessibleFilterMenu * pAccMenu) const676 void ScMenuFloatingWindow::fillMenuItemsToAccessible(ScAccessibleFilterMenu* pAccMenu) const
677 {
678     vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
679     for (itr = itrBeg; itr != itrEnd; ++itr)
680     {
681         size_t nPos = ::std::distance(itrBeg, itr);
682         pAccMenu->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
683     }
684 }
685 
getDoc()686 ScDocument* ScMenuFloatingWindow::getDoc()
687 {
688     return mpDoc;
689 }
690 
resizeToFitMenuItems()691 void ScMenuFloatingWindow::resizeToFitMenuItems()
692 {
693     if (maMenuItems.empty())
694         return;
695 
696     vector<MenuItemData>::const_iterator itr = maMenuItems.begin(), itrEnd = maMenuItems.end();
697     long nTextWidth = 0;
698     for (; itr != itrEnd; ++itr)
699         nTextWidth = ::std::max(GetTextWidth(itr->maText), nTextWidth);
700 
701     size_t nLastPos = maMenuItems.size()-1;
702     Point aPos;
703     Size aSize;
704     getMenuItemPosSize(nLastPos, aPos, aSize);
705     aPos.X() += nTextWidth + 15;
706     aPos.Y() += aSize.Height() + 5;
707     SetOutputSizePixel(Size(aPos.X(), aPos.Y()));
708 }
709 
selectMenuItem(size_t nPos,bool bSelected,bool bSubMenuTimer)710 void ScMenuFloatingWindow::selectMenuItem(size_t nPos, bool bSelected, bool bSubMenuTimer)
711 {
712     if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
713     {
714         queueCloseSubMenu();
715         return;
716     }
717 
718     if (!maMenuItems[nPos].mbEnabled)
719     {
720         queueCloseSubMenu();
721         return;
722     }
723 
724     highlightMenuItem(nPos, bSelected);
725 
726     if (bSelected)
727     {
728         if (mpParentMenu)
729             mpParentMenu->setSubMenuFocused(this);
730 
731         if (bSubMenuTimer)
732         {
733             if (maMenuItems[nPos].mpSubMenuWin)
734             {
735                 ScMenuFloatingWindow* pSubMenu = maMenuItems[nPos].mpSubMenuWin.get();
736                 queueLaunchSubMenu(nPos, pSubMenu);
737             }
738             else
739                 queueCloseSubMenu();
740         }
741     }
742 }
743 
clearSelectedMenuItem()744 void ScMenuFloatingWindow::clearSelectedMenuItem()
745 {
746     selectMenuItem(mnSelectedMenu, false, false);
747     mnSelectedMenu = MENU_NOT_SELECTED;
748 }
749 
getSubMenuWindow(size_t nPos) const750 ScMenuFloatingWindow* ScMenuFloatingWindow::getSubMenuWindow(size_t nPos) const
751 {
752     if (maMenuItems.size() <= nPos)
753         return NULL;
754 
755     return maMenuItems[nPos].mpSubMenuWin.get();
756 }
757 
isMenuItemSelected(size_t nPos) const758 bool ScMenuFloatingWindow::isMenuItemSelected(size_t nPos) const
759 {
760     return nPos == mnSelectedMenu;
761 }
762 
setName(const OUString & rName)763 void ScMenuFloatingWindow::setName(const OUString& rName)
764 {
765     maName = rName;
766 }
767 
getName() const768 const OUString& ScMenuFloatingWindow::getName() const
769 {
770     return maName;
771 }
772 
highlightMenuItem(size_t nPos,bool bSelected)773 void ScMenuFloatingWindow::highlightMenuItem(size_t nPos, bool bSelected)
774 {
775     if (nPos == MENU_NOT_SELECTED)
776         return;
777 
778     const StyleSettings& rStyle = GetSettings().GetStyleSettings();
779     Color aBackColor = rStyle.GetMenuColor();
780     SetFillColor(aBackColor);
781     SetLineColor(aBackColor);
782 
783     Point aPos;
784     Size aSize;
785     getMenuItemPosSize(nPos, aPos, aSize);
786     Rectangle aRegion(aPos,aSize);
787 
788     if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
789     {
790         Push(PUSH_CLIPREGION);
791         IntersectClipRegion(Rectangle(aPos, aSize));
792         Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
793         DrawNativeControl(
794             CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
795             ImplControlValue(), OUString());
796 
797         Pop();
798     }
799 
800     bool bNativeDrawn = true;
801     if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM))
802     {
803         ControlState nState = bSelected ? CTRL_STATE_SELECTED : 0;
804         if (maMenuItems[nPos].mbEnabled)
805             nState |= CTRL_STATE_ENABLED;
806         bNativeDrawn = DrawNativeControl(
807             CTRL_MENU_POPUP, PART_MENU_ITEM, aRegion, nState, ImplControlValue(), OUString());
808     }
809     else
810         bNativeDrawn = false;
811 
812     if (!bNativeDrawn)
813     {
814         if (bSelected)
815         {
816             aBackColor = rStyle.GetMenuHighlightColor();
817             SetFillColor(aBackColor);
818             SetLineColor(aBackColor);
819         }
820         DrawRect(Rectangle(aPos,aSize));
821     }
822 
823     Color aTextColor = bSelected ? rStyle.GetMenuHighlightTextColor() : rStyle.GetMenuTextColor();
824     SetTextColor(aTextColor);
825     drawMenuItem(nPos);
826 }
827 
getMenuItemPosSize(size_t nPos,Point & rPos,Size & rSize) const828 void ScMenuFloatingWindow::getMenuItemPosSize(size_t nPos, Point& rPos, Size& rSize) const
829 {
830     const sal_uInt16 nLeftMargin = 5;
831     const sal_uInt16 nTopMargin = 5;
832     const sal_uInt16 nMenuItemHeight = static_cast< sal_uInt16 >( maLabelFont.GetHeight()*1.8 );
833 
834     Size aWndSize = GetSizePixel();
835 
836     Point aPos1(nLeftMargin, nTopMargin);
837     Size aSize1(aWndSize.Width() - nLeftMargin*2, nMenuItemHeight);
838 
839     rPos = aPos1;
840     rPos.Y() += aSize1.Height()*nPos;
841     rSize = aSize1;
842 }
843 
getParentMenuWindow() const844 ScMenuFloatingWindow* ScMenuFloatingWindow::getParentMenuWindow() const
845 {
846     return mpParentMenu;
847 }
848 
getEnclosingMenuItem(const Point & rPos) const849 size_t ScMenuFloatingWindow::getEnclosingMenuItem(const Point& rPos) const
850 {
851     size_t n = maMenuItems.size();
852     for (size_t i = 0; i < n; ++i)
853     {
854         Point aPos;
855         Size aSize;
856         getMenuItemPosSize(i, aPos, aSize);
857         Rectangle aRect(aPos, aSize);
858         if (aRect.IsInside(rPos))
859             return i;
860     }
861     return MENU_NOT_SELECTED;
862 }
863 
getSubMenuPos(ScMenuFloatingWindow * pSubMenu)864 size_t ScMenuFloatingWindow::getSubMenuPos(ScMenuFloatingWindow* pSubMenu)
865 {
866     size_t n = maMenuItems.size();
867     for (size_t i = 0; i < n; ++i)
868     {
869         if (maMenuItems[i].mpSubMenuWin.get() == pSubMenu)
870             return i;
871     }
872     return MENU_NOT_SELECTED;
873 }
874 
fireMenuHighlightedEvent()875 void ScMenuFloatingWindow::fireMenuHighlightedEvent()
876 {
877     if (mnSelectedMenu == MENU_NOT_SELECTED)
878         return;
879 
880     if (!mxAccessible.is())
881         return;
882 
883     Reference<XAccessibleContext> xAccCxt = mxAccessible->getAccessibleContext();
884     if (!xAccCxt.is())
885         return;
886 
887     Reference<XAccessible> xAccMenu = xAccCxt->getAccessibleChild(mnSelectedMenu);
888     if (!xAccMenu.is())
889         return;
890 
891     VclAccessibleEvent aEvent(VCLEVENT_MENU_HIGHLIGHT, xAccMenu);
892     FireVclEvent(&aEvent);
893 }
894 
setSubMenuFocused(ScMenuFloatingWindow * pSubMenu)895 void ScMenuFloatingWindow::setSubMenuFocused(ScMenuFloatingWindow* pSubMenu)
896 {
897     maCloseTimer.reset();
898     size_t nMenuPos = getSubMenuPos(pSubMenu);
899     if (mnSelectedMenu != nMenuPos)
900     {
901         highlightMenuItem(nMenuPos, true);
902         mnSelectedMenu = nMenuPos;
903     }
904 }
905 
ensureSubMenuVisible(ScMenuFloatingWindow * pSubMenu)906 void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu)
907 {
908     if (mpParentMenu)
909         mpParentMenu->ensureSubMenuVisible(this);
910 
911     if (pSubMenu->IsVisible())
912         return;
913 
914     // Find the menu position of the submenu.
915     size_t nMenuPos = getSubMenuPos(pSubMenu);
916     if (nMenuPos != MENU_NOT_SELECTED)
917     {
918         setSelectedMenuItem(nMenuPos, false, false);
919 
920         Point aPos;
921         Size aSize;
922         getMenuItemPosSize(nMenuPos, aPos, aSize);
923 
924         sal_uInt32 nOldFlags = GetPopupModeFlags();
925         SetPopupModeFlags(nOldFlags | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE);
926         pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
927         pSubMenu->StartPopupMode(
928             Rectangle(aPos,aSize), (FLOATWIN_POPUPMODE_RIGHT | FLOATWIN_POPUPMODE_GRABFOCUS));
929         pSubMenu->AddPopupModeWindow(this);
930         SetPopupModeFlags(nOldFlags);
931     }
932 }
933 
ensureSubMenuNotVisible()934 void ScMenuFloatingWindow::ensureSubMenuNotVisible()
935 {
936     if (mnSelectedMenu <= maMenuItems.size() &&
937         maMenuItems[mnSelectedMenu].mpSubMenuWin &&
938         maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
939     {
940         maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
941     }
942 
943     EndPopupMode();
944 }
945 
terminateAllPopupMenus()946 void ScMenuFloatingWindow::terminateAllPopupMenus()
947 {
948     EndPopupMode();
949     if (mpParentMenu)
950         mpParentMenu->terminateAllPopupMenus();
951 }
952 
IMPL_LINK(ScMenuFloatingWindow,PopupEndHdl,void *,EMPTYARG)953 IMPL_LINK( ScMenuFloatingWindow, PopupEndHdl, void*, EMPTYARG )
954 {
955     clearSelectedMenuItem();
956     return 0;
957 }
958 
959 // ============================================================================
960 
Member()961 ScDPFieldPopupWindow::Member::Member() :
962     mbVisible(true)
963 {
964 }
965 
966 // ----------------------------------------------------------------------------
967 
CancelButton(ScDPFieldPopupWindow * pParent)968 ScDPFieldPopupWindow::CancelButton::CancelButton(ScDPFieldPopupWindow* pParent) :
969     ::CancelButton(pParent), mpParent(pParent) {}
970 
Click()971 void ScDPFieldPopupWindow::CancelButton::Click()
972 {
973     mpParent->EndPopupMode();
974     ::CancelButton::Click();
975 }
976 
977 // ----------------------------------------------------------------------------
978 
ScDPFieldPopupWindow(Window * pParent,ScDocument * pDoc)979 ScDPFieldPopupWindow::ScDPFieldPopupWindow(Window* pParent, ScDocument* pDoc) :
980     ScMenuFloatingWindow(pParent, pDoc),
981     maChecks(this, 0),
982     maChkToggleAll(this, 0),
983     maBtnSelectSingle  (this, 0),
984     maBtnUnselectSingle(this, 0),
985     maBtnOk(this),
986     maBtnCancel(this),
987     mnCurTabStop(0),
988     mpExtendedData(NULL),
989     mpOKAction(NULL),
990     maWndSize(240, 330),
991     mePrevToggleAllState(STATE_DONTKNOW)
992 {
993     maTabStopCtrls.reserve(7);
994     maTabStopCtrls.push_back(this);
995     maTabStopCtrls.push_back(&maChecks);
996     maTabStopCtrls.push_back(&maChkToggleAll);
997     maTabStopCtrls.push_back(&maBtnSelectSingle);
998     maTabStopCtrls.push_back(&maBtnUnselectSingle);
999     maTabStopCtrls.push_back(&maBtnOk);
1000     maTabStopCtrls.push_back(&maBtnCancel);
1001 
1002     const StyleSettings& rStyle = GetSettings().GetStyleSettings();
1003 
1004     Point aPos;
1005     Size aSize;
1006     getSectionPosSize(aPos, aSize, WHOLE);
1007     SetOutputSizePixel(aSize);
1008     Size aOutSize = GetOutputSizePixel();
1009 
1010     getSectionPosSize(aPos, aSize, BTN_OK);
1011     maBtnOk.SetPosSizePixel(aPos, aSize);
1012     maBtnOk.SetFont(getLabelFont());
1013     maBtnOk.SetClickHdl( LINK(this, ScDPFieldPopupWindow, ButtonHdl) );
1014     maBtnOk.Show();
1015 
1016     getSectionPosSize(aPos, aSize, BTN_CANCEL);
1017     maBtnCancel.SetPosSizePixel(aPos, aSize);
1018     maBtnCancel.SetFont(getLabelFont());
1019     maBtnCancel.Show();
1020 
1021     getSectionPosSize(aPos, aSize, LISTBOX_AREA_INNER);
1022     maChecks.SetPosSizePixel(aPos, aSize);
1023     maChecks.SetFont(getLabelFont());
1024     maChecks.SetCheckButtonHdl( LINK(this, ScDPFieldPopupWindow, CheckHdl) );
1025     maChecks.Show();
1026 
1027     getSectionPosSize(aPos, aSize, CHECK_TOGGLE_ALL);
1028     maChkToggleAll.SetPosSizePixel(aPos, aSize);
1029     maChkToggleAll.SetFont(getLabelFont());
1030     maChkToggleAll.SetText(ScRscStrLoader(RID_POPUP_FILTER, STR_BTN_TOGGLE_ALL).GetString());
1031     maChkToggleAll.SetControlBackground(rStyle.GetMenuColor());
1032     maChkToggleAll.SetClickHdl( LINK(this, ScDPFieldPopupWindow, TriStateHdl) );
1033     maChkToggleAll.Show();
1034 
1035     getSectionPosSize(aPos, aSize, BTN_SINGLE_SELECT);
1036     maBtnSelectSingle.SetPosSizePixel(aPos, aSize);
1037     maBtnSelectSingle.SetQuickHelpText(ScRscStrLoader(RID_POPUP_FILTER, STR_BTN_SELECT_CURRENT).GetString());
1038     maBtnSelectSingle.SetModeImage(Image(ScResId(RID_IMG_SELECT_CURRENT)), BMP_COLOR_NORMAL);
1039     maBtnSelectSingle.SetClickHdl( LINK(this, ScDPFieldPopupWindow, ButtonHdl) );
1040     maBtnSelectSingle.Show();
1041 
1042     getSectionPosSize(aPos, aSize, BTN_SINGLE_UNSELECT);
1043     maBtnUnselectSingle.SetPosSizePixel(aPos, aSize);
1044     maBtnUnselectSingle.SetQuickHelpText(ScRscStrLoader(RID_POPUP_FILTER, STR_BTN_UNSELECT_CURRENT).GetString());
1045     maBtnUnselectSingle.SetModeImage(Image(ScResId(RID_IMG_UNSELECT_CURRENT)), BMP_COLOR_NORMAL);
1046     maBtnUnselectSingle.SetClickHdl( LINK(this, ScDPFieldPopupWindow, ButtonHdl) );
1047     maBtnUnselectSingle.Show();
1048 }
1049 
~ScDPFieldPopupWindow()1050 ScDPFieldPopupWindow::~ScDPFieldPopupWindow()
1051 {
1052 }
1053 
getSectionPosSize(Point & rPos,Size & rSize,SectionType eType) const1054 void ScDPFieldPopupWindow::getSectionPosSize(Point& rPos, Size& rSize, SectionType eType) const
1055 {
1056     // constant parameters.
1057     const sal_uInt16 nListBoxMargin = 5;            // horizontal distance from the side of the dialog to the listbox border.
1058     const sal_uInt16 nListBoxInnerPadding = 5;
1059     const sal_uInt16 nTopMargin = 5;
1060     const sal_uInt16 nMenuHeight = 60;
1061     const sal_uInt16 nSingleItemBtnAreaHeight = 32; // height of the middle area below the list box where the single-action buttons are.
1062     const sal_uInt16 nBottomBtnAreaHeight = 50;     // height of the bottom area where the OK and Cancel buttons are.
1063     const sal_uInt16 nBtnWidth = 90;
1064     const sal_uInt16 nLabelHeight = static_cast< sal_uInt16 >( getLabelFont().GetHeight() );
1065     const sal_uInt16 nBtnHeight = nLabelHeight*2;
1066     const sal_uInt16 nBottomMargin = 10;
1067     const sal_uInt16 nMenuListMargin = 20;
1068 
1069     // parameters calculated from constants.
1070     const sal_uInt16 nListBoxWidth = static_cast< sal_uInt16 >( maWndSize.Width() - nListBoxMargin*2 );
1071     const sal_uInt16 nListBoxHeight = static_cast< sal_uInt16 >( maWndSize.Height() - nTopMargin - nMenuHeight -
1072         nMenuListMargin - nSingleItemBtnAreaHeight - nBottomBtnAreaHeight );
1073 
1074     const sal_uInt16 nSingleBtnAreaY = nTopMargin + nMenuHeight + nListBoxHeight + nMenuListMargin - 1;
1075 
1076     switch (eType)
1077     {
1078         case WHOLE:
1079         {
1080             rPos  = Point(0, 0);
1081             rSize = maWndSize;
1082         }
1083         break;
1084         case LISTBOX_AREA_OUTER:
1085         {
1086             rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
1087             rSize = Size(nListBoxWidth, nListBoxHeight);
1088         }
1089         break;
1090         case LISTBOX_AREA_INNER:
1091         {
1092             rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
1093             rPos.X() += nListBoxInnerPadding;
1094             rPos.Y() += nListBoxInnerPadding;
1095 
1096             rSize = Size(nListBoxWidth, nListBoxHeight);
1097             rSize.Width()  -= nListBoxInnerPadding*2;
1098             rSize.Height() -= nListBoxInnerPadding*2;
1099         }
1100         break;
1101         case SINGLE_BTN_AREA:
1102         {
1103             rPos = Point(nListBoxMargin, nSingleBtnAreaY);
1104             rSize = Size(nListBoxWidth, nSingleItemBtnAreaHeight);
1105         }
1106         break;
1107         case CHECK_TOGGLE_ALL:
1108         {
1109             long h = nLabelHeight*3/2; // check box height is heuristically 150% of the text height.
1110             rPos = Point(nListBoxMargin, nSingleBtnAreaY);
1111             rPos.X() += 5;
1112             rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
1113             rSize = Size(70, h);
1114         }
1115         break;
1116         case BTN_SINGLE_SELECT:
1117         {
1118             long h = 26;
1119             rPos = Point(nListBoxMargin, nSingleBtnAreaY);
1120             rPos.X() += 150;
1121             rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
1122             rSize = Size(h, h);
1123         }
1124         break;
1125         case BTN_SINGLE_UNSELECT:
1126         {
1127             long h = 26;
1128             rPos = Point(nListBoxMargin, nSingleBtnAreaY);
1129             rPos.X() += 150 + h + 10;
1130             rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
1131             rSize = Size(h, h);
1132         }
1133         break;
1134         case BTN_OK:
1135         {
1136             long x = (maWndSize.Width() - nBtnWidth*2)/3;
1137             long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
1138             rPos = Point(x, y);
1139             rSize = Size(nBtnWidth, nBtnHeight);
1140         }
1141         break;
1142         case BTN_CANCEL:
1143         {
1144             long x = (maWndSize.Width() - nBtnWidth*2)/3*2 + nBtnWidth;
1145             long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
1146             rPos = Point(x, y);
1147             rSize = Size(nBtnWidth, nBtnHeight);
1148         }
1149         break;
1150         default:
1151             ;
1152     }
1153 }
1154 
setAllMemberState(bool bSet)1155 void ScDPFieldPopupWindow::setAllMemberState(bool bSet)
1156 {
1157     size_t n = maMembers.size();
1158     for (size_t i = 0; i < n; ++i)
1159         maChecks.CheckEntryPos(static_cast< sal_uInt16 >( i ), bSet);
1160 }
1161 
selectCurrentMemberOnly(bool bSet)1162 void ScDPFieldPopupWindow::selectCurrentMemberOnly(bool bSet)
1163 {
1164     setAllMemberState(!bSet);
1165     sal_uInt16 nSelected = maChecks.GetSelectEntryPos();
1166     maChecks.CheckEntryPos(nSelected, bSet);
1167 }
1168 
cycleFocus(bool bReverse)1169 void ScDPFieldPopupWindow::cycleFocus(bool bReverse)
1170 {
1171     maTabStopCtrls[mnCurTabStop]->SetFakeFocus(false);
1172     maTabStopCtrls[mnCurTabStop]->LoseFocus();
1173     if (mnCurTabStop == 0)
1174         clearSelectedMenuItem();
1175 
1176     if (bReverse)
1177     {
1178         if (mnCurTabStop > 0)
1179             --mnCurTabStop;
1180         else
1181             mnCurTabStop = maTabStopCtrls.size() - 1;
1182     }
1183     else
1184     {
1185         ++mnCurTabStop;
1186         if (mnCurTabStop >= maTabStopCtrls.size())
1187             mnCurTabStop = 0;
1188     }
1189     maTabStopCtrls[mnCurTabStop]->SetFakeFocus(true);
1190     maTabStopCtrls[mnCurTabStop]->GrabFocus();
1191 }
1192 
IMPL_LINK(ScDPFieldPopupWindow,ButtonHdl,Button *,pBtn)1193 IMPL_LINK( ScDPFieldPopupWindow, ButtonHdl, Button*, pBtn )
1194 {
1195     if (pBtn == &maBtnOk)
1196         close(true);
1197     else if (pBtn == &maBtnSelectSingle)
1198     {
1199         selectCurrentMemberOnly(true);
1200         CheckHdl(&maChecks);
1201     }
1202     else if (pBtn == &maBtnUnselectSingle)
1203     {
1204         selectCurrentMemberOnly(false);
1205         CheckHdl(&maChecks);
1206     }
1207     return 0;
1208 }
1209 
IMPL_LINK(ScDPFieldPopupWindow,TriStateHdl,TriStateBox *,EMPTYARG)1210 IMPL_LINK( ScDPFieldPopupWindow, TriStateHdl, TriStateBox*, EMPTYARG )
1211 {
1212     switch (mePrevToggleAllState)
1213     {
1214         case STATE_NOCHECK:
1215             maChkToggleAll.SetState(STATE_CHECK);
1216             setAllMemberState(true);
1217         break;
1218         case STATE_CHECK:
1219             maChkToggleAll.SetState(STATE_NOCHECK);
1220             setAllMemberState(false);
1221         break;
1222         case STATE_DONTKNOW:
1223         default:
1224             maChkToggleAll.SetState(STATE_CHECK);
1225             setAllMemberState(true);
1226         break;
1227     }
1228 
1229     mePrevToggleAllState = maChkToggleAll.GetState();
1230     return 0;
1231 }
1232 
IMPL_LINK(ScDPFieldPopupWindow,CheckHdl,SvTreeListBox *,pChecks)1233 IMPL_LINK( ScDPFieldPopupWindow, CheckHdl, SvTreeListBox*, pChecks )
1234 {
1235     if (pChecks != &maChecks)
1236         return 0;
1237 
1238     size_t nNumChecked = maChecks.GetCheckedEntryCount();
1239     if (nNumChecked == maMembers.size())
1240         // all members visible
1241         maChkToggleAll.SetState(STATE_CHECK);
1242     else if (nNumChecked == 0)
1243         // no members visible
1244         maChkToggleAll.SetState(STATE_NOCHECK);
1245     else
1246         maChkToggleAll.SetState(STATE_DONTKNOW);
1247 
1248     mePrevToggleAllState = maChkToggleAll.GetState();
1249     return 0;
1250 }
1251 
MouseMove(const MouseEvent & rMEvt)1252 void ScDPFieldPopupWindow::MouseMove(const MouseEvent& rMEvt)
1253 {
1254     ScMenuFloatingWindow::MouseMove(rMEvt);
1255 
1256     size_t nSelectedMenu = getSelectedMenuItem();
1257     if (nSelectedMenu == MENU_NOT_SELECTED)
1258         queueCloseSubMenu();
1259 }
1260 
Notify(NotifyEvent & rNEvt)1261 long ScDPFieldPopupWindow::Notify(NotifyEvent& rNEvt)
1262 {
1263     switch (rNEvt.GetType())
1264     {
1265         case EVENT_KEYUP:
1266         {
1267             const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
1268             const KeyCode& rCode = pKeyEvent->GetKeyCode();
1269             bool bShift = rCode.IsShift();
1270             if (rCode.GetCode() == KEY_TAB)
1271             {
1272                 cycleFocus(bShift);
1273                 return true;
1274             }
1275         }
1276         break;
1277     }
1278     return ScMenuFloatingWindow::Notify(rNEvt);
1279 }
1280 
Paint(const Rectangle & rRect)1281 void ScDPFieldPopupWindow::Paint(const Rectangle& rRect)
1282 {
1283     ScMenuFloatingWindow::Paint(rRect);
1284 
1285     const StyleSettings& rStyle = GetSettings().GetStyleSettings();
1286     Color aMemberBackColor = rStyle.GetFieldColor();
1287     Color aBorderColor = rStyle.GetShadowColor();
1288 
1289     Point aPos;
1290     Size aSize;
1291     getSectionPosSize(aPos, aSize, LISTBOX_AREA_OUTER);
1292 
1293     // Member list box background
1294     SetFillColor(aMemberBackColor);
1295     SetLineColor(aBorderColor);
1296     DrawRect(Rectangle(aPos,aSize));
1297 
1298     // Single-action button box
1299     getSectionPosSize(aPos, aSize, SINGLE_BTN_AREA);
1300     SetFillColor(rStyle.GetMenuColor());
1301     DrawRect(Rectangle(aPos,aSize));
1302 }
1303 
GetPreferredKeyInputWindow()1304 Window* ScDPFieldPopupWindow::GetPreferredKeyInputWindow()
1305 {
1306     return maTabStopCtrls[mnCurTabStop];
1307 }
1308 
CreateAccessible()1309 Reference<XAccessible> ScDPFieldPopupWindow::CreateAccessible()
1310 {
1311     if (!mxAccessible.is())
1312     {
1313         mxAccessible.set(new ScAccessibleFilterTopWindow(
1314             GetAccessibleParentWindow()->GetAccessible(), this, getName(), getDoc()));
1315         ScAccessibleFilterTopWindow* pAccTop = static_cast<ScAccessibleFilterTopWindow*>(mxAccessible.get());
1316         fillMenuItemsToAccessible(pAccTop);
1317 
1318         pAccTop->setAccessibleChild(
1319             maChecks.CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX);
1320         pAccTop->setAccessibleChild(
1321             maChkToggleAll.CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL);
1322         pAccTop->setAccessibleChild(
1323             maBtnSelectSingle.CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_ON_BTN);
1324         pAccTop->setAccessibleChild(
1325             maBtnUnselectSingle.CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_OFF_BTN);
1326         pAccTop->setAccessibleChild(
1327             maBtnOk.CreateAccessible(), ScAccessibleFilterTopWindow::OK_BTN);
1328         pAccTop->setAccessibleChild(
1329             maBtnCancel.CreateAccessible(), ScAccessibleFilterTopWindow::CANCEL_BTN);
1330     }
1331 
1332     return mxAccessible;
1333 }
1334 
setMemberSize(size_t n)1335 void ScDPFieldPopupWindow::setMemberSize(size_t n)
1336 {
1337     maMembers.reserve(n);
1338 }
1339 
addMember(const OUString & rName,bool bVisible)1340 void ScDPFieldPopupWindow::addMember(const OUString& rName, bool bVisible)
1341 {
1342     Member aMember;
1343     aMember.maName = rName;
1344     aMember.mbVisible = bVisible;
1345     maMembers.push_back(aMember);
1346 }
1347 
initMembers()1348 void ScDPFieldPopupWindow::initMembers()
1349 {
1350     size_t n = maMembers.size();
1351     size_t nVisMemCount = 0;
1352     for (size_t i = 0; i < n; ++i)
1353     {
1354         maChecks.InsertEntry(maMembers[i].maName);
1355         maChecks.CheckEntryPos(static_cast< sal_uInt16 >( i ), maMembers[i].mbVisible);
1356         if (maMembers[i].mbVisible)
1357             ++nVisMemCount;
1358     }
1359     if (nVisMemCount == n)
1360     {
1361         // all members visible
1362         maChkToggleAll.SetState(STATE_CHECK);
1363         mePrevToggleAllState = STATE_CHECK;
1364     }
1365     else if (nVisMemCount == 0)
1366     {
1367         // no members visible
1368         maChkToggleAll.SetState(STATE_NOCHECK);
1369         mePrevToggleAllState = STATE_NOCHECK;
1370     }
1371     else
1372     {
1373         maChkToggleAll.SetState(STATE_DONTKNOW);
1374         mePrevToggleAllState = STATE_DONTKNOW;
1375     }
1376 }
1377 
getWindowSize() const1378 const Size& ScDPFieldPopupWindow::getWindowSize() const
1379 {
1380     return maWndSize;
1381 }
1382 
getResult(hash_map<OUString,bool,OUStringHash> & rResult)1383 void ScDPFieldPopupWindow::getResult(hash_map<OUString, bool, OUStringHash>& rResult)
1384 {
1385     typedef hash_map<OUString, bool, OUStringHash> ResultMap;
1386     ResultMap aResult;
1387     size_t n = maMembers.size();
1388     for (size_t i = 0; i < n; ++i)
1389     {
1390         bool bState = maChecks.IsChecked(static_cast< sal_uInt16 >( i ));
1391         aResult.insert(ResultMap::value_type(maMembers[i].maName, bState));
1392     }
1393     rResult.swap(aResult);
1394 }
1395 
close(bool bOK)1396 void ScDPFieldPopupWindow::close(bool bOK)
1397 {
1398     if (bOK && mpOKAction.get())
1399         mpOKAction->execute();
1400 
1401     EndPopupMode();
1402 }
1403 
setExtendedData(ExtendedData * p)1404 void ScDPFieldPopupWindow::setExtendedData(ExtendedData* p)
1405 {
1406     mpExtendedData.reset(p);
1407 }
1408 
getExtendedData()1409 ScDPFieldPopupWindow::ExtendedData* ScDPFieldPopupWindow::getExtendedData()
1410 {
1411     return mpExtendedData.get();
1412 }
1413 
setOKAction(Action * p)1414 void ScDPFieldPopupWindow::setOKAction(Action* p)
1415 {
1416     mpOKAction.reset(p);
1417 }
1418 
1419