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