Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "checklistmenu.hxx"
21 : #include "checklistmenu.hrc"
22 : #include "strload.hxx"
23 :
24 : #include <vcl/decoview.hxx>
25 : #include <vcl/settings.hxx>
26 : #include <tools/wintypes.hxx>
27 :
28 : #include "AccessibleFilterMenu.hxx"
29 : #include "AccessibleFilterTopWindow.hxx"
30 :
31 : #include <com/sun/star/accessibility/XAccessible.hpp>
32 : #include <com/sun/star/accessibility/XAccessibleContext.hpp>
33 : #include <svtools/fmtfield.hxx>
34 : #include "document.hxx"
35 :
36 : using namespace com::sun::star;
37 : using ::com::sun::star::uno::Reference;
38 : using ::com::sun::star::accessibility::XAccessible;
39 : using ::com::sun::star::accessibility::XAccessibleContext;
40 : using ::std::vector;
41 : using ::boost::unordered_map;
42 :
43 0 : ScMenuFloatingWindow::MenuItemData::MenuItemData() :
44 : mbEnabled(true), mbSeparator(false),
45 : mpAction(static_cast<ScCheckListMenuWindow::Action*>(NULL)),
46 0 : mpSubMenuWin(static_cast<ScMenuFloatingWindow*>(NULL))
47 : {
48 0 : }
49 :
50 0 : ScMenuFloatingWindow::SubMenuItemData::SubMenuItemData(ScMenuFloatingWindow* pParent) :
51 : mpSubMenu(NULL),
52 : mnMenuPos(MENU_NOT_SELECTED),
53 0 : mpParent(pParent)
54 : {
55 0 : maTimer.SetTimeoutHdl( LINK(this, ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl) );
56 0 : maTimer.SetTimeout(mpParent->GetSettings().GetMouseSettings().GetMenuDelay());
57 0 : }
58 :
59 0 : void ScMenuFloatingWindow::SubMenuItemData::reset()
60 : {
61 0 : mpSubMenu = NULL;
62 0 : mnMenuPos = MENU_NOT_SELECTED;
63 0 : maTimer.Stop();
64 0 : }
65 :
66 0 : IMPL_LINK_NOARG(ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl)
67 : {
68 0 : mpParent->handleMenuTimeout(this);
69 0 : return 0;
70 : }
71 :
72 : size_t ScMenuFloatingWindow::MENU_NOT_SELECTED = 999;
73 :
74 0 : 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 0 : mpParentMenu(dynamic_cast<ScMenuFloatingWindow*>(pParent))
83 : {
84 0 : SetMenuStackLevel(nMenuStackLevel);
85 :
86 : // TODO: How do we get the right font to use here ?
87 0 : const sal_uInt16 nPopupFontHeight = 12;
88 0 : const StyleSettings& rStyle = GetSettings().GetStyleSettings();
89 0 : maLabelFont = rStyle.GetLabelFont();
90 0 : maLabelFont.SetHeight(nPopupFontHeight);
91 0 : SetFont(maLabelFont);
92 :
93 0 : SetText( OUString("ScMenuFloatingWindow") );
94 0 : }
95 :
96 0 : ScMenuFloatingWindow::~ScMenuFloatingWindow()
97 : {
98 0 : EndPopupMode();
99 0 : }
100 :
101 0 : void ScMenuFloatingWindow::PopupModeEnd()
102 : {
103 0 : handlePopupEnd();
104 0 : }
105 :
106 0 : void ScMenuFloatingWindow::MouseMove(const MouseEvent& rMEvt)
107 : {
108 0 : const Point& rPos = rMEvt.GetPosPixel();
109 0 : size_t nSelectedMenu = getEnclosingMenuItem(rPos);
110 0 : setSelectedMenuItem(nSelectedMenu, true, false);
111 :
112 0 : Window::MouseMove(rMEvt);
113 0 : }
114 :
115 0 : void ScMenuFloatingWindow::MouseButtonDown(const MouseEvent& rMEvt)
116 : {
117 0 : const Point& rPos = rMEvt.GetPosPixel();
118 0 : mnClickedMenu = getEnclosingMenuItem(rPos);
119 0 : Window::MouseButtonDown(rMEvt);
120 0 : }
121 :
122 0 : void ScMenuFloatingWindow::MouseButtonUp(const MouseEvent& rMEvt)
123 : {
124 0 : executeMenuItem(mnClickedMenu);
125 0 : mnClickedMenu = MENU_NOT_SELECTED;
126 0 : Window::MouseButtonUp(rMEvt);
127 0 : }
128 :
129 0 : void ScMenuFloatingWindow::KeyInput(const KeyEvent& rKEvt)
130 : {
131 0 : if (maMenuItems.empty())
132 : {
133 0 : Window::KeyInput(rKEvt);
134 0 : return;
135 : }
136 :
137 0 : const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
138 0 : bool bHandled = true;
139 0 : size_t nSelectedMenu = mnSelectedMenu;
140 0 : size_t nLastMenuPos = maMenuItems.size() - 1;
141 0 : switch (rKeyCode.GetCode())
142 : {
143 : case KEY_UP:
144 : {
145 0 : if (nLastMenuPos == 0)
146 : // There is only one menu item. Do nothing.
147 0 : break;
148 :
149 0 : size_t nOldPos = nSelectedMenu;
150 :
151 0 : if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == 0)
152 0 : nSelectedMenu = nLastMenuPos;
153 : else
154 0 : --nSelectedMenu;
155 :
156 : // Loop until a non-separator menu item is found.
157 0 : while (nSelectedMenu != nOldPos)
158 : {
159 0 : if (maMenuItems[nSelectedMenu].mbSeparator)
160 : {
161 0 : if (nSelectedMenu)
162 0 : --nSelectedMenu;
163 : else
164 0 : nSelectedMenu = nLastMenuPos;
165 : }
166 : else
167 0 : break;
168 : }
169 :
170 0 : setSelectedMenuItem(nSelectedMenu, false, false);
171 : }
172 0 : break;
173 : case KEY_DOWN:
174 : {
175 0 : if (nLastMenuPos == 0)
176 : // There is only one menu item. Do nothing.
177 0 : break;
178 :
179 0 : size_t nOldPos = nSelectedMenu;
180 :
181 0 : if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == nLastMenuPos)
182 0 : nSelectedMenu = 0;
183 : else
184 0 : ++nSelectedMenu;
185 :
186 : // Loop until a non-separator menu item is found.
187 0 : while (nSelectedMenu != nOldPos)
188 : {
189 0 : if (maMenuItems[nSelectedMenu].mbSeparator)
190 : {
191 0 : if (nSelectedMenu == nLastMenuPos)
192 0 : nSelectedMenu = 0;
193 : else
194 0 : ++nSelectedMenu;
195 : }
196 : else
197 0 : break;
198 : }
199 :
200 0 : setSelectedMenuItem(nSelectedMenu, false, false);
201 : }
202 0 : break;
203 : case KEY_LEFT:
204 0 : if (mpParentMenu)
205 0 : mpParentMenu->endSubMenu(this);
206 0 : break;
207 : case KEY_RIGHT:
208 : {
209 0 : if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
210 0 : break;
211 :
212 0 : const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
213 0 : if (!rMenu.mbEnabled || !rMenu.mpSubMenuWin)
214 0 : break;
215 :
216 0 : maOpenTimer.mnMenuPos = mnSelectedMenu;
217 0 : maOpenTimer.mpSubMenu = rMenu.mpSubMenuWin.get();
218 0 : launchSubMenu(true);
219 : }
220 0 : break;
221 : case KEY_RETURN:
222 0 : if (nSelectedMenu != MENU_NOT_SELECTED)
223 0 : executeMenuItem(nSelectedMenu);
224 0 : break;
225 : default:
226 0 : bHandled = false;
227 : }
228 :
229 0 : if (!bHandled)
230 0 : Window::KeyInput(rKEvt);
231 : }
232 :
233 0 : void ScMenuFloatingWindow::Paint(const Rectangle& /*rRect*/)
234 : {
235 0 : const StyleSettings& rStyle = GetSettings().GetStyleSettings();
236 0 : Color aBackColor = rStyle.GetMenuColor();
237 0 : Color aBorderColor = rStyle.GetShadowColor();
238 :
239 0 : Rectangle aCtrlRect(Point(0, 0), GetOutputSizePixel());
240 :
241 : // Window background
242 0 : bool bNativeDrawn = true;
243 0 : if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
244 : {
245 0 : SetClipRegion();
246 : bNativeDrawn = DrawNativeControl(
247 : CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
248 0 : ImplControlValue(), OUString());
249 : }
250 : else
251 0 : bNativeDrawn = false;
252 :
253 0 : if (!bNativeDrawn)
254 : {
255 0 : SetFillColor(aBackColor);
256 0 : SetLineColor(aBorderColor);
257 0 : DrawRect(aCtrlRect);
258 : }
259 :
260 : // Menu items
261 0 : SetTextColor(rStyle.GetMenuTextColor());
262 0 : drawAllMenuItems();
263 0 : }
264 :
265 0 : Reference<XAccessible> ScMenuFloatingWindow::CreateAccessible()
266 : {
267 0 : if (!mxAccessible.is())
268 : {
269 : Reference<XAccessible> xAccParent = mpParentMenu ?
270 0 : mpParentMenu->GetAccessible() : GetAccessibleParentWindow()->GetAccessible();
271 :
272 0 : mxAccessible.set(new ScAccessibleFilterMenu(xAccParent, this, maName, 999));
273 : ScAccessibleFilterMenu* p = static_cast<ScAccessibleFilterMenu*>(
274 0 : mxAccessible.get());
275 :
276 0 : vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
277 0 : for (itr = itrBeg; itr != itrEnd; ++itr)
278 : {
279 0 : size_t nPos = ::std::distance(itrBeg, itr);
280 0 : p->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
281 0 : }
282 : }
283 :
284 0 : return mxAccessible;
285 : }
286 :
287 0 : void ScMenuFloatingWindow::addMenuItem(const OUString& rText, bool bEnabled, Action* pAction)
288 : {
289 0 : MenuItemData aItem;
290 0 : aItem.maText = rText;
291 0 : aItem.mbEnabled = bEnabled;
292 0 : aItem.mpAction.reset(pAction);
293 0 : maMenuItems.push_back(aItem);
294 0 : }
295 :
296 0 : void ScMenuFloatingWindow::addSeparator()
297 : {
298 0 : MenuItemData aItem;
299 0 : aItem.mbSeparator = true;
300 0 : maMenuItems.push_back(aItem);
301 0 : }
302 :
303 0 : ScMenuFloatingWindow* ScMenuFloatingWindow::addSubMenuItem(const OUString& rText, bool bEnabled)
304 : {
305 0 : MenuItemData aItem;
306 0 : aItem.maText = rText;
307 0 : aItem.mbEnabled = bEnabled;
308 0 : aItem.mpSubMenuWin.reset(new ScMenuFloatingWindow(this, mpDoc, GetMenuStackLevel()+1));
309 0 : aItem.mpSubMenuWin->setName(rText);
310 0 : maMenuItems.push_back(aItem);
311 0 : return aItem.mpSubMenuWin.get();
312 : }
313 :
314 0 : void ScMenuFloatingWindow::handlePopupEnd()
315 : {
316 0 : clearSelectedMenuItem();
317 0 : }
318 :
319 0 : Size ScMenuFloatingWindow::getMenuSize() const
320 : {
321 0 : if (maMenuItems.empty())
322 0 : return Size();
323 :
324 0 : vector<MenuItemData>::const_iterator itr = maMenuItems.begin(), itrEnd = maMenuItems.end();
325 0 : long nTextWidth = 0;
326 0 : for (; itr != itrEnd; ++itr)
327 : {
328 0 : if (itr->mbSeparator)
329 0 : continue;
330 :
331 0 : nTextWidth = ::std::max(GetTextWidth(itr->maText), nTextWidth);
332 : }
333 :
334 0 : size_t nLastPos = maMenuItems.size()-1;
335 0 : Point aPos;
336 0 : Size aSize;
337 0 : getMenuItemPosSize(nLastPos, aPos, aSize);
338 0 : aPos.X() += nTextWidth + 15;
339 0 : aPos.Y() += aSize.Height() + 5;
340 0 : return Size(aPos.X(), aPos.Y());
341 : }
342 :
343 0 : void ScMenuFloatingWindow::drawMenuItem(size_t nPos)
344 : {
345 0 : if (nPos >= maMenuItems.size())
346 0 : return;
347 :
348 0 : Point aPos;
349 0 : Size aSize;
350 0 : getMenuItemPosSize(nPos, aPos, aSize);
351 :
352 0 : DecorationView aDecoView(this);
353 0 : long nXOffset = 5;
354 0 : long nYOffset = (aSize.Height() - maLabelFont.GetHeight())/2;
355 0 : DrawCtrlText(Point(aPos.X()+nXOffset, aPos.Y() + nYOffset), maMenuItems[nPos].maText, 0,
356 0 : maMenuItems[nPos].maText.getLength(),
357 0 : maMenuItems[nPos].mbEnabled ? TEXT_DRAW_MNEMONIC : TEXT_DRAW_DISABLE);
358 :
359 0 : if (maMenuItems[nPos].mpSubMenuWin)
360 : {
361 0 : long nFontHeight = maLabelFont.GetHeight();
362 0 : Point aMarkerPos = aPos;
363 0 : aMarkerPos.Y() += aSize.Height()/2 - nFontHeight/4 + 1;
364 0 : aMarkerPos.X() += aSize.Width() - nFontHeight + nFontHeight/4;
365 0 : Size aMarkerSize(nFontHeight/2, nFontHeight/2);
366 : aDecoView.DrawSymbol(Rectangle(aMarkerPos, aMarkerSize),
367 0 : SymbolType::SPIN_RIGHT, GetTextColor(), 0);
368 : }
369 : }
370 :
371 0 : void ScMenuFloatingWindow::drawSeparator(size_t nPos)
372 : {
373 0 : Point aPos;
374 0 : Size aSize;
375 0 : getMenuItemPosSize(nPos, aPos, aSize);
376 0 : Rectangle aRegion(aPos,aSize);
377 :
378 0 : if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
379 : {
380 0 : Push(PushFlags::CLIPREGION);
381 0 : IntersectClipRegion(aRegion);
382 0 : Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
383 : DrawNativeControl(
384 : CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
385 0 : ImplControlValue(), OUString());
386 :
387 0 : Pop();
388 : }
389 :
390 0 : bool bNativeDrawn = false;
391 0 : if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_SEPARATOR))
392 : {
393 0 : ControlState nState = 0;
394 0 : const MenuItemData& rData = maMenuItems[nPos];
395 0 : if (rData.mbEnabled)
396 0 : nState |= CTRL_STATE_ENABLED;
397 :
398 : bNativeDrawn = DrawNativeControl(
399 : CTRL_MENU_POPUP, PART_MENU_SEPARATOR,
400 0 : aRegion, nState, ImplControlValue(), OUString());
401 : }
402 :
403 0 : if (!bNativeDrawn)
404 : {
405 0 : const StyleSettings& rStyle = GetSettings().GetStyleSettings();
406 0 : Point aTmpPos = aPos;
407 0 : aTmpPos.Y() += aSize.Height()/2;
408 0 : SetLineColor(rStyle.GetShadowColor());
409 0 : DrawLine(aTmpPos, Point(aSize.Width()+aTmpPos.X(), aTmpPos.Y()));
410 0 : ++aTmpPos.Y();
411 0 : SetLineColor(rStyle.GetLightColor());
412 0 : DrawLine(aTmpPos, Point(aSize.Width()+aTmpPos.X(), aTmpPos.Y()));
413 0 : SetLineColor();
414 : }
415 0 : }
416 :
417 0 : void ScMenuFloatingWindow::drawAllMenuItems()
418 : {
419 0 : size_t n = maMenuItems.size();
420 0 : for (size_t i = 0; i < n; ++i)
421 : {
422 0 : if (maMenuItems[i].mbSeparator)
423 : // Separator
424 0 : drawSeparator(i);
425 : else
426 : // Normal menu item
427 0 : highlightMenuItem(i, i == mnSelectedMenu);
428 : }
429 0 : }
430 :
431 0 : void ScMenuFloatingWindow::executeMenuItem(size_t nPos)
432 : {
433 0 : if (nPos >= maMenuItems.size())
434 0 : return;
435 :
436 0 : if (!maMenuItems[nPos].mpAction)
437 : // no action is defined.
438 0 : return;
439 :
440 0 : maMenuItems[nPos].mpAction->execute();
441 0 : terminateAllPopupMenus();
442 : }
443 :
444 0 : void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu)
445 : {
446 0 : if (mnSelectedMenu == nPos)
447 : // nothing to do.
448 0 : return;
449 :
450 0 : if (bEnsureSubMenu)
451 : {
452 : // Dismiss any child popup menu windows.
453 0 : if (mnSelectedMenu < maMenuItems.size() &&
454 0 : maMenuItems[mnSelectedMenu].mpSubMenuWin &&
455 0 : maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
456 : {
457 0 : maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
458 : }
459 :
460 : // The popup is not visible, yet a menu item is selected. The request
461 : // most likely comes from the accessible object. Make sure this
462 : // window, as well as all its parent windows are visible.
463 0 : if (!IsVisible() && mpParentMenu)
464 0 : mpParentMenu->ensureSubMenuVisible(this);
465 : }
466 :
467 0 : selectMenuItem(mnSelectedMenu, false, bSubMenuTimer);
468 0 : selectMenuItem(nPos, true, bSubMenuTimer);
469 0 : mnSelectedMenu = nPos;
470 :
471 0 : fireMenuHighlightedEvent();
472 : }
473 :
474 0 : void ScMenuFloatingWindow::handleMenuTimeout(SubMenuItemData* pTimer)
475 : {
476 0 : if (pTimer == &maOpenTimer)
477 : {
478 : // Close any open submenu immediately.
479 0 : if (maCloseTimer.mpSubMenu)
480 : {
481 0 : maCloseTimer.mpSubMenu->EndPopupMode();
482 0 : maCloseTimer.mpSubMenu = NULL;
483 0 : maCloseTimer.maTimer.Stop();
484 : }
485 :
486 0 : launchSubMenu(false);
487 : }
488 0 : else if (pTimer == &maCloseTimer)
489 : {
490 : // end submenu.
491 0 : if (maCloseTimer.mpSubMenu)
492 : {
493 0 : maOpenTimer.mpSubMenu = NULL;
494 :
495 0 : maCloseTimer.mpSubMenu->EndPopupMode();
496 0 : maCloseTimer.mpSubMenu = NULL;
497 :
498 0 : highlightMenuItem(maOpenTimer.mnMenuPos, false);
499 0 : maOpenTimer.mnMenuPos = MENU_NOT_SELECTED;
500 : }
501 : }
502 0 : }
503 :
504 0 : void ScMenuFloatingWindow::queueLaunchSubMenu(size_t nPos, ScMenuFloatingWindow* pMenu)
505 : {
506 0 : if (!pMenu)
507 0 : return;
508 :
509 : // Set the submenu on launch queue.
510 0 : if (maOpenTimer.mpSubMenu)
511 : {
512 0 : if (maOpenTimer.mpSubMenu == pMenu)
513 : {
514 0 : if (pMenu == maCloseTimer.mpSubMenu)
515 0 : maCloseTimer.reset();
516 0 : return;
517 : }
518 :
519 : // new submenu is being requested.
520 0 : queueCloseSubMenu();
521 : }
522 :
523 0 : maOpenTimer.mpSubMenu = pMenu;
524 0 : maOpenTimer.mnMenuPos = nPos;
525 0 : maOpenTimer.maTimer.Start();
526 : }
527 :
528 0 : void ScMenuFloatingWindow::queueCloseSubMenu()
529 : {
530 0 : if (!maOpenTimer.mpSubMenu)
531 : // There is no submenu to close.
532 0 : return;
533 :
534 : // Stop any submenu on queue for opening.
535 0 : maOpenTimer.maTimer.Stop();
536 :
537 0 : maCloseTimer.mpSubMenu = maOpenTimer.mpSubMenu;
538 0 : maCloseTimer.mnMenuPos = maOpenTimer.mnMenuPos;
539 0 : maCloseTimer.maTimer.Start();
540 : }
541 :
542 0 : void ScMenuFloatingWindow::launchSubMenu(bool bSetMenuPos)
543 : {
544 0 : Point aPos;
545 0 : Size aSize;
546 0 : getMenuItemPosSize(maOpenTimer.mnMenuPos, aPos, aSize);
547 0 : ScMenuFloatingWindow* pSubMenu = maOpenTimer.mpSubMenu;
548 :
549 0 : if (!pSubMenu)
550 0 : return;
551 :
552 0 : sal_uInt32 nOldFlags = GetPopupModeFlags();
553 0 : SetPopupModeFlags(nOldFlags | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE);
554 0 : pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
555 : pSubMenu->StartPopupMode(
556 0 : Rectangle(aPos,aSize), (FLOATWIN_POPUPMODE_RIGHT | FLOATWIN_POPUPMODE_GRABFOCUS));
557 0 : pSubMenu->AddPopupModeWindow(this);
558 0 : if (bSetMenuPos)
559 0 : pSubMenu->setSelectedMenuItem(0, false, false); // select menu item after the popup becomes fully visible.
560 0 : SetPopupModeFlags(nOldFlags);
561 : }
562 :
563 0 : void ScMenuFloatingWindow::endSubMenu(ScMenuFloatingWindow* pSubMenu)
564 : {
565 0 : if (!pSubMenu)
566 0 : return;
567 :
568 0 : pSubMenu->EndPopupMode();
569 0 : maOpenTimer.reset();
570 :
571 0 : size_t nMenuPos = getSubMenuPos(pSubMenu);
572 0 : if (nMenuPos != MENU_NOT_SELECTED)
573 : {
574 0 : highlightMenuItem(nMenuPos, true);
575 0 : mnSelectedMenu = nMenuPos;
576 0 : fireMenuHighlightedEvent();
577 : }
578 : }
579 :
580 0 : void ScMenuFloatingWindow::fillMenuItemsToAccessible(ScAccessibleFilterMenu* pAccMenu) const
581 : {
582 0 : vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
583 0 : for (itr = itrBeg; itr != itrEnd; ++itr)
584 : {
585 0 : size_t nPos = ::std::distance(itrBeg, itr);
586 0 : pAccMenu->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
587 : }
588 0 : }
589 :
590 0 : void ScMenuFloatingWindow::resizeToFitMenuItems()
591 : {
592 0 : SetOutputSizePixel(getMenuSize());
593 0 : }
594 :
595 0 : void ScMenuFloatingWindow::selectMenuItem(size_t nPos, bool bSelected, bool bSubMenuTimer)
596 : {
597 0 : if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
598 : {
599 0 : queueCloseSubMenu();
600 0 : return;
601 : }
602 :
603 0 : if (!maMenuItems[nPos].mbEnabled)
604 : {
605 0 : queueCloseSubMenu();
606 0 : return;
607 : }
608 :
609 0 : highlightMenuItem(nPos, bSelected);
610 :
611 0 : if (bSelected)
612 : {
613 0 : if (mpParentMenu)
614 0 : mpParentMenu->setSubMenuFocused(this);
615 :
616 0 : if (bSubMenuTimer)
617 : {
618 0 : if (maMenuItems[nPos].mpSubMenuWin)
619 : {
620 0 : ScMenuFloatingWindow* pSubMenu = maMenuItems[nPos].mpSubMenuWin.get();
621 0 : queueLaunchSubMenu(nPos, pSubMenu);
622 : }
623 : else
624 0 : queueCloseSubMenu();
625 : }
626 : }
627 : }
628 :
629 0 : void ScMenuFloatingWindow::clearSelectedMenuItem()
630 : {
631 0 : selectMenuItem(mnSelectedMenu, false, false);
632 0 : mnSelectedMenu = MENU_NOT_SELECTED;
633 0 : }
634 :
635 0 : ScMenuFloatingWindow* ScMenuFloatingWindow::getSubMenuWindow(size_t nPos) const
636 : {
637 0 : if (maMenuItems.size() <= nPos)
638 0 : return NULL;
639 :
640 0 : return maMenuItems[nPos].mpSubMenuWin.get();
641 : }
642 :
643 0 : bool ScMenuFloatingWindow::isMenuItemSelected(size_t nPos) const
644 : {
645 0 : return nPos == mnSelectedMenu;
646 : }
647 :
648 0 : void ScMenuFloatingWindow::setName(const OUString& rName)
649 : {
650 0 : maName = rName;
651 0 : }
652 :
653 0 : void ScMenuFloatingWindow::highlightMenuItem(size_t nPos, bool bSelected)
654 : {
655 0 : if (nPos == MENU_NOT_SELECTED)
656 0 : return;
657 :
658 0 : const StyleSettings& rStyle = GetSettings().GetStyleSettings();
659 0 : Color aBackColor = rStyle.GetMenuColor();
660 0 : SetFillColor(aBackColor);
661 0 : SetLineColor(aBackColor);
662 :
663 0 : Point aPos;
664 0 : Size aSize;
665 0 : getMenuItemPosSize(nPos, aPos, aSize);
666 0 : Rectangle aRegion(aPos,aSize);
667 :
668 0 : if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
669 : {
670 0 : Push(PushFlags::CLIPREGION);
671 0 : IntersectClipRegion(Rectangle(aPos, aSize));
672 0 : Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
673 : DrawNativeControl(
674 : CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED,
675 0 : ImplControlValue(), OUString());
676 :
677 0 : Pop();
678 : }
679 :
680 0 : bool bNativeDrawn = true;
681 0 : if (IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM))
682 : {
683 0 : ControlState nState = bSelected ? CTRL_STATE_SELECTED : 0;
684 0 : if (maMenuItems[nPos].mbEnabled)
685 0 : nState |= CTRL_STATE_ENABLED;
686 : bNativeDrawn = DrawNativeControl(
687 0 : CTRL_MENU_POPUP, PART_MENU_ITEM, aRegion, nState, ImplControlValue(), OUString());
688 : }
689 : else
690 0 : bNativeDrawn = false;
691 :
692 0 : if (!bNativeDrawn)
693 : {
694 0 : if (bSelected)
695 : {
696 0 : aBackColor = rStyle.GetMenuHighlightColor();
697 0 : SetFillColor(aBackColor);
698 0 : SetLineColor(aBackColor);
699 : }
700 0 : DrawRect(Rectangle(aPos,aSize));
701 : }
702 :
703 0 : Color aTextColor = bSelected ? rStyle.GetMenuHighlightTextColor() : rStyle.GetMenuTextColor();
704 0 : SetTextColor(aTextColor);
705 0 : drawMenuItem(nPos);
706 : }
707 :
708 0 : void ScMenuFloatingWindow::getMenuItemPosSize(size_t nPos, Point& rPos, Size& rSize) const
709 : {
710 0 : size_t nCount = maMenuItems.size();
711 0 : if (nPos >= nCount)
712 0 : return;
713 :
714 0 : const sal_uInt16 nLeftMargin = 5;
715 0 : const sal_uInt16 nTopMargin = 5;
716 0 : const sal_uInt16 nMenuItemHeight = static_cast<sal_uInt16>(maLabelFont.GetHeight()*1.8);
717 0 : const sal_uInt16 nSepHeight = static_cast<sal_uInt16>(maLabelFont.GetHeight()*0.8);
718 :
719 0 : Point aPos1(nLeftMargin, nTopMargin);
720 0 : rPos = aPos1;
721 0 : for (size_t i = 0; i < nPos; ++i)
722 0 : rPos.Y() += maMenuItems[i].mbSeparator ? nSepHeight : nMenuItemHeight;
723 :
724 0 : Size aWndSize = GetSizePixel();
725 0 : sal_uInt16 nH = maMenuItems[nPos].mbSeparator ? nSepHeight : nMenuItemHeight;
726 0 : rSize = Size(aWndSize.Width() - nLeftMargin*2, nH);
727 : }
728 :
729 0 : size_t ScMenuFloatingWindow::getEnclosingMenuItem(const Point& rPos) const
730 : {
731 0 : size_t n = maMenuItems.size();
732 0 : for (size_t i = 0; i < n; ++i)
733 : {
734 0 : Point aPos;
735 0 : Size aSize;
736 0 : getMenuItemPosSize(i, aPos, aSize);
737 0 : Rectangle aRect(aPos, aSize);
738 0 : if (aRect.IsInside(rPos))
739 0 : return maMenuItems[i].mbSeparator ? MENU_NOT_SELECTED : i;
740 : }
741 0 : return MENU_NOT_SELECTED;
742 : }
743 :
744 0 : size_t ScMenuFloatingWindow::getSubMenuPos(ScMenuFloatingWindow* pSubMenu)
745 : {
746 0 : size_t n = maMenuItems.size();
747 0 : for (size_t i = 0; i < n; ++i)
748 : {
749 0 : if (maMenuItems[i].mpSubMenuWin.get() == pSubMenu)
750 0 : return i;
751 : }
752 0 : return MENU_NOT_SELECTED;
753 : }
754 :
755 0 : void ScMenuFloatingWindow::fireMenuHighlightedEvent()
756 : {
757 0 : if (mnSelectedMenu == MENU_NOT_SELECTED)
758 0 : return;
759 :
760 0 : if (!mxAccessible.is())
761 0 : return;
762 :
763 0 : Reference<XAccessibleContext> xAccCxt = mxAccessible->getAccessibleContext();
764 0 : if (!xAccCxt.is())
765 0 : return;
766 :
767 0 : Reference<XAccessible> xAccMenu = xAccCxt->getAccessibleChild(mnSelectedMenu);
768 0 : if (!xAccMenu.is())
769 0 : return;
770 :
771 0 : VclAccessibleEvent aEvent(VCLEVENT_MENU_HIGHLIGHT, xAccMenu);
772 0 : FireVclEvent(&aEvent);
773 : }
774 :
775 0 : void ScMenuFloatingWindow::setSubMenuFocused(ScMenuFloatingWindow* pSubMenu)
776 : {
777 0 : maCloseTimer.reset();
778 0 : size_t nMenuPos = getSubMenuPos(pSubMenu);
779 0 : if (mnSelectedMenu != nMenuPos)
780 : {
781 0 : highlightMenuItem(nMenuPos, true);
782 0 : mnSelectedMenu = nMenuPos;
783 : }
784 0 : }
785 :
786 0 : void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu)
787 : {
788 0 : if (mpParentMenu)
789 0 : mpParentMenu->ensureSubMenuVisible(this);
790 :
791 0 : if (pSubMenu->IsVisible())
792 0 : return;
793 :
794 : // Find the menu position of the submenu.
795 0 : size_t nMenuPos = getSubMenuPos(pSubMenu);
796 0 : if (nMenuPos != MENU_NOT_SELECTED)
797 : {
798 0 : setSelectedMenuItem(nMenuPos, false, false);
799 :
800 0 : Point aPos;
801 0 : Size aSize;
802 0 : getMenuItemPosSize(nMenuPos, aPos, aSize);
803 :
804 0 : sal_uInt32 nOldFlags = GetPopupModeFlags();
805 0 : SetPopupModeFlags(nOldFlags | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE);
806 0 : pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
807 : pSubMenu->StartPopupMode(
808 0 : Rectangle(aPos,aSize), (FLOATWIN_POPUPMODE_RIGHT | FLOATWIN_POPUPMODE_GRABFOCUS));
809 0 : pSubMenu->AddPopupModeWindow(this);
810 0 : SetPopupModeFlags(nOldFlags);
811 : }
812 : }
813 :
814 0 : void ScMenuFloatingWindow::ensureSubMenuNotVisible()
815 : {
816 0 : if (mnSelectedMenu <= maMenuItems.size() &&
817 0 : maMenuItems[mnSelectedMenu].mpSubMenuWin &&
818 0 : maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
819 : {
820 0 : maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
821 : }
822 :
823 0 : EndPopupMode();
824 0 : }
825 :
826 0 : void ScMenuFloatingWindow::terminateAllPopupMenus()
827 : {
828 0 : EndPopupMode();
829 0 : if (mpParentMenu)
830 0 : mpParentMenu->terminateAllPopupMenus();
831 0 : }
832 :
833 0 : ScCheckListMenuWindow::Config::Config() :
834 0 : mbAllowEmptySet(true), mbRTL(false)
835 : {
836 0 : }
837 :
838 0 : ScCheckListMenuWindow::Member::Member()
839 : : mbVisible(true)
840 : , mbDate(false)
841 : , mbLeaf(false)
842 0 : , mpParent(NULL)
843 : {
844 0 : }
845 :
846 0 : ScCheckListMenuWindow::CancelButton::CancelButton(ScCheckListMenuWindow* pParent) :
847 0 : ::CancelButton(pParent), mpParent(pParent) {}
848 :
849 0 : void ScCheckListMenuWindow::CancelButton::Click()
850 : {
851 0 : mpParent->EndPopupMode();
852 0 : ::CancelButton::Click();
853 0 : }
854 :
855 0 : ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* pDoc) :
856 : ScMenuFloatingWindow(pParent, pDoc),
857 : maChecks(this, WB_HASBUTTONS | WB_HASLINES | WB_HASLINESATROOT | WB_HASBUTTONSATROOT ),
858 : maChkToggleAll(this, 0),
859 : maBtnSelectSingle (this, 0),
860 : maBtnUnselectSingle(this, 0),
861 : maBtnOk(this),
862 : maBtnCancel(this),
863 : mnCurTabStop(0),
864 : mpExtendedData(NULL),
865 : mpOKAction(NULL),
866 : mpPopupEndAction(NULL),
867 : maWndSize(200, 330),
868 0 : mePrevToggleAllState(TRISTATE_INDET)
869 : {
870 0 : maTabStopCtrls.reserve(7);
871 0 : maTabStopCtrls.push_back(this);
872 0 : maTabStopCtrls.push_back(&maChecks);
873 0 : maTabStopCtrls.push_back(&maChkToggleAll);
874 0 : maTabStopCtrls.push_back(&maBtnSelectSingle);
875 0 : maTabStopCtrls.push_back(&maBtnUnselectSingle);
876 0 : maTabStopCtrls.push_back(&maBtnOk);
877 0 : maTabStopCtrls.push_back(&maBtnCancel);
878 :
879 : // Enable type-ahead search in the check list box.
880 0 : maChecks.SetStyle(maChecks.GetStyle() | WB_QUICK_SEARCH);
881 0 : }
882 :
883 0 : ScCheckListMenuWindow::~ScCheckListMenuWindow()
884 : {
885 0 : }
886 :
887 0 : void ScCheckListMenuWindow::getSectionPosSize(
888 : Point& rPos, Size& rSize, SectionType eType) const
889 : {
890 : // constant parameters.
891 0 : const long nListBoxMargin = 5; // horizontal distance from the side of the dialog to the listbox border.
892 0 : const long nListBoxInnerPadding = 5;
893 0 : const long nTopMargin = 5;
894 0 : const long nMenuHeight = maMenuSize.getHeight();
895 0 : const long nSingleItemBtnAreaHeight = 32; // height of the middle area below the list box where the single-action buttons are.
896 0 : const long nBottomBtnAreaHeight = 50; // height of the bottom area where the OK and Cancel buttons are.
897 0 : const long nBtnWidth = 90;
898 0 : const long nLabelHeight = getLabelFont().GetHeight();
899 0 : const long nBtnHeight = nLabelHeight*2;
900 0 : const long nBottomMargin = 10;
901 0 : const long nMenuListMargin = 5;
902 :
903 : // parameters calculated from constants.
904 0 : const long nListBoxWidth = maWndSize.Width() - nListBoxMargin*2;
905 0 : const long nListBoxHeight = maWndSize.Height() - nTopMargin - nMenuHeight -
906 0 : nMenuListMargin - nSingleItemBtnAreaHeight - nBottomBtnAreaHeight;
907 :
908 0 : const long nSingleBtnAreaY = nTopMargin + nMenuHeight + nListBoxHeight + nMenuListMargin - 1;
909 :
910 0 : switch (eType)
911 : {
912 : case WHOLE:
913 : {
914 0 : rPos = Point(0, 0);
915 0 : rSize = maWndSize;
916 : }
917 0 : break;
918 : case LISTBOX_AREA_OUTER:
919 : {
920 0 : rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
921 0 : rSize = Size(nListBoxWidth, nListBoxHeight);
922 : }
923 0 : break;
924 : case LISTBOX_AREA_INNER:
925 : {
926 0 : rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
927 0 : rPos.X() += nListBoxInnerPadding;
928 0 : rPos.Y() += nListBoxInnerPadding;
929 :
930 0 : rSize = Size(nListBoxWidth, nListBoxHeight);
931 0 : rSize.Width() -= nListBoxInnerPadding*2;
932 0 : rSize.Height() -= nListBoxInnerPadding*2;
933 : }
934 0 : break;
935 : case SINGLE_BTN_AREA:
936 : {
937 0 : rPos = Point(nListBoxMargin, nSingleBtnAreaY);
938 0 : rSize = Size(nListBoxWidth, nSingleItemBtnAreaHeight);
939 : }
940 0 : break;
941 : case CHECK_TOGGLE_ALL:
942 : {
943 0 : long h = std::min(maChkToggleAll.CalcMinimumSize().Height(), 26L);
944 0 : rPos = Point(nListBoxMargin, nSingleBtnAreaY);
945 0 : rPos.X() += 5;
946 0 : rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
947 0 : rSize = Size(70, h);
948 : }
949 0 : break;
950 : case BTN_SINGLE_SELECT:
951 : {
952 0 : long h = 26;
953 0 : rPos = Point(nListBoxMargin, nSingleBtnAreaY);
954 0 : rPos.X() += nListBoxWidth - h - 10 - h - 10;
955 0 : rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
956 0 : rSize = Size(h, h);
957 : }
958 0 : break;
959 : case BTN_SINGLE_UNSELECT:
960 : {
961 0 : long h = 26;
962 0 : rPos = Point(nListBoxMargin, nSingleBtnAreaY);
963 0 : rPos.X() += nListBoxWidth - h - 10;
964 0 : rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
965 0 : rSize = Size(h, h);
966 : }
967 0 : break;
968 : case BTN_OK:
969 : {
970 0 : long x = (maWndSize.Width() - nBtnWidth*2)/3;
971 0 : long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
972 0 : rPos = Point(x, y);
973 0 : rSize = Size(nBtnWidth, nBtnHeight);
974 : }
975 0 : break;
976 : case BTN_CANCEL:
977 : {
978 0 : long x = (maWndSize.Width() - nBtnWidth*2)/3*2 + nBtnWidth;
979 0 : long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
980 0 : rPos = Point(x, y);
981 0 : rSize = Size(nBtnWidth, nBtnHeight);
982 : }
983 0 : break;
984 : default:
985 : ;
986 : }
987 0 : }
988 :
989 0 : void ScCheckListMenuWindow::packWindow()
990 : {
991 0 : maMenuSize = getMenuSize();
992 :
993 0 : if (maWndSize.Width() < maMenuSize.Width())
994 : // Widen the window to fit the menu items.
995 0 : maWndSize.Width() = maMenuSize.Width();
996 :
997 : // Set proper window height based on the number of menu items.
998 0 : if (maWndSize.Height() < maMenuSize.Height()*2.8)
999 0 : maWndSize.Height() = maMenuSize.Height()*2.8;
1000 :
1001 : // TODO: Make sure the window height never exceeds the height of the
1002 : // screen. Also do adjustment based on the number of check box items.
1003 :
1004 0 : SetOutputSizePixel(maWndSize);
1005 :
1006 0 : const StyleSettings& rStyle = GetSettings().GetStyleSettings();
1007 :
1008 0 : Point aPos;
1009 0 : Size aSize;
1010 0 : getSectionPosSize(aPos, aSize, WHOLE);
1011 0 : SetOutputSizePixel(aSize);
1012 :
1013 0 : getSectionPosSize(aPos, aSize, BTN_OK);
1014 0 : maBtnOk.SetPosSizePixel(aPos, aSize);
1015 0 : maBtnOk.SetFont(getLabelFont());
1016 0 : maBtnOk.SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
1017 0 : maBtnOk.Show();
1018 :
1019 0 : getSectionPosSize(aPos, aSize, BTN_CANCEL);
1020 0 : maBtnCancel.SetPosSizePixel(aPos, aSize);
1021 0 : maBtnCancel.SetFont(getLabelFont());
1022 0 : maBtnCancel.Show();
1023 :
1024 0 : getSectionPosSize(aPos, aSize, LISTBOX_AREA_INNER);
1025 0 : maChecks.SetPosSizePixel(aPos, aSize);
1026 0 : maChecks.SetFont(getLabelFont());
1027 0 : maChecks.SetCheckButtonHdl( LINK(this, ScCheckListMenuWindow, CheckHdl) );
1028 0 : maChecks.Show();
1029 :
1030 0 : getSectionPosSize(aPos, aSize, CHECK_TOGGLE_ALL);
1031 0 : maChkToggleAll.SetPosSizePixel(aPos, aSize);
1032 0 : maChkToggleAll.SetFont(getLabelFont());
1033 0 : maChkToggleAll.SetText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_TOGGLE_ALL));
1034 0 : maChkToggleAll.SetTextColor(rStyle.GetMenuTextColor());
1035 0 : maChkToggleAll.SetControlBackground(rStyle.GetMenuColor());
1036 0 : maChkToggleAll.SetClickHdl( LINK(this, ScCheckListMenuWindow, TriStateHdl) );
1037 0 : maChkToggleAll.Show();
1038 :
1039 0 : getSectionPosSize(aPos, aSize, BTN_SINGLE_SELECT);
1040 0 : maBtnSelectSingle.SetPosSizePixel(aPos, aSize);
1041 0 : maBtnSelectSingle.SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_SELECT_CURRENT));
1042 0 : maBtnSelectSingle.SetModeImage(Image(ScResId(RID_IMG_SELECT_CURRENT)));
1043 0 : maBtnSelectSingle.SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
1044 0 : maBtnSelectSingle.Show();
1045 :
1046 0 : getSectionPosSize(aPos, aSize, BTN_SINGLE_UNSELECT);
1047 0 : maBtnUnselectSingle.SetPosSizePixel(aPos, aSize);
1048 0 : maBtnUnselectSingle.SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_UNSELECT_CURRENT));
1049 0 : maBtnUnselectSingle.SetModeImage(Image(ScResId(RID_IMG_UNSELECT_CURRENT)));
1050 0 : maBtnUnselectSingle.SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
1051 0 : maBtnUnselectSingle.Show();
1052 0 : }
1053 :
1054 0 : void ScCheckListMenuWindow::setAllMemberState(bool bSet)
1055 : {
1056 0 : size_t n = maMembers.size();
1057 0 : for (size_t i = 0; i < n; ++i)
1058 0 : maChecks.CheckEntry( maMembers[i].maName, maMembers[i].mpParent, bSet);
1059 :
1060 0 : if (!maConfig.mbAllowEmptySet)
1061 : // We need to have at least one member selected.
1062 0 : maBtnOk.Enable(maChecks.GetCheckedEntryCount() != 0);
1063 0 : }
1064 :
1065 0 : void ScCheckListMenuWindow::selectCurrentMemberOnly(bool bSet)
1066 : {
1067 0 : setAllMemberState(!bSet);
1068 0 : SvTreeListEntry* pEntry = maChecks.GetCurEntry();
1069 0 : maChecks.CheckEntry(pEntry, bSet );
1070 0 : }
1071 :
1072 0 : void ScCheckListMenuWindow::cycleFocus(bool bReverse)
1073 : {
1074 0 : maTabStopCtrls[mnCurTabStop]->SetFakeFocus(false);
1075 0 : maTabStopCtrls[mnCurTabStop]->LoseFocus();
1076 0 : if (mnCurTabStop == 0)
1077 0 : clearSelectedMenuItem();
1078 :
1079 0 : if (bReverse)
1080 : {
1081 0 : if (mnCurTabStop > 0)
1082 0 : --mnCurTabStop;
1083 : else
1084 0 : mnCurTabStop = maTabStopCtrls.size() - 1;
1085 : }
1086 : else
1087 : {
1088 0 : ++mnCurTabStop;
1089 0 : if (mnCurTabStop >= maTabStopCtrls.size())
1090 0 : mnCurTabStop = 0;
1091 : }
1092 0 : maTabStopCtrls[mnCurTabStop]->SetFakeFocus(true);
1093 0 : maTabStopCtrls[mnCurTabStop]->GrabFocus();
1094 0 : }
1095 :
1096 0 : IMPL_LINK( ScCheckListMenuWindow, ButtonHdl, Button*, pBtn )
1097 : {
1098 0 : if (pBtn == &maBtnOk)
1099 0 : close(true);
1100 0 : else if (pBtn == &maBtnSelectSingle)
1101 : {
1102 0 : selectCurrentMemberOnly(true);
1103 0 : CheckHdl(&maChecks);
1104 : }
1105 0 : else if (pBtn == &maBtnUnselectSingle)
1106 : {
1107 0 : selectCurrentMemberOnly(false);
1108 0 : CheckHdl(&maChecks);
1109 : }
1110 0 : return 0;
1111 : }
1112 :
1113 0 : IMPL_LINK_NOARG(ScCheckListMenuWindow, TriStateHdl)
1114 : {
1115 0 : switch (mePrevToggleAllState)
1116 : {
1117 : case TRISTATE_FALSE:
1118 0 : maChkToggleAll.SetState(TRISTATE_TRUE);
1119 0 : setAllMemberState(true);
1120 0 : break;
1121 : case TRISTATE_TRUE:
1122 0 : maChkToggleAll.SetState(TRISTATE_FALSE);
1123 0 : setAllMemberState(false);
1124 0 : break;
1125 : case TRISTATE_INDET:
1126 : default:
1127 0 : maChkToggleAll.SetState(TRISTATE_TRUE);
1128 0 : setAllMemberState(true);
1129 0 : break;
1130 : }
1131 :
1132 0 : mePrevToggleAllState = maChkToggleAll.GetState();
1133 0 : return 0;
1134 : }
1135 :
1136 0 : IMPL_LINK( ScCheckListMenuWindow, CheckHdl, SvTreeListBox*, pChecks )
1137 : {
1138 0 : if (pChecks != &maChecks)
1139 0 : return 0;
1140 0 : SvTreeListEntry* pEntry = pChecks->GetHdlEntry();
1141 0 : if ( pEntry )
1142 0 : maChecks.CheckEntry( pEntry, ( pChecks->GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) );
1143 0 : size_t nNumChecked = maChecks.GetCheckedEntryCount();
1144 0 : if (nNumChecked == maMembers.size())
1145 : // all members visible
1146 0 : maChkToggleAll.SetState(TRISTATE_TRUE);
1147 0 : else if (nNumChecked == 0)
1148 : // no members visible
1149 0 : maChkToggleAll.SetState(TRISTATE_FALSE);
1150 : else
1151 0 : maChkToggleAll.SetState(TRISTATE_INDET);
1152 :
1153 0 : if (!maConfig.mbAllowEmptySet)
1154 : // We need to have at least one member selected.
1155 0 : maBtnOk.Enable(nNumChecked != 0);
1156 :
1157 0 : mePrevToggleAllState = maChkToggleAll.GetState();
1158 0 : return 0;
1159 : }
1160 :
1161 0 : void ScCheckListMenuWindow::MouseMove(const MouseEvent& rMEvt)
1162 : {
1163 0 : ScMenuFloatingWindow::MouseMove(rMEvt);
1164 :
1165 0 : size_t nSelectedMenu = getSelectedMenuItem();
1166 0 : if (nSelectedMenu == MENU_NOT_SELECTED)
1167 0 : queueCloseSubMenu();
1168 0 : }
1169 :
1170 0 : bool ScCheckListMenuWindow::Notify(NotifyEvent& rNEvt)
1171 : {
1172 0 : switch (rNEvt.GetType())
1173 : {
1174 : case EVENT_KEYUP:
1175 : {
1176 0 : const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
1177 0 : const vcl::KeyCode& rCode = pKeyEvent->GetKeyCode();
1178 0 : bool bShift = rCode.IsShift();
1179 0 : if (rCode.GetCode() == KEY_TAB)
1180 : {
1181 0 : cycleFocus(bShift);
1182 0 : return true;
1183 : }
1184 : }
1185 0 : break;
1186 : }
1187 0 : return ScMenuFloatingWindow::Notify(rNEvt);
1188 : }
1189 :
1190 0 : void ScCheckListMenuWindow::Paint(const Rectangle& rRect)
1191 : {
1192 0 : ScMenuFloatingWindow::Paint(rRect);
1193 :
1194 0 : const StyleSettings& rStyle = GetSettings().GetStyleSettings();
1195 0 : Color aMemberBackColor = rStyle.GetFieldColor();
1196 0 : Color aBorderColor = rStyle.GetShadowColor();
1197 :
1198 0 : Point aPos;
1199 0 : Size aSize;
1200 0 : getSectionPosSize(aPos, aSize, LISTBOX_AREA_OUTER);
1201 :
1202 : // Member list box background
1203 0 : SetFillColor(aMemberBackColor);
1204 0 : SetLineColor(aBorderColor);
1205 0 : DrawRect(Rectangle(aPos,aSize));
1206 :
1207 : // Single-action button box
1208 0 : getSectionPosSize(aPos, aSize, SINGLE_BTN_AREA);
1209 0 : SetFillColor(rStyle.GetMenuColor());
1210 0 : DrawRect(Rectangle(aPos,aSize));
1211 0 : }
1212 :
1213 0 : vcl::Window* ScCheckListMenuWindow::GetPreferredKeyInputWindow()
1214 : {
1215 0 : return maTabStopCtrls[mnCurTabStop];
1216 : }
1217 :
1218 0 : Reference<XAccessible> ScCheckListMenuWindow::CreateAccessible()
1219 : {
1220 0 : if (!mxAccessible.is())
1221 : {
1222 : mxAccessible.set(new ScAccessibleFilterTopWindow(
1223 0 : GetAccessibleParentWindow()->GetAccessible(), this, getName()));
1224 0 : ScAccessibleFilterTopWindow* pAccTop = static_cast<ScAccessibleFilterTopWindow*>(mxAccessible.get());
1225 0 : fillMenuItemsToAccessible(pAccTop);
1226 :
1227 : pAccTop->setAccessibleChild(
1228 0 : maChecks.CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX);
1229 : pAccTop->setAccessibleChild(
1230 0 : maChkToggleAll.CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL);
1231 : pAccTop->setAccessibleChild(
1232 0 : maBtnSelectSingle.CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_ON_BTN);
1233 : pAccTop->setAccessibleChild(
1234 0 : maBtnUnselectSingle.CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_OFF_BTN);
1235 : pAccTop->setAccessibleChild(
1236 0 : maBtnOk.CreateAccessible(), ScAccessibleFilterTopWindow::OK_BTN);
1237 : pAccTop->setAccessibleChild(
1238 0 : maBtnCancel.CreateAccessible(), ScAccessibleFilterTopWindow::CANCEL_BTN);
1239 : }
1240 :
1241 0 : return mxAccessible;
1242 : }
1243 :
1244 0 : void ScCheckListMenuWindow::setMemberSize(size_t n)
1245 : {
1246 0 : maMembers.reserve(n);
1247 0 : }
1248 :
1249 0 : void ScCheckListMenuWindow::addDateMember(const OUString& rsName, double nVal, bool bVisible)
1250 : {
1251 0 : ScDocument* pDoc = getDoc();
1252 0 : SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
1253 :
1254 : // Convert the numeric date value to a date object.
1255 0 : Date aDate = *(pFormatter->GetNullDate());
1256 0 : aDate += static_cast<long>(rtl::math::approxFloor(nVal));
1257 :
1258 0 : sal_uInt16 nYear = aDate.GetYear();
1259 0 : sal_uInt16 nMonth = aDate.GetMonth();
1260 0 : sal_uInt16 nDay = aDate.GetDay();
1261 :
1262 : // Get the localized month name list.
1263 0 : CalendarWrapper* pCalendar = ScGlobal::GetCalendar();
1264 0 : uno::Sequence<i18n::CalendarItem2> aMonths = pCalendar->getMonths();
1265 0 : if (aMonths.getLength() < nMonth)
1266 0 : return;
1267 :
1268 0 : OUString aYearName = OUString::number(nYear);
1269 0 : OUString aMonthName = aMonths[nMonth-1].FullName;
1270 0 : OUString aDayName = OUString::number(nDay);
1271 :
1272 0 : maChecks.SetUpdateMode(false);
1273 :
1274 0 : SvTreeListEntry* pYearEntry = maChecks.FindEntry(NULL, aYearName);
1275 0 : if (!pYearEntry)
1276 : {
1277 0 : pYearEntry = maChecks.InsertEntry(aYearName, NULL, true);
1278 0 : Member aMemYear;
1279 0 : aMemYear.maName = aYearName;
1280 0 : aMemYear.maRealName = rsName;
1281 0 : aMemYear.mbDate = true;
1282 0 : aMemYear.mbLeaf = false;
1283 0 : aMemYear.mbVisible = bVisible;
1284 0 : aMemYear.mpParent = NULL;
1285 0 : maMembers.push_back(aMemYear);
1286 : }
1287 :
1288 0 : SvTreeListEntry* pMonthEntry = maChecks.FindEntry(pYearEntry, aMonthName);
1289 0 : if (!pMonthEntry)
1290 : {
1291 0 : pMonthEntry = maChecks.InsertEntry(aMonthName, pYearEntry, true);
1292 0 : Member aMemMonth;
1293 0 : aMemMonth.maName = aMonthName;
1294 0 : aMemMonth.maRealName = rsName;
1295 0 : aMemMonth.mbDate = true;
1296 0 : aMemMonth.mbLeaf = false;
1297 0 : aMemMonth.mbVisible = bVisible;
1298 0 : aMemMonth.mpParent = pYearEntry;
1299 0 : maMembers.push_back(aMemMonth);
1300 : }
1301 :
1302 0 : SvTreeListEntry* pDayEntry = maChecks.FindEntry(pMonthEntry, aDayName);
1303 0 : if (!pDayEntry)
1304 : {
1305 0 : maChecks.InsertEntry(aDayName, pMonthEntry, false);
1306 0 : Member aMemDay;
1307 0 : aMemDay.maName = aDayName;
1308 0 : aMemDay.maRealName = rsName;
1309 0 : aMemDay.mbDate = true;
1310 0 : aMemDay.mbLeaf = true;
1311 0 : aMemDay.mbVisible = bVisible;
1312 0 : aMemDay.mpParent = pMonthEntry;
1313 0 : maMembers.push_back(aMemDay);
1314 : }
1315 :
1316 0 : maChecks.SetUpdateMode(true);
1317 : }
1318 :
1319 0 : void ScCheckListMenuWindow::addMember(const OUString& rName, bool bVisible)
1320 : {
1321 0 : Member aMember;
1322 0 : aMember.maName = rName;
1323 0 : aMember.mbDate = false;
1324 0 : aMember.mbLeaf = true;
1325 0 : aMember.mbVisible = bVisible;
1326 0 : aMember.mpParent = NULL;
1327 0 : maMembers.push_back(aMember);
1328 0 : }
1329 :
1330 0 : ScCheckListBox::ScCheckListBox( vcl::Window* pParent, WinBits nWinStyle )
1331 0 : : SvTreeListBox( pParent, nWinStyle ), mpCheckButton( NULL )
1332 : {
1333 0 : Init();
1334 0 : }
1335 :
1336 0 : SvTreeListEntry* ScCheckListBox::FindEntry( SvTreeListEntry* pParent, const OUString& sNode )
1337 : {
1338 0 : sal_uInt16 nRootPos = 0;
1339 0 : SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : GetEntry( nRootPos );
1340 0 : while ( pEntry )
1341 : {
1342 0 : if ( sNode.equals(GetEntryText( pEntry )) )
1343 0 : return pEntry;
1344 :
1345 0 : pEntry = pParent ? NextSibling( pEntry ) : GetEntry( ++nRootPos );
1346 : }
1347 0 : return NULL;
1348 : }
1349 :
1350 0 : void ScCheckListBox::Init()
1351 : {
1352 0 : mpCheckButton = new SvLBoxButtonData( this );
1353 0 : EnableCheckButton( mpCheckButton );
1354 0 : SetNodeDefaultImages();
1355 0 : }
1356 :
1357 0 : bool ScCheckListBox::IsChecked( const OUString& sName, SvTreeListEntry* pParent )
1358 : {
1359 0 : SvTreeListEntry* pEntry = FindEntry( pParent, sName );
1360 0 : if ( pEntry && GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED)
1361 0 : return true;
1362 0 : return false;
1363 : }
1364 :
1365 0 : void ScCheckListBox::CheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bCheck )
1366 : {
1367 0 : SvTreeListEntry* pEntry = FindEntry( pParent, sName );
1368 0 : if ( pEntry )
1369 0 : CheckEntry( pEntry, bCheck );
1370 0 : }
1371 :
1372 : // Recursively check all children of pParent
1373 0 : void ScCheckListBox::CheckAllChildren( SvTreeListEntry* pParent, bool bCheck )
1374 : {
1375 0 : if ( pParent )
1376 : {
1377 : SetCheckButtonState(
1378 : pParent, bCheck ? SvButtonState( SV_BUTTON_CHECKED ) :
1379 0 : SvButtonState( SV_BUTTON_UNCHECKED ) );
1380 : }
1381 0 : SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1382 0 : while ( pEntry )
1383 : {
1384 0 : CheckAllChildren( pEntry, bCheck );
1385 0 : pEntry = NextSibling( pEntry );
1386 : }
1387 0 : }
1388 :
1389 0 : void ScCheckListBox::CheckEntry( SvTreeListEntry* pParent, bool bCheck )
1390 : {
1391 : // recursively check all items below pParent
1392 0 : CheckAllChildren( pParent, bCheck );
1393 : // checking pParent can affect ancestors, e.g. if ancestor is unchecked and pParent is
1394 : // now checked then the ancestor needs to be checked also
1395 0 : SvTreeListEntry* pAncestor = GetParent(pParent);
1396 0 : if ( pAncestor )
1397 : {
1398 0 : while ( pAncestor )
1399 : {
1400 : // if any first level children checked then ancestor
1401 : // needs to be checked, similarly if no first level children
1402 : // checked then ancestor needs to be unchecked
1403 0 : SvTreeListEntry* pChild = FirstChild( pAncestor );
1404 0 : bool bChildChecked = false;
1405 :
1406 0 : while ( pChild )
1407 : {
1408 0 : if ( GetCheckButtonState( pChild ) == SV_BUTTON_CHECKED )
1409 : {
1410 0 : bChildChecked = true;
1411 0 : break;
1412 : }
1413 0 : pChild = NextSibling( pChild );
1414 : }
1415 : SetCheckButtonState(
1416 : pAncestor, bChildChecked ? SvButtonState( SV_BUTTON_CHECKED ) :
1417 0 : SvButtonState( SV_BUTTON_UNCHECKED ) );
1418 0 : pAncestor = GetParent(pAncestor);
1419 : }
1420 : }
1421 0 : }
1422 :
1423 0 : SvTreeListEntry* ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
1424 : {
1425 0 : if ( pParent && GetCheckButtonState( pParent ) == SV_BUTTON_CHECKED )
1426 0 : nCount++;
1427 : // Iterate over the children
1428 0 : SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1429 0 : while ( pEntry )
1430 : {
1431 0 : CountCheckedEntries( pEntry, nCount );
1432 0 : pEntry = NextSibling( pEntry );
1433 : }
1434 0 : return NULL;
1435 : }
1436 :
1437 0 : sal_uInt16 ScCheckListBox::GetCheckedEntryCount() const
1438 : {
1439 0 : sal_uLong nCount = 0;
1440 0 : CountCheckedEntries( NULL, nCount );
1441 0 : return nCount;
1442 : }
1443 :
1444 0 : void ScCheckListBox::ExpandChildren( SvTreeListEntry* pParent )
1445 : {
1446 0 : if ( pParent )
1447 0 : Expand( pParent );
1448 : // Iterate over the children
1449 0 : SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
1450 0 : while ( pEntry )
1451 : {
1452 0 : ExpandChildren( pEntry );
1453 0 : pEntry = NextSibling( pEntry );
1454 : }
1455 0 : }
1456 :
1457 0 : void ScCheckListBox::KeyInput( const KeyEvent& rKEvt )
1458 : {
1459 0 : const vcl::KeyCode& rKey = rKEvt.GetKeyCode();
1460 :
1461 0 : if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
1462 : {
1463 0 : SvTreeListEntry* pEntry = GetCurEntry();
1464 :
1465 0 : if ( pEntry )
1466 : {
1467 0 : bool bCheck = ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED );
1468 0 : CheckEntry( pEntry, !bCheck );
1469 0 : if ( bCheck != ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) )
1470 0 : CheckButtonHdl();
1471 : }
1472 : }
1473 0 : else if ( GetEntryCount() )
1474 0 : SvTreeListBox::KeyInput( rKEvt );
1475 0 : }
1476 :
1477 0 : void ScCheckListMenuWindow::initMembers()
1478 : {
1479 0 : size_t n = maMembers.size();
1480 0 : size_t nVisMemCount = 0;
1481 :
1482 0 : maChecks.SetUpdateMode(false);
1483 0 : maChecks.GetModel()->EnableInvalidate(false);
1484 :
1485 0 : for (size_t i = 0; i < n; ++i)
1486 : {
1487 0 : if (maMembers[i].mbDate)
1488 : {
1489 0 : maChecks.CheckEntry(maMembers[i].maName, maMembers[i].mpParent, maMembers[i].mbVisible);
1490 : // Expand first node of checked dates
1491 0 : if (!maMembers[i].mpParent && maChecks.IsChecked(maMembers[i].maName, maMembers[i].mpParent))
1492 : {
1493 0 : SvTreeListEntry* pEntry = maChecks.FindEntry(NULL, maMembers[i].maName);
1494 0 : if (pEntry)
1495 0 : maChecks.Expand(pEntry);
1496 : }
1497 : }
1498 : else
1499 : {
1500 : SvTreeListEntry* pEntry = maChecks.InsertEntry(
1501 0 : maMembers[i].maName, NULL, false, TREELIST_APPEND, NULL,
1502 0 : SvLBoxButtonKind_enabledCheckbox);
1503 :
1504 : maChecks.SetCheckButtonState(
1505 0 : pEntry, maMembers[i].mbVisible ? SV_BUTTON_CHECKED : SV_BUTTON_UNCHECKED);
1506 : }
1507 :
1508 0 : if (maMembers[i].mbVisible)
1509 0 : ++nVisMemCount;
1510 : }
1511 0 : if (nVisMemCount == n)
1512 : {
1513 : // all members visible
1514 0 : maChkToggleAll.SetState(TRISTATE_TRUE);
1515 0 : mePrevToggleAllState = TRISTATE_TRUE;
1516 : }
1517 0 : else if (nVisMemCount == 0)
1518 : {
1519 : // no members visible
1520 0 : maChkToggleAll.SetState(TRISTATE_FALSE);
1521 0 : mePrevToggleAllState = TRISTATE_FALSE;
1522 : }
1523 : else
1524 : {
1525 0 : maChkToggleAll.SetState(TRISTATE_INDET);
1526 0 : mePrevToggleAllState = TRISTATE_INDET;
1527 : }
1528 :
1529 0 : maChecks.GetModel()->EnableInvalidate(true);
1530 0 : maChecks.SetUpdateMode(true);
1531 0 : }
1532 :
1533 0 : void ScCheckListMenuWindow::setConfig(const Config& rConfig)
1534 : {
1535 0 : maConfig = rConfig;
1536 0 : }
1537 :
1538 0 : bool ScCheckListMenuWindow::isAllSelected() const
1539 : {
1540 0 : return maChkToggleAll.IsChecked();
1541 : }
1542 :
1543 0 : void ScCheckListMenuWindow::getResult(ResultType& rResult)
1544 : {
1545 0 : ResultType aResult;
1546 0 : size_t n = maMembers.size();
1547 0 : for (size_t i = 0; i < n; ++i)
1548 : {
1549 0 : if ( maMembers[i].mbLeaf )
1550 : {
1551 0 : bool bState = maChecks.IsChecked( maMembers[i].maName, maMembers[i].mpParent );
1552 0 : OUString sName;
1553 0 : if ( maMembers[i].mbDate )
1554 0 : sName = maMembers[i].maRealName;
1555 : else
1556 0 : sName = maMembers[i].maName;
1557 0 : aResult.insert(ResultType::value_type(sName, bState));
1558 : }
1559 : }
1560 0 : rResult.swap(aResult);
1561 0 : }
1562 :
1563 0 : void ScCheckListMenuWindow::launch(const Rectangle& rRect)
1564 : {
1565 0 : packWindow();
1566 0 : if (!maConfig.mbAllowEmptySet)
1567 : // We need to have at least one member selected.
1568 0 : maBtnOk.Enable(maChecks.GetCheckedEntryCount() != 0);
1569 :
1570 0 : Rectangle aRect(rRect);
1571 0 : if (maConfig.mbRTL)
1572 : {
1573 : // In RTL mode, the logical "left" is visual "right".
1574 0 : long nLeft = aRect.Left() - aRect.GetWidth();
1575 0 : aRect.Left() = nLeft;
1576 : }
1577 0 : else if (maWndSize.Width() < aRect.GetWidth())
1578 : {
1579 : // Target rectangle (i.e. cell width) is wider than the window.
1580 : // Simulate right-aligned launch by modifying the target rectangle
1581 : // size.
1582 0 : long nDiff = aRect.GetWidth() - maWndSize.Width();
1583 0 : aRect.Left() += nDiff;
1584 : }
1585 :
1586 0 : StartPopupMode(aRect, (FLOATWIN_POPUPMODE_DOWN | FLOATWIN_POPUPMODE_GRABFOCUS));
1587 0 : cycleFocus(); // Set initial focus to the check list box.
1588 0 : }
1589 :
1590 0 : void ScCheckListMenuWindow::close(bool bOK)
1591 : {
1592 0 : if (bOK && mpOKAction.get())
1593 0 : mpOKAction->execute();
1594 :
1595 0 : EndPopupMode();
1596 0 : }
1597 :
1598 0 : void ScCheckListMenuWindow::setExtendedData(ExtendedData* p)
1599 : {
1600 0 : mpExtendedData.reset(p);
1601 0 : }
1602 :
1603 0 : ScCheckListMenuWindow::ExtendedData* ScCheckListMenuWindow::getExtendedData()
1604 : {
1605 0 : return mpExtendedData.get();
1606 : }
1607 :
1608 0 : void ScCheckListMenuWindow::setOKAction(Action* p)
1609 : {
1610 0 : mpOKAction.reset(p);
1611 0 : }
1612 :
1613 0 : void ScCheckListMenuWindow::setPopupEndAction(Action* p)
1614 : {
1615 0 : mpPopupEndAction.reset(p);
1616 0 : }
1617 :
1618 0 : void ScCheckListMenuWindow::handlePopupEnd()
1619 : {
1620 0 : clearSelectedMenuItem();
1621 0 : if (mpPopupEndAction)
1622 0 : mpPopupEndAction->execute();
1623 228 : }
1624 :
1625 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|