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