In the paints, we must use the size of the Window for the computations, not of the RenderContext - the RenderContext can be much bigger than the Window in the double-buffering case. Fixes for example the list boxes, and many others. Change-Id: I4c7607569f88b2d097587140858d0862e54b5ea6
1774 lines
54 KiB
C++
1774 lines
54 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include "checklistmenu.hxx"
|
|
#include "checklistmenu.hrc"
|
|
#include "strload.hxx"
|
|
#include "globstr.hrc"
|
|
|
|
#include <vcl/decoview.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <tools/wintypes.hxx>
|
|
|
|
#include "AccessibleFilterMenu.hxx"
|
|
#include "AccessibleFilterTopWindow.hxx"
|
|
|
|
#include <com/sun/star/accessibility/XAccessible.hpp>
|
|
#include <com/sun/star/accessibility/XAccessibleContext.hpp>
|
|
#include <svtools/fmtfield.hxx>
|
|
#include <svtools/treelistentry.hxx>
|
|
#include "document.hxx"
|
|
|
|
using namespace com::sun::star;
|
|
using ::com::sun::star::uno::Reference;
|
|
using ::com::sun::star::accessibility::XAccessible;
|
|
using ::com::sun::star::accessibility::XAccessibleContext;
|
|
using ::std::vector;
|
|
|
|
ScMenuFloatingWindow::MenuItemData::MenuItemData() :
|
|
mbEnabled(true), mbSeparator(false),
|
|
mpAction(static_cast<ScCheckListMenuWindow::Action*>(NULL)),
|
|
mpSubMenuWin(static_cast<ScMenuFloatingWindow*>(NULL))
|
|
{
|
|
}
|
|
|
|
ScMenuFloatingWindow::SubMenuItemData::SubMenuItemData(ScMenuFloatingWindow* pParent) :
|
|
mpSubMenu(NULL),
|
|
mnMenuPos(MENU_NOT_SELECTED),
|
|
mpParent(pParent)
|
|
{
|
|
maTimer.SetTimeoutHdl( LINK(this, ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl) );
|
|
maTimer.SetTimeout(mpParent->GetSettings().GetMouseSettings().GetMenuDelay());
|
|
}
|
|
|
|
void ScMenuFloatingWindow::SubMenuItemData::reset()
|
|
{
|
|
mpSubMenu = NULL;
|
|
mnMenuPos = MENU_NOT_SELECTED;
|
|
maTimer.Stop();
|
|
}
|
|
|
|
IMPL_LINK_NOARG_TYPED(ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl, Timer *, void)
|
|
{
|
|
mpParent->handleMenuTimeout(this);
|
|
}
|
|
|
|
size_t ScMenuFloatingWindow::MENU_NOT_SELECTED = 999;
|
|
|
|
ScMenuFloatingWindow::ScMenuFloatingWindow(vcl::Window* pParent, ScDocument* pDoc, sal_uInt16 nMenuStackLevel) :
|
|
PopupMenuFloatingWindow(pParent),
|
|
maOpenTimer(this),
|
|
maCloseTimer(this),
|
|
maName("ScMenuFloatingWindow"),
|
|
mnSelectedMenu(MENU_NOT_SELECTED),
|
|
mnClickedMenu(MENU_NOT_SELECTED),
|
|
mpDoc(pDoc),
|
|
mpParentMenu(dynamic_cast<ScMenuFloatingWindow*>(pParent))
|
|
{
|
|
SetMenuStackLevel(nMenuStackLevel);
|
|
SetText(OUString("ScMenuFloatingWindow"));
|
|
|
|
const StyleSettings& rStyle = GetSettings().GetStyleSettings();
|
|
|
|
sal_Int32 nScaleFactor = GetDPIScaleFactor();
|
|
const sal_uInt16 nPopupFontHeight = 12 * nScaleFactor;
|
|
maLabelFont = rStyle.GetLabelFont();
|
|
maLabelFont.SetHeight(nPopupFontHeight);
|
|
}
|
|
|
|
ScMenuFloatingWindow::~ScMenuFloatingWindow()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::dispose()
|
|
{
|
|
EndPopupMode();
|
|
mpParentMenu.clear();
|
|
PopupMenuFloatingWindow::dispose();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::PopupModeEnd()
|
|
{
|
|
handlePopupEnd();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::MouseMove(const MouseEvent& rMEvt)
|
|
{
|
|
const Point& rPos = rMEvt.GetPosPixel();
|
|
size_t nSelectedMenu = getEnclosingMenuItem(rPos);
|
|
setSelectedMenuItem(nSelectedMenu, true, false);
|
|
|
|
Window::MouseMove(rMEvt);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::MouseButtonDown(const MouseEvent& rMEvt)
|
|
{
|
|
const Point& rPos = rMEvt.GetPosPixel();
|
|
mnClickedMenu = getEnclosingMenuItem(rPos);
|
|
Window::MouseButtonDown(rMEvt);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::MouseButtonUp(const MouseEvent& rMEvt)
|
|
{
|
|
executeMenuItem(mnClickedMenu);
|
|
mnClickedMenu = MENU_NOT_SELECTED;
|
|
Window::MouseButtonUp(rMEvt);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::KeyInput(const KeyEvent& rKEvt)
|
|
{
|
|
if (maMenuItems.empty())
|
|
{
|
|
Window::KeyInput(rKEvt);
|
|
return;
|
|
}
|
|
|
|
const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
|
|
bool bHandled = true;
|
|
size_t nSelectedMenu = mnSelectedMenu;
|
|
size_t nLastMenuPos = maMenuItems.size() - 1;
|
|
switch (rKeyCode.GetCode())
|
|
{
|
|
case KEY_UP:
|
|
{
|
|
if (nLastMenuPos == 0)
|
|
// There is only one menu item. Do nothing.
|
|
break;
|
|
|
|
size_t nOldPos = nSelectedMenu;
|
|
|
|
if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == 0)
|
|
nSelectedMenu = nLastMenuPos;
|
|
else
|
|
--nSelectedMenu;
|
|
|
|
// Loop until a non-separator menu item is found.
|
|
while (nSelectedMenu != nOldPos)
|
|
{
|
|
if (maMenuItems[nSelectedMenu].mbSeparator)
|
|
{
|
|
if (nSelectedMenu)
|
|
--nSelectedMenu;
|
|
else
|
|
nSelectedMenu = nLastMenuPos;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
setSelectedMenuItem(nSelectedMenu, false, false);
|
|
}
|
|
break;
|
|
case KEY_DOWN:
|
|
{
|
|
if (nLastMenuPos == 0)
|
|
// There is only one menu item. Do nothing.
|
|
break;
|
|
|
|
size_t nOldPos = nSelectedMenu;
|
|
|
|
if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == nLastMenuPos)
|
|
nSelectedMenu = 0;
|
|
else
|
|
++nSelectedMenu;
|
|
|
|
// Loop until a non-separator menu item is found.
|
|
while (nSelectedMenu != nOldPos)
|
|
{
|
|
if (maMenuItems[nSelectedMenu].mbSeparator)
|
|
{
|
|
if (nSelectedMenu == nLastMenuPos)
|
|
nSelectedMenu = 0;
|
|
else
|
|
++nSelectedMenu;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
setSelectedMenuItem(nSelectedMenu, false, false);
|
|
}
|
|
break;
|
|
case KEY_LEFT:
|
|
if (mpParentMenu)
|
|
mpParentMenu->endSubMenu(this);
|
|
break;
|
|
case KEY_RIGHT:
|
|
{
|
|
if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
|
|
break;
|
|
|
|
const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
|
|
if (!rMenu.mbEnabled || !rMenu.mpSubMenuWin)
|
|
break;
|
|
|
|
maOpenTimer.mnMenuPos = mnSelectedMenu;
|
|
maOpenTimer.mpSubMenu = rMenu.mpSubMenuWin.get();
|
|
launchSubMenu(true);
|
|
}
|
|
break;
|
|
case KEY_RETURN:
|
|
if (nSelectedMenu != MENU_NOT_SELECTED)
|
|
executeMenuItem(nSelectedMenu);
|
|
break;
|
|
default:
|
|
bHandled = false;
|
|
}
|
|
|
|
if (!bHandled)
|
|
Window::KeyInput(rKEvt);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::Paint(vcl::RenderContext& rRenderContext, const Rectangle& /*rRect*/)
|
|
{
|
|
const StyleSettings& rStyle = GetSettings().GetStyleSettings();
|
|
|
|
SetFont(maLabelFont);
|
|
|
|
Color aBackColor = rStyle.GetMenuColor();
|
|
Color aBorderColor = rStyle.GetShadowColor();
|
|
|
|
Rectangle aCtrlRect(Point(0, 0), GetOutputSizePixel());
|
|
|
|
// Window background
|
|
bool bNativeDrawn = true;
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
|
|
{
|
|
rRenderContext.SetClipRegion();
|
|
bNativeDrawn = rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect,
|
|
ControlState::ENABLED, ImplControlValue(), OUString());
|
|
}
|
|
else
|
|
bNativeDrawn = false;
|
|
|
|
if (!bNativeDrawn)
|
|
{
|
|
rRenderContext.SetFillColor(aBackColor);
|
|
rRenderContext.SetLineColor(aBorderColor);
|
|
rRenderContext.DrawRect(aCtrlRect);
|
|
}
|
|
|
|
// Menu items
|
|
rRenderContext.SetTextColor(rStyle.GetMenuTextColor());
|
|
drawAllMenuItems(rRenderContext);
|
|
}
|
|
|
|
Reference<XAccessible> ScMenuFloatingWindow::CreateAccessible()
|
|
{
|
|
if (!mxAccessible.is())
|
|
{
|
|
Reference<XAccessible> xAccParent = mpParentMenu ?
|
|
mpParentMenu->GetAccessible() : GetAccessibleParentWindow()->GetAccessible();
|
|
|
|
mxAccessible.set(new ScAccessibleFilterMenu(xAccParent, this, maName, 999));
|
|
ScAccessibleFilterMenu* p = static_cast<ScAccessibleFilterMenu*>(
|
|
mxAccessible.get());
|
|
|
|
vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
|
|
for (itr = itrBeg; itr != itrEnd; ++itr)
|
|
{
|
|
size_t nPos = ::std::distance(itrBeg, itr);
|
|
p->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
|
|
}
|
|
}
|
|
|
|
return mxAccessible;
|
|
}
|
|
|
|
void ScMenuFloatingWindow::addMenuItem(const OUString& rText, bool bEnabled, Action* pAction)
|
|
{
|
|
MenuItemData aItem;
|
|
aItem.maText = rText;
|
|
aItem.mbEnabled = bEnabled;
|
|
aItem.mpAction.reset(pAction);
|
|
maMenuItems.push_back(aItem);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::addSeparator()
|
|
{
|
|
MenuItemData aItem;
|
|
aItem.mbSeparator = true;
|
|
maMenuItems.push_back(aItem);
|
|
}
|
|
|
|
ScMenuFloatingWindow* ScMenuFloatingWindow::addSubMenuItem(const OUString& rText, bool bEnabled)
|
|
{
|
|
MenuItemData aItem;
|
|
aItem.maText = rText;
|
|
aItem.mbEnabled = bEnabled;
|
|
aItem.mpSubMenuWin.reset(VclPtr<ScMenuFloatingWindow>::Create(this, mpDoc, GetMenuStackLevel()+1));
|
|
aItem.mpSubMenuWin->setName(rText);
|
|
maMenuItems.push_back(aItem);
|
|
return aItem.mpSubMenuWin.get();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::handlePopupEnd()
|
|
{
|
|
clearSelectedMenuItem();
|
|
}
|
|
|
|
Size ScMenuFloatingWindow::getMenuSize() const
|
|
{
|
|
if (maMenuItems.empty())
|
|
return Size();
|
|
|
|
vector<MenuItemData>::const_iterator itr = maMenuItems.begin(), itrEnd = maMenuItems.end();
|
|
long nTextWidth = 0;
|
|
for (; itr != itrEnd; ++itr)
|
|
{
|
|
if (itr->mbSeparator)
|
|
continue;
|
|
|
|
nTextWidth = ::std::max(GetTextWidth(itr->maText), nTextWidth);
|
|
}
|
|
|
|
size_t nLastPos = maMenuItems.size()-1;
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(nLastPos, aPos, aSize);
|
|
aPos.X() += nTextWidth + 15;
|
|
aPos.Y() += aSize.Height() + 5;
|
|
return Size(aPos.X(), aPos.Y());
|
|
}
|
|
|
|
void ScMenuFloatingWindow::drawMenuItem(vcl::RenderContext& rRenderContext, size_t nPos)
|
|
{
|
|
if (nPos >= maMenuItems.size())
|
|
return;
|
|
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(nPos, aPos, aSize);
|
|
|
|
DecorationView aDecoView(&rRenderContext);
|
|
long nXOffset = 5;
|
|
long nYOffset = (aSize.Height() - maLabelFont.GetHeight())/2;
|
|
rRenderContext. DrawCtrlText(Point(aPos.X()+nXOffset, aPos.Y() + nYOffset), maMenuItems[nPos].maText, 0,
|
|
maMenuItems[nPos].maText.getLength(),
|
|
maMenuItems[nPos].mbEnabled ? DrawTextFlags::Mnemonic : DrawTextFlags::Disable);
|
|
|
|
if (maMenuItems[nPos].mpSubMenuWin)
|
|
{
|
|
long nFontHeight = maLabelFont.GetHeight();
|
|
Point aMarkerPos = aPos;
|
|
aMarkerPos.Y() += aSize.Height() / 2 - nFontHeight / 4 + 1;
|
|
aMarkerPos.X() += aSize.Width() - nFontHeight + nFontHeight / 4;
|
|
Size aMarkerSize(nFontHeight / 2, nFontHeight / 2);
|
|
aDecoView.DrawSymbol(Rectangle(aMarkerPos, aMarkerSize), SymbolType::SPIN_RIGHT, GetTextColor());
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::drawSeparator(vcl::RenderContext& rRenderContext, size_t nPos)
|
|
{
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(nPos, aPos, aSize);
|
|
Rectangle aRegion(aPos,aSize);
|
|
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
|
|
{
|
|
rRenderContext.Push(PushFlags::CLIPREGION);
|
|
rRenderContext.IntersectClipRegion(aRegion);
|
|
Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
|
|
rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect,
|
|
ControlState::ENABLED, ImplControlValue(), OUString());
|
|
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
bool bNativeDrawn = false;
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_SEPARATOR))
|
|
{
|
|
ControlState nState = ControlState::NONE;
|
|
const MenuItemData& rData = maMenuItems[nPos];
|
|
if (rData.mbEnabled)
|
|
nState |= ControlState::ENABLED;
|
|
|
|
bNativeDrawn = rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_MENU_SEPARATOR,
|
|
aRegion, nState, ImplControlValue(), OUString());
|
|
}
|
|
|
|
if (!bNativeDrawn)
|
|
{
|
|
const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
|
|
Point aTmpPos = aPos;
|
|
aTmpPos.Y() += aSize.Height() / 2;
|
|
rRenderContext.SetLineColor(rStyle.GetShadowColor());
|
|
rRenderContext.DrawLine(aTmpPos, Point(aSize.Width() + aTmpPos.X(), aTmpPos.Y()));
|
|
++aTmpPos.Y();
|
|
rRenderContext.SetLineColor(rStyle.GetLightColor());
|
|
rRenderContext.DrawLine(aTmpPos, Point(aSize.Width() + aTmpPos.X(), aTmpPos.Y()));
|
|
rRenderContext.SetLineColor();
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::drawAllMenuItems(vcl::RenderContext& rRenderContext)
|
|
{
|
|
size_t n = maMenuItems.size();
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (maMenuItems[i].mbSeparator)
|
|
{
|
|
// Separator
|
|
drawSeparator(rRenderContext, i);
|
|
}
|
|
else
|
|
{
|
|
// Normal menu item
|
|
highlightMenuItem(rRenderContext, i, i == mnSelectedMenu);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::executeMenuItem(size_t nPos)
|
|
{
|
|
if (nPos >= maMenuItems.size())
|
|
return;
|
|
|
|
if (!maMenuItems[nPos].mpAction)
|
|
// no action is defined.
|
|
return;
|
|
|
|
maMenuItems[nPos].mpAction->execute();
|
|
terminateAllPopupMenus();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu)
|
|
{
|
|
if (mnSelectedMenu == nPos)
|
|
// nothing to do.
|
|
return;
|
|
|
|
if (bEnsureSubMenu)
|
|
{
|
|
// Dismiss any child popup menu windows.
|
|
if (mnSelectedMenu < maMenuItems.size() &&
|
|
maMenuItems[mnSelectedMenu].mpSubMenuWin &&
|
|
maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
|
|
{
|
|
maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
|
|
}
|
|
|
|
// The popup is not visible, yet a menu item is selected. The request
|
|
// most likely comes from the accessible object. Make sure this
|
|
// window, as well as all its parent windows are visible.
|
|
if (!IsVisible() && mpParentMenu)
|
|
mpParentMenu->ensureSubMenuVisible(this);
|
|
}
|
|
|
|
selectMenuItem(mnSelectedMenu, false, bSubMenuTimer);
|
|
selectMenuItem(nPos, true, bSubMenuTimer);
|
|
mnSelectedMenu = nPos;
|
|
|
|
fireMenuHighlightedEvent();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::handleMenuTimeout(SubMenuItemData* pTimer)
|
|
{
|
|
if (pTimer == &maOpenTimer)
|
|
{
|
|
// Close any open submenu immediately.
|
|
if (maCloseTimer.mpSubMenu)
|
|
{
|
|
maCloseTimer.mpSubMenu->EndPopupMode();
|
|
maCloseTimer.mpSubMenu = NULL;
|
|
maCloseTimer.maTimer.Stop();
|
|
}
|
|
|
|
launchSubMenu(false);
|
|
}
|
|
else if (pTimer == &maCloseTimer)
|
|
{
|
|
// end submenu.
|
|
if (maCloseTimer.mpSubMenu)
|
|
{
|
|
maOpenTimer.mpSubMenu = NULL;
|
|
|
|
maCloseTimer.mpSubMenu->EndPopupMode();
|
|
maCloseTimer.mpSubMenu = NULL;
|
|
|
|
Invalidate();
|
|
maOpenTimer.mnMenuPos = MENU_NOT_SELECTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::queueLaunchSubMenu(size_t nPos, ScMenuFloatingWindow* pMenu)
|
|
{
|
|
if (!pMenu)
|
|
return;
|
|
|
|
// Set the submenu on launch queue.
|
|
if (maOpenTimer.mpSubMenu)
|
|
{
|
|
if (maOpenTimer.mpSubMenu == pMenu)
|
|
{
|
|
if (pMenu == maCloseTimer.mpSubMenu)
|
|
maCloseTimer.reset();
|
|
return;
|
|
}
|
|
|
|
// new submenu is being requested.
|
|
queueCloseSubMenu();
|
|
}
|
|
|
|
maOpenTimer.mpSubMenu = pMenu;
|
|
maOpenTimer.mnMenuPos = nPos;
|
|
maOpenTimer.maTimer.Start();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::queueCloseSubMenu()
|
|
{
|
|
if (!maOpenTimer.mpSubMenu)
|
|
// There is no submenu to close.
|
|
return;
|
|
|
|
// Stop any submenu on queue for opening.
|
|
maOpenTimer.maTimer.Stop();
|
|
|
|
maCloseTimer.mpSubMenu = maOpenTimer.mpSubMenu;
|
|
maCloseTimer.mnMenuPos = maOpenTimer.mnMenuPos;
|
|
maCloseTimer.maTimer.Start();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::launchSubMenu(bool bSetMenuPos)
|
|
{
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(maOpenTimer.mnMenuPos, aPos, aSize);
|
|
ScMenuFloatingWindow* pSubMenu = maOpenTimer.mpSubMenu;
|
|
|
|
if (!pSubMenu)
|
|
return;
|
|
|
|
FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
|
|
SetPopupModeFlags(nOldFlags | FloatWinPopupFlags::NoAppFocusClose);
|
|
pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
|
|
pSubMenu->StartPopupMode(
|
|
Rectangle(aPos,aSize), (FloatWinPopupFlags::Right | FloatWinPopupFlags::GrabFocus));
|
|
pSubMenu->AddPopupModeWindow(this);
|
|
if (bSetMenuPos)
|
|
pSubMenu->setSelectedMenuItem(0, false, false); // select menu item after the popup becomes fully visible.
|
|
SetPopupModeFlags(nOldFlags);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::endSubMenu(ScMenuFloatingWindow* pSubMenu)
|
|
{
|
|
if (!pSubMenu)
|
|
return;
|
|
|
|
pSubMenu->EndPopupMode();
|
|
maOpenTimer.reset();
|
|
|
|
size_t nMenuPos = getSubMenuPos(pSubMenu);
|
|
if (nMenuPos != MENU_NOT_SELECTED)
|
|
{
|
|
mnSelectedMenu = nMenuPos;
|
|
Invalidate();
|
|
fireMenuHighlightedEvent();
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::fillMenuItemsToAccessible(ScAccessibleFilterMenu* pAccMenu) const
|
|
{
|
|
vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
|
|
for (itr = itrBeg; itr != itrEnd; ++itr)
|
|
{
|
|
size_t nPos = ::std::distance(itrBeg, itr);
|
|
pAccMenu->appendMenuItem(itr->maText, itr->mbEnabled, nPos);
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::resizeToFitMenuItems()
|
|
{
|
|
SetOutputSizePixel(getMenuSize());
|
|
}
|
|
|
|
void ScMenuFloatingWindow::selectMenuItem(size_t nPos, bool bSelected, bool bSubMenuTimer)
|
|
{
|
|
if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
|
|
{
|
|
queueCloseSubMenu();
|
|
return;
|
|
}
|
|
|
|
if (!maMenuItems[nPos].mbEnabled)
|
|
{
|
|
queueCloseSubMenu();
|
|
return;
|
|
}
|
|
|
|
Invalidate();
|
|
|
|
if (bSelected)
|
|
{
|
|
if (mpParentMenu)
|
|
mpParentMenu->setSubMenuFocused(this);
|
|
|
|
if (bSubMenuTimer)
|
|
{
|
|
if (maMenuItems[nPos].mpSubMenuWin)
|
|
{
|
|
ScMenuFloatingWindow* pSubMenu = maMenuItems[nPos].mpSubMenuWin.get();
|
|
queueLaunchSubMenu(nPos, pSubMenu);
|
|
}
|
|
else
|
|
queueCloseSubMenu();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::clearSelectedMenuItem()
|
|
{
|
|
selectMenuItem(mnSelectedMenu, false, false);
|
|
mnSelectedMenu = MENU_NOT_SELECTED;
|
|
}
|
|
|
|
ScMenuFloatingWindow* ScMenuFloatingWindow::getSubMenuWindow(size_t nPos) const
|
|
{
|
|
if (maMenuItems.size() <= nPos)
|
|
return NULL;
|
|
|
|
return maMenuItems[nPos].mpSubMenuWin.get();
|
|
}
|
|
|
|
bool ScMenuFloatingWindow::isMenuItemSelected(size_t nPos) const
|
|
{
|
|
return nPos == mnSelectedMenu;
|
|
}
|
|
|
|
void ScMenuFloatingWindow::setName(const OUString& rName)
|
|
{
|
|
maName = rName;
|
|
}
|
|
|
|
void ScMenuFloatingWindow::highlightMenuItem(vcl::RenderContext& rRenderContext, size_t nPos, bool bSelected)
|
|
{
|
|
if (nPos == MENU_NOT_SELECTED)
|
|
return;
|
|
|
|
const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
|
|
Color aBackColor = rStyle.GetMenuColor();
|
|
rRenderContext.SetFillColor(aBackColor);
|
|
rRenderContext.SetLineColor(aBackColor);
|
|
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(nPos, aPos, aSize);
|
|
Rectangle aRegion(aPos,aSize);
|
|
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
|
|
{
|
|
rRenderContext.Push(PushFlags::CLIPREGION);
|
|
rRenderContext.IntersectClipRegion(Rectangle(aPos, aSize));
|
|
Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
|
|
rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect, ControlState::ENABLED,
|
|
ImplControlValue(), OUString());
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
bool bNativeDrawn = true;
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM))
|
|
{
|
|
ControlState nState = bSelected ? ControlState::SELECTED : ControlState::NONE;
|
|
if (maMenuItems[nPos].mbEnabled)
|
|
nState |= ControlState::ENABLED;
|
|
bNativeDrawn = rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_MENU_ITEM,
|
|
aRegion, nState, ImplControlValue(), OUString());
|
|
}
|
|
else
|
|
bNativeDrawn = false;
|
|
|
|
if (!bNativeDrawn)
|
|
{
|
|
if (bSelected)
|
|
{
|
|
aBackColor = rStyle.GetMenuHighlightColor();
|
|
rRenderContext.SetFillColor(aBackColor);
|
|
rRenderContext.SetLineColor(aBackColor);
|
|
}
|
|
rRenderContext.DrawRect(Rectangle(aPos,aSize));
|
|
}
|
|
|
|
Color aTextColor = bSelected ? rStyle.GetMenuHighlightTextColor() : rStyle.GetMenuTextColor();
|
|
rRenderContext.SetTextColor(aTextColor);
|
|
drawMenuItem(rRenderContext, nPos);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::getMenuItemPosSize(size_t nPos, Point& rPos, Size& rSize) const
|
|
{
|
|
size_t nCount = maMenuItems.size();
|
|
if (nPos >= nCount)
|
|
return;
|
|
|
|
const sal_uInt16 nLeftMargin = 5;
|
|
const sal_uInt16 nTopMargin = 5;
|
|
const sal_uInt16 nMenuItemHeight = static_cast<sal_uInt16>(maLabelFont.GetHeight()*1.8);
|
|
const sal_uInt16 nSepHeight = static_cast<sal_uInt16>(maLabelFont.GetHeight()*0.8);
|
|
|
|
Point aPos1(nLeftMargin, nTopMargin);
|
|
rPos = aPos1;
|
|
for (size_t i = 0; i < nPos; ++i)
|
|
rPos.Y() += maMenuItems[i].mbSeparator ? nSepHeight : nMenuItemHeight;
|
|
|
|
Size aWndSize = GetSizePixel();
|
|
sal_uInt16 nH = maMenuItems[nPos].mbSeparator ? nSepHeight : nMenuItemHeight;
|
|
rSize = Size(aWndSize.Width() - nLeftMargin*2, nH);
|
|
}
|
|
|
|
size_t ScMenuFloatingWindow::getEnclosingMenuItem(const Point& rPos) const
|
|
{
|
|
size_t n = maMenuItems.size();
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(i, aPos, aSize);
|
|
Rectangle aRect(aPos, aSize);
|
|
if (aRect.IsInside(rPos))
|
|
return maMenuItems[i].mbSeparator ? MENU_NOT_SELECTED : i;
|
|
}
|
|
return MENU_NOT_SELECTED;
|
|
}
|
|
|
|
size_t ScMenuFloatingWindow::getSubMenuPos(ScMenuFloatingWindow* pSubMenu)
|
|
{
|
|
size_t n = maMenuItems.size();
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (maMenuItems[i].mpSubMenuWin.get() == pSubMenu)
|
|
return i;
|
|
}
|
|
return MENU_NOT_SELECTED;
|
|
}
|
|
|
|
void ScMenuFloatingWindow::fireMenuHighlightedEvent()
|
|
{
|
|
if (mnSelectedMenu == MENU_NOT_SELECTED)
|
|
return;
|
|
|
|
if (!mxAccessible.is())
|
|
return;
|
|
|
|
Reference<XAccessibleContext> xAccCxt = mxAccessible->getAccessibleContext();
|
|
if (!xAccCxt.is())
|
|
return;
|
|
|
|
Reference<XAccessible> xAccMenu = xAccCxt->getAccessibleChild(mnSelectedMenu);
|
|
if (!xAccMenu.is())
|
|
return;
|
|
|
|
VclAccessibleEvent aEvent(VCLEVENT_MENU_HIGHLIGHT, xAccMenu);
|
|
FireVclEvent(&aEvent);
|
|
}
|
|
|
|
void ScMenuFloatingWindow::setSubMenuFocused(ScMenuFloatingWindow* pSubMenu)
|
|
{
|
|
maCloseTimer.reset();
|
|
size_t nMenuPos = getSubMenuPos(pSubMenu);
|
|
if (mnSelectedMenu != nMenuPos)
|
|
{
|
|
mnSelectedMenu = nMenuPos;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu)
|
|
{
|
|
if (mpParentMenu)
|
|
mpParentMenu->ensureSubMenuVisible(this);
|
|
|
|
if (pSubMenu->IsVisible())
|
|
return;
|
|
|
|
// Find the menu position of the submenu.
|
|
size_t nMenuPos = getSubMenuPos(pSubMenu);
|
|
if (nMenuPos != MENU_NOT_SELECTED)
|
|
{
|
|
setSelectedMenuItem(nMenuPos, false, false);
|
|
|
|
Point aPos;
|
|
Size aSize;
|
|
getMenuItemPosSize(nMenuPos, aPos, aSize);
|
|
|
|
FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
|
|
SetPopupModeFlags(nOldFlags | FloatWinPopupFlags::NoAppFocusClose);
|
|
pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
|
|
pSubMenu->StartPopupMode(
|
|
Rectangle(aPos,aSize), (FloatWinPopupFlags::Right | FloatWinPopupFlags::GrabFocus));
|
|
pSubMenu->AddPopupModeWindow(this);
|
|
SetPopupModeFlags(nOldFlags);
|
|
}
|
|
}
|
|
|
|
void ScMenuFloatingWindow::ensureSubMenuNotVisible()
|
|
{
|
|
if (mnSelectedMenu <= maMenuItems.size() &&
|
|
maMenuItems[mnSelectedMenu].mpSubMenuWin &&
|
|
maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
|
|
{
|
|
maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
|
|
}
|
|
|
|
EndPopupMode();
|
|
}
|
|
|
|
void ScMenuFloatingWindow::terminateAllPopupMenus()
|
|
{
|
|
EndPopupMode();
|
|
if (mpParentMenu)
|
|
mpParentMenu->terminateAllPopupMenus();
|
|
}
|
|
|
|
ScCheckListMenuWindow::Config::Config() :
|
|
mbAllowEmptySet(true), mbRTL(false)
|
|
{
|
|
}
|
|
|
|
ScCheckListMenuWindow::Member::Member()
|
|
: mbVisible(true)
|
|
, mbDate(false)
|
|
, mbLeaf(false)
|
|
, mpParent(NULL)
|
|
{
|
|
}
|
|
|
|
ScCheckListMenuWindow::CancelButton::CancelButton(ScCheckListMenuWindow* pParent) :
|
|
::CancelButton(pParent), mpParent(pParent) {}
|
|
|
|
ScCheckListMenuWindow::CancelButton::~CancelButton()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::CancelButton::dispose()
|
|
{
|
|
mpParent.clear();
|
|
::CancelButton::dispose();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::CancelButton::Click()
|
|
{
|
|
mpParent->EndPopupMode();
|
|
::CancelButton::Click();
|
|
}
|
|
|
|
ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* pDoc) :
|
|
ScMenuFloatingWindow(pParent, pDoc),
|
|
maEdSearch(VclPtr<Edit>::Create(this)),
|
|
maChecks(VclPtr<ScCheckListBox>::Create(this, WB_HASBUTTONS | WB_HASLINES | WB_HASLINESATROOT | WB_HASBUTTONSATROOT) ),
|
|
maChkToggleAll(VclPtr<TriStateBox>::Create(this, 0)),
|
|
maBtnSelectSingle(VclPtr<ImageButton>::Create(this, 0)),
|
|
maBtnUnselectSingle(VclPtr<ImageButton>::Create(this, 0)),
|
|
maBtnOk(VclPtr<OKButton>::Create(this)),
|
|
maBtnCancel(VclPtr<CancelButton>::Create(this)),
|
|
mnCurTabStop(0),
|
|
mpExtendedData(NULL),
|
|
mpOKAction(NULL),
|
|
mpPopupEndAction(NULL),
|
|
maWndSize(),
|
|
mePrevToggleAllState(TRISTATE_INDET)
|
|
{
|
|
sal_Int32 nScaleFactor = GetDPIScaleFactor();
|
|
|
|
maWndSize = Size(200 * nScaleFactor, 330 * nScaleFactor);
|
|
|
|
maTabStopCtrls.reserve(8);
|
|
maTabStopCtrls.push_back(this);
|
|
maTabStopCtrls.push_back(maEdSearch.get());
|
|
maTabStopCtrls.push_back(maChecks.get());
|
|
maTabStopCtrls.push_back(maChkToggleAll.get());
|
|
maTabStopCtrls.push_back(maBtnSelectSingle.get());
|
|
maTabStopCtrls.push_back(maBtnUnselectSingle.get());
|
|
maTabStopCtrls.push_back(maBtnOk.get());
|
|
maTabStopCtrls.push_back(maBtnCancel.get());
|
|
|
|
// Enable type-ahead search in the check list box.
|
|
maChecks->SetStyle(maChecks->GetStyle() | WB_QUICK_SEARCH);
|
|
}
|
|
|
|
ScCheckListMenuWindow::~ScCheckListMenuWindow()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::dispose()
|
|
{
|
|
maEdSearch.disposeAndClear();
|
|
maChecks.disposeAndClear();
|
|
maChkToggleAll.disposeAndClear();
|
|
maBtnSelectSingle.disposeAndClear();
|
|
maBtnUnselectSingle.disposeAndClear();
|
|
maBtnOk.disposeAndClear();
|
|
maBtnCancel.disposeAndClear();
|
|
ScMenuFloatingWindow::dispose();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::getSectionPosSize(
|
|
Point& rPos, Size& rSize, SectionType eType) const
|
|
{
|
|
sal_Int32 nScaleFactor = GetDPIScaleFactor();
|
|
|
|
// constant parameters.
|
|
const long nSearchBoxMargin = 10 *nScaleFactor;
|
|
const long nListBoxMargin = 5 * nScaleFactor; // horizontal distance from the side of the dialog to the listbox border.
|
|
const long nListBoxInnerPadding = 5 * nScaleFactor;
|
|
const long nTopMargin = 5 * nScaleFactor;
|
|
const long nMenuHeight = maMenuSize.getHeight();
|
|
const long nSingleItemBtnAreaHeight = 32 * nScaleFactor; // height of the middle area below the list box where the single-action buttons are.
|
|
const long nBottomBtnAreaHeight = 50 * nScaleFactor; // height of the bottom area where the OK and Cancel buttons are.
|
|
const long nBtnWidth = 90 * nScaleFactor;
|
|
const long nLabelHeight = getLabelFont().GetHeight();
|
|
const long nBtnHeight = nLabelHeight * 2;
|
|
const long nBottomMargin = 10 * nScaleFactor;
|
|
const long nMenuListMargin = 5 * nScaleFactor;
|
|
const long nSearchBoxHeight = nLabelHeight * 2;
|
|
|
|
// parameters calculated from constants.
|
|
const long nListBoxWidth = maWndSize.Width() - nListBoxMargin*2;
|
|
const long nListBoxHeight = maWndSize.Height() - nTopMargin - nMenuHeight -
|
|
nMenuListMargin - nSearchBoxHeight - nSearchBoxMargin - nSingleItemBtnAreaHeight - nBottomBtnAreaHeight;
|
|
|
|
const long nSingleBtnAreaY = nTopMargin + nMenuHeight + nListBoxHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin - 1;
|
|
|
|
switch (eType)
|
|
{
|
|
case WHOLE:
|
|
{
|
|
rPos = Point(0, 0);
|
|
rSize = maWndSize;
|
|
}
|
|
break;
|
|
case EDIT_SEARCH:
|
|
{
|
|
rPos = Point(nSearchBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
|
|
rSize = Size(maWndSize.Width() - 2*nSearchBoxMargin, nSearchBoxHeight);
|
|
}
|
|
break;
|
|
case LISTBOX_AREA_OUTER:
|
|
{
|
|
rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
|
|
rSize = Size(nListBoxWidth, nListBoxHeight);
|
|
}
|
|
break;
|
|
case LISTBOX_AREA_INNER:
|
|
{
|
|
rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
|
|
rPos.X() += nListBoxInnerPadding;
|
|
rPos.Y() += nListBoxInnerPadding;
|
|
|
|
rSize = Size(nListBoxWidth, nListBoxHeight);
|
|
rSize.Width() -= nListBoxInnerPadding*2;
|
|
rSize.Height() -= nListBoxInnerPadding*2;
|
|
}
|
|
break;
|
|
case SINGLE_BTN_AREA:
|
|
{
|
|
rPos = Point(nListBoxMargin, nSingleBtnAreaY);
|
|
rSize = Size(nListBoxWidth, nSingleItemBtnAreaHeight);
|
|
}
|
|
break;
|
|
case CHECK_TOGGLE_ALL:
|
|
{
|
|
long h = std::min(maChkToggleAll->CalcMinimumSize().Height(), 26L);
|
|
rPos = Point(nListBoxMargin, nSingleBtnAreaY);
|
|
rPos.X() += 5;
|
|
rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
|
|
rSize = Size(70, h);
|
|
}
|
|
break;
|
|
case BTN_SINGLE_SELECT:
|
|
{
|
|
long h = 26 * nScaleFactor;
|
|
rPos = Point(nListBoxMargin, nSingleBtnAreaY);
|
|
rPos.X() += nListBoxWidth - h - 10 - h - 10;
|
|
rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
|
|
rSize = Size(h, h);
|
|
}
|
|
break;
|
|
case BTN_SINGLE_UNSELECT:
|
|
{
|
|
long h = 26 * nScaleFactor;
|
|
rPos = Point(nListBoxMargin, nSingleBtnAreaY);
|
|
rPos.X() += nListBoxWidth - h - 10;
|
|
rPos.Y() += (nSingleItemBtnAreaHeight - h)/2;
|
|
rSize = Size(h, h);
|
|
}
|
|
break;
|
|
case BTN_OK:
|
|
{
|
|
long x = (maWndSize.Width() - nBtnWidth*2)/3;
|
|
long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
|
|
rPos = Point(x, y);
|
|
rSize = Size(nBtnWidth, nBtnHeight);
|
|
}
|
|
break;
|
|
case BTN_CANCEL:
|
|
{
|
|
long x = (maWndSize.Width() - nBtnWidth*2)/3*2 + nBtnWidth;
|
|
long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
|
|
rPos = Point(x, y);
|
|
rSize = Size(nBtnWidth, nBtnHeight);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
void ScCheckListMenuWindow::packWindow()
|
|
{
|
|
maMenuSize = getMenuSize();
|
|
|
|
if (maWndSize.Width() < maMenuSize.Width())
|
|
// Widen the window to fit the menu items.
|
|
maWndSize.Width() = maMenuSize.Width();
|
|
|
|
// Set proper window height based on the number of menu items.
|
|
if (maWndSize.Height() < maMenuSize.Height()*2.8)
|
|
maWndSize.Height() = maMenuSize.Height()*2.8;
|
|
|
|
// TODO: Make sure the window height never exceeds the height of the
|
|
// screen. Also do adjustment based on the number of check box items.
|
|
|
|
SetOutputSizePixel(maWndSize);
|
|
|
|
const StyleSettings& rStyle = GetSettings().GetStyleSettings();
|
|
|
|
Point aPos;
|
|
Size aSize;
|
|
getSectionPosSize(aPos, aSize, WHOLE);
|
|
SetOutputSizePixel(aSize);
|
|
|
|
getSectionPosSize(aPos, aSize, BTN_OK);
|
|
maBtnOk->SetPosSizePixel(aPos, aSize);
|
|
maBtnOk->SetFont(getLabelFont());
|
|
maBtnOk->SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
|
|
maBtnOk->Show();
|
|
|
|
getSectionPosSize(aPos, aSize, BTN_CANCEL);
|
|
maBtnCancel->SetPosSizePixel(aPos, aSize);
|
|
maBtnCancel->SetFont(getLabelFont());
|
|
maBtnCancel->Show();
|
|
|
|
getSectionPosSize(aPos, aSize, EDIT_SEARCH);
|
|
maEdSearch->SetPosSizePixel(aPos, aSize);
|
|
maEdSearch->SetFont(getLabelFont());
|
|
maEdSearch->SetControlBackground(rStyle.GetFieldColor());
|
|
maEdSearch->SetPlaceholderText(SC_STRLOAD(RID_POPUP_FILTER, STR_EDIT_SEARCH_ITEMS));
|
|
maEdSearch->SetModifyHdl( LINK(this, ScCheckListMenuWindow, EdModifyHdl) );
|
|
maEdSearch->Show();
|
|
|
|
getSectionPosSize(aPos, aSize, LISTBOX_AREA_INNER);
|
|
maChecks->SetPosSizePixel(aPos, aSize);
|
|
maChecks->SetFont(getLabelFont());
|
|
maChecks->SetCheckButtonHdl( LINK(this, ScCheckListMenuWindow, CheckHdl) );
|
|
maChecks->Show();
|
|
|
|
getSectionPosSize(aPos, aSize, CHECK_TOGGLE_ALL);
|
|
maChkToggleAll->SetPosSizePixel(aPos, aSize);
|
|
maChkToggleAll->SetFont(getLabelFont());
|
|
maChkToggleAll->SetText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_TOGGLE_ALL));
|
|
maChkToggleAll->SetTextColor(rStyle.GetMenuTextColor());
|
|
maChkToggleAll->SetControlBackground(rStyle.GetMenuColor());
|
|
maChkToggleAll->SetClickHdl( LINK(this, ScCheckListMenuWindow, TriStateHdl) );
|
|
maChkToggleAll->Show();
|
|
|
|
sal_Int32 nScaleFactor = GetDPIScaleFactor();
|
|
|
|
Image aSingleSelect(ScResId(RID_IMG_SELECT_CURRENT));
|
|
if (nScaleFactor != 1)
|
|
{
|
|
BitmapEx aBitmap = aSingleSelect.GetBitmapEx();
|
|
aBitmap.Scale(nScaleFactor, nScaleFactor, BmpScaleFlag::Fast);
|
|
aSingleSelect = Image(aBitmap);
|
|
}
|
|
|
|
getSectionPosSize(aPos, aSize, BTN_SINGLE_SELECT);
|
|
maBtnSelectSingle->SetPosSizePixel(aPos, aSize);
|
|
maBtnSelectSingle->SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_SELECT_CURRENT));
|
|
maBtnSelectSingle->SetModeImage(aSingleSelect);
|
|
maBtnSelectSingle->SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
|
|
maBtnSelectSingle->Show();
|
|
|
|
Image aSingleUnselect(ScResId(RID_IMG_UNSELECT_CURRENT));
|
|
if (nScaleFactor != 1)
|
|
{
|
|
BitmapEx aBitmap = aSingleUnselect.GetBitmapEx();
|
|
aBitmap.Scale(nScaleFactor, nScaleFactor, BmpScaleFlag::Fast);
|
|
aSingleUnselect = Image(aBitmap);
|
|
}
|
|
|
|
getSectionPosSize(aPos, aSize, BTN_SINGLE_UNSELECT);
|
|
maBtnUnselectSingle->SetPosSizePixel(aPos, aSize);
|
|
maBtnUnselectSingle->SetQuickHelpText(SC_STRLOAD(RID_POPUP_FILTER, STR_BTN_UNSELECT_CURRENT));
|
|
maBtnUnselectSingle->SetModeImage(aSingleUnselect);
|
|
maBtnUnselectSingle->SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
|
|
maBtnUnselectSingle->Show();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::setAllMemberState(bool bSet)
|
|
{
|
|
size_t n = maMembers.size();
|
|
OUString aLabel;
|
|
for (size_t i = 0; i < n; ++i) {
|
|
aLabel = maMembers[i].maName;
|
|
if (aLabel.isEmpty())
|
|
aLabel = ScGlobal::GetRscString(STR_EMPTYDATA);
|
|
maChecks->ShowCheckEntry( aLabel, maMembers[i].mpParent, true, bSet);
|
|
}
|
|
|
|
if (!maConfig.mbAllowEmptySet)
|
|
// We need to have at least one member selected.
|
|
maBtnOk->Enable(maChecks->GetCheckedEntryCount() != 0);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::selectCurrentMemberOnly(bool bSet)
|
|
{
|
|
setAllMemberState(!bSet);
|
|
SvTreeListEntry* pEntry = maChecks->GetCurEntry();
|
|
if (!pEntry)
|
|
return;
|
|
maChecks->CheckEntry(pEntry, bSet );
|
|
}
|
|
|
|
void ScCheckListMenuWindow::cycleFocus(bool bReverse)
|
|
{
|
|
maTabStopCtrls[mnCurTabStop]->SetFakeFocus(false);
|
|
maTabStopCtrls[mnCurTabStop]->LoseFocus();
|
|
if (mnCurTabStop == 0)
|
|
clearSelectedMenuItem();
|
|
|
|
if (bReverse)
|
|
{
|
|
if (mnCurTabStop > 0)
|
|
--mnCurTabStop;
|
|
else
|
|
mnCurTabStop = maTabStopCtrls.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
++mnCurTabStop;
|
|
if (mnCurTabStop >= maTabStopCtrls.size())
|
|
mnCurTabStop = 0;
|
|
}
|
|
maTabStopCtrls[mnCurTabStop]->SetFakeFocus(true);
|
|
maTabStopCtrls[mnCurTabStop]->GrabFocus();
|
|
}
|
|
|
|
IMPL_LINK( ScCheckListMenuWindow, ButtonHdl, Button*, pBtn )
|
|
{
|
|
if (pBtn == maBtnOk.get())
|
|
close(true);
|
|
else if (pBtn == maBtnSelectSingle.get())
|
|
{
|
|
selectCurrentMemberOnly(true);
|
|
CheckHdl(maChecks.get());
|
|
}
|
|
else if (pBtn == maBtnUnselectSingle.get())
|
|
{
|
|
selectCurrentMemberOnly(false);
|
|
CheckHdl(maChecks.get());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
IMPL_LINK_NOARG(ScCheckListMenuWindow, TriStateHdl)
|
|
{
|
|
switch (mePrevToggleAllState)
|
|
{
|
|
case TRISTATE_FALSE:
|
|
maChkToggleAll->SetState(TRISTATE_TRUE);
|
|
setAllMemberState(true);
|
|
break;
|
|
case TRISTATE_TRUE:
|
|
maChkToggleAll->SetState(TRISTATE_FALSE);
|
|
setAllMemberState(false);
|
|
break;
|
|
case TRISTATE_INDET:
|
|
default:
|
|
maChkToggleAll->SetState(TRISTATE_TRUE);
|
|
setAllMemberState(true);
|
|
break;
|
|
}
|
|
|
|
mePrevToggleAllState = maChkToggleAll->GetState();
|
|
return 0;
|
|
}
|
|
|
|
IMPL_LINK_NOARG(ScCheckListMenuWindow, EdModifyHdl)
|
|
{
|
|
OUString aSearchText = maEdSearch->GetText();
|
|
aSearchText = aSearchText.toAsciiLowerCase();
|
|
bool bSearchTextEmpty = aSearchText.isEmpty();
|
|
size_t n = maMembers.size();
|
|
size_t nSelCount = 0;
|
|
OUString aLabelDisp;
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
aLabelDisp = maMembers[i].maName;
|
|
|
|
if ( aLabelDisp.isEmpty() )
|
|
aLabelDisp = ScGlobal::GetRscString( STR_EMPTYDATA );
|
|
|
|
if ( bSearchTextEmpty )
|
|
{
|
|
maChecks->ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, true, maMembers[i].mbVisible );
|
|
if ( maMembers[i].mbVisible )
|
|
++nSelCount;
|
|
continue;
|
|
}
|
|
|
|
if ( aLabelDisp.toAsciiLowerCase().indexOf( aSearchText ) != -1 )
|
|
{
|
|
maChecks->ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, true, true );
|
|
++nSelCount;
|
|
}
|
|
else
|
|
maChecks->ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, false, false );
|
|
}
|
|
|
|
if ( nSelCount == n )
|
|
maChkToggleAll->SetState( TRISTATE_TRUE );
|
|
else if ( nSelCount == 0 )
|
|
maChkToggleAll->SetState( TRISTATE_FALSE );
|
|
else
|
|
maChkToggleAll->SetState( TRISTATE_INDET );
|
|
|
|
return 0;
|
|
}
|
|
|
|
IMPL_LINK( ScCheckListMenuWindow, CheckHdl, SvTreeListBox*, pChecks )
|
|
{
|
|
if (pChecks != maChecks.get())
|
|
return 0;
|
|
SvTreeListEntry* pEntry = pChecks->GetHdlEntry();
|
|
if ( pEntry )
|
|
maChecks->CheckEntry( pEntry, ( pChecks->GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) );
|
|
size_t nNumChecked = maChecks->GetCheckedEntryCount();
|
|
if (nNumChecked == maMembers.size())
|
|
// all members visible
|
|
maChkToggleAll->SetState(TRISTATE_TRUE);
|
|
else if (nNumChecked == 0)
|
|
// no members visible
|
|
maChkToggleAll->SetState(TRISTATE_FALSE);
|
|
else
|
|
maChkToggleAll->SetState(TRISTATE_INDET);
|
|
|
|
if (!maConfig.mbAllowEmptySet)
|
|
// We need to have at least one member selected.
|
|
maBtnOk->Enable(nNumChecked != 0);
|
|
|
|
mePrevToggleAllState = maChkToggleAll->GetState();
|
|
return 0;
|
|
}
|
|
|
|
void ScCheckListMenuWindow::MouseMove(const MouseEvent& rMEvt)
|
|
{
|
|
ScMenuFloatingWindow::MouseMove(rMEvt);
|
|
|
|
size_t nSelectedMenu = getSelectedMenuItem();
|
|
if (nSelectedMenu == MENU_NOT_SELECTED)
|
|
queueCloseSubMenu();
|
|
}
|
|
|
|
bool ScCheckListMenuWindow::Notify(NotifyEvent& rNEvt)
|
|
{
|
|
if (rNEvt.GetType() == MouseNotifyEvent::KEYUP)
|
|
{
|
|
const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
|
|
const vcl::KeyCode& rCode = pKeyEvent->GetKeyCode();
|
|
bool bShift = rCode.IsShift();
|
|
if (rCode.GetCode() == KEY_TAB)
|
|
{
|
|
cycleFocus(bShift);
|
|
return true;
|
|
}
|
|
}
|
|
return ScMenuFloatingWindow::Notify(rNEvt);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
|
|
{
|
|
ScMenuFloatingWindow::Paint(rRenderContext, rRect);
|
|
|
|
const StyleSettings& rStyle = GetSettings().GetStyleSettings();
|
|
Color aMemberBackColor = rStyle.GetFieldColor();
|
|
Color aBorderColor = rStyle.GetShadowColor();
|
|
|
|
Point aPos;
|
|
Size aSize;
|
|
getSectionPosSize(aPos, aSize, LISTBOX_AREA_OUTER);
|
|
|
|
// Member list box background
|
|
rRenderContext.SetFillColor(aMemberBackColor);
|
|
rRenderContext.SetLineColor(aBorderColor);
|
|
rRenderContext.DrawRect(Rectangle(aPos,aSize));
|
|
|
|
// Single-action button box
|
|
getSectionPosSize(aPos, aSize, SINGLE_BTN_AREA);
|
|
rRenderContext.SetFillColor(rStyle.GetMenuColor());
|
|
rRenderContext.DrawRect(Rectangle(aPos,aSize));
|
|
}
|
|
|
|
vcl::Window* ScCheckListMenuWindow::GetPreferredKeyInputWindow()
|
|
{
|
|
return maTabStopCtrls[mnCurTabStop];
|
|
}
|
|
|
|
Reference<XAccessible> ScCheckListMenuWindow::CreateAccessible()
|
|
{
|
|
if (!mxAccessible.is())
|
|
{
|
|
mxAccessible.set(new ScAccessibleFilterTopWindow(
|
|
GetAccessibleParentWindow()->GetAccessible(), this, getName()));
|
|
ScAccessibleFilterTopWindow* pAccTop = static_cast<ScAccessibleFilterTopWindow*>(mxAccessible.get());
|
|
fillMenuItemsToAccessible(pAccTop);
|
|
|
|
pAccTop->setAccessibleChild(
|
|
maEdSearch->CreateAccessible(), ScAccessibleFilterTopWindow::EDIT_SEARCH_BOX);
|
|
pAccTop->setAccessibleChild(
|
|
maChecks->CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX);
|
|
pAccTop->setAccessibleChild(
|
|
maChkToggleAll->CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL);
|
|
pAccTop->setAccessibleChild(
|
|
maBtnSelectSingle->CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_ON_BTN);
|
|
pAccTop->setAccessibleChild(
|
|
maBtnUnselectSingle->CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_OFF_BTN);
|
|
pAccTop->setAccessibleChild(
|
|
maBtnOk->CreateAccessible(), ScAccessibleFilterTopWindow::OK_BTN);
|
|
pAccTop->setAccessibleChild(
|
|
maBtnCancel->CreateAccessible(), ScAccessibleFilterTopWindow::CANCEL_BTN);
|
|
}
|
|
|
|
return mxAccessible;
|
|
}
|
|
|
|
void ScCheckListMenuWindow::setMemberSize(size_t n)
|
|
{
|
|
maMembers.reserve(n);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::addDateMember(const OUString& rsName, double nVal, bool bVisible)
|
|
{
|
|
ScDocument* pDoc = getDoc();
|
|
SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
|
|
|
|
// Convert the numeric date value to a date object.
|
|
Date aDate = *(pFormatter->GetNullDate());
|
|
aDate += static_cast<long>(rtl::math::approxFloor(nVal));
|
|
|
|
sal_uInt16 nYear = aDate.GetYear();
|
|
sal_uInt16 nMonth = aDate.GetMonth();
|
|
sal_uInt16 nDay = aDate.GetDay();
|
|
|
|
// Get the localized month name list.
|
|
CalendarWrapper* pCalendar = ScGlobal::GetCalendar();
|
|
uno::Sequence<i18n::CalendarItem2> aMonths = pCalendar->getMonths();
|
|
if (aMonths.getLength() < nMonth)
|
|
return;
|
|
|
|
OUString aYearName = OUString::number(nYear);
|
|
OUString aMonthName = aMonths[nMonth-1].FullName;
|
|
OUString aDayName = OUString::number(nDay);
|
|
|
|
maChecks->SetUpdateMode(false);
|
|
|
|
SvTreeListEntry* pYearEntry = maChecks->FindEntry(NULL, aYearName);
|
|
if (!pYearEntry)
|
|
{
|
|
pYearEntry = maChecks->InsertEntry(aYearName, NULL, true);
|
|
Member aMemYear;
|
|
aMemYear.maName = aYearName;
|
|
aMemYear.maRealName = rsName;
|
|
aMemYear.mbDate = true;
|
|
aMemYear.mbLeaf = false;
|
|
aMemYear.mbVisible = bVisible;
|
|
aMemYear.mpParent = NULL;
|
|
maMembers.push_back(aMemYear);
|
|
}
|
|
|
|
SvTreeListEntry* pMonthEntry = maChecks->FindEntry(pYearEntry, aMonthName);
|
|
if (!pMonthEntry)
|
|
{
|
|
pMonthEntry = maChecks->InsertEntry(aMonthName, pYearEntry, true);
|
|
Member aMemMonth;
|
|
aMemMonth.maName = aMonthName;
|
|
aMemMonth.maRealName = rsName;
|
|
aMemMonth.mbDate = true;
|
|
aMemMonth.mbLeaf = false;
|
|
aMemMonth.mbVisible = bVisible;
|
|
aMemMonth.mpParent = pYearEntry;
|
|
maMembers.push_back(aMemMonth);
|
|
}
|
|
|
|
SvTreeListEntry* pDayEntry = maChecks->FindEntry(pMonthEntry, aDayName);
|
|
if (!pDayEntry)
|
|
{
|
|
maChecks->InsertEntry(aDayName, pMonthEntry, false);
|
|
Member aMemDay;
|
|
aMemDay.maName = aDayName;
|
|
aMemDay.maRealName = rsName;
|
|
aMemDay.mbDate = true;
|
|
aMemDay.mbLeaf = true;
|
|
aMemDay.mbVisible = bVisible;
|
|
aMemDay.mpParent = pMonthEntry;
|
|
maMembers.push_back(aMemDay);
|
|
}
|
|
|
|
maChecks->SetUpdateMode(true);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::addMember(const OUString& rName, bool bVisible)
|
|
{
|
|
Member aMember;
|
|
aMember.maName = rName;
|
|
aMember.mbDate = false;
|
|
aMember.mbLeaf = true;
|
|
aMember.mbVisible = bVisible;
|
|
aMember.mpParent = NULL;
|
|
maMembers.push_back(aMember);
|
|
}
|
|
|
|
ScCheckListBox::ScCheckListBox( vcl::Window* pParent, WinBits nWinStyle )
|
|
: SvTreeListBox( pParent, nWinStyle ), mpCheckButton( NULL )
|
|
{
|
|
Init();
|
|
}
|
|
|
|
SvTreeListEntry* ScCheckListBox::FindEntry( SvTreeListEntry* pParent, const OUString& sNode )
|
|
{
|
|
sal_uInt16 nRootPos = 0;
|
|
SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : GetEntry( nRootPos );
|
|
while ( pEntry )
|
|
{
|
|
if ( sNode.equals(GetEntryText( pEntry )) )
|
|
return pEntry;
|
|
|
|
pEntry = pParent ? NextSibling( pEntry ) : GetEntry( ++nRootPos );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void ScCheckListBox::Init()
|
|
{
|
|
mpCheckButton = new SvLBoxButtonData( this );
|
|
EnableCheckButton( mpCheckButton );
|
|
SetNodeDefaultImages();
|
|
}
|
|
|
|
bool ScCheckListBox::IsChecked( const OUString& sName, SvTreeListEntry* pParent )
|
|
{
|
|
SvTreeListEntry* pEntry = FindEntry( pParent, sName );
|
|
if ( pEntry && GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void ScCheckListBox::CheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bCheck )
|
|
{
|
|
SvTreeListEntry* pEntry = FindEntry( pParent, sName );
|
|
if ( pEntry )
|
|
CheckEntry( pEntry, bCheck );
|
|
}
|
|
|
|
// Recursively check all children of pParent
|
|
void ScCheckListBox::CheckAllChildren( SvTreeListEntry* pParent, bool bCheck )
|
|
{
|
|
if ( pParent )
|
|
{
|
|
SetCheckButtonState(
|
|
pParent, bCheck ? SvButtonState( SV_BUTTON_CHECKED ) :
|
|
SvButtonState( SV_BUTTON_UNCHECKED ) );
|
|
}
|
|
SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
|
|
while ( pEntry )
|
|
{
|
|
CheckAllChildren( pEntry, bCheck );
|
|
pEntry = NextSibling( pEntry );
|
|
}
|
|
}
|
|
|
|
void ScCheckListBox::CheckEntry( SvTreeListEntry* pParent, bool bCheck )
|
|
{
|
|
// recursively check all items below pParent
|
|
CheckAllChildren( pParent, bCheck );
|
|
// checking pParent can affect ancestors, e.g. if ancestor is unchecked and pParent is
|
|
// now checked then the ancestor needs to be checked also
|
|
SvTreeListEntry* pAncestor = GetParent(pParent);
|
|
if ( pAncestor )
|
|
{
|
|
while ( pAncestor )
|
|
{
|
|
// if any first level children checked then ancestor
|
|
// needs to be checked, similarly if no first level children
|
|
// checked then ancestor needs to be unchecked
|
|
SvTreeListEntry* pChild = FirstChild( pAncestor );
|
|
bool bChildChecked = false;
|
|
|
|
while ( pChild )
|
|
{
|
|
if ( GetCheckButtonState( pChild ) == SV_BUTTON_CHECKED )
|
|
{
|
|
bChildChecked = true;
|
|
break;
|
|
}
|
|
pChild = NextSibling( pChild );
|
|
}
|
|
SetCheckButtonState(
|
|
pAncestor, bChildChecked ? SvButtonState( SV_BUTTON_CHECKED ) :
|
|
SvButtonState( SV_BUTTON_UNCHECKED ) );
|
|
pAncestor = GetParent(pAncestor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScCheckListBox::ShowCheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bShow, bool bCheck )
|
|
{
|
|
SvTreeListEntry* pEntry = FindEntry( pParent, sName );
|
|
if ( bShow )
|
|
{
|
|
if ( !pEntry )
|
|
{
|
|
pEntry = InsertEntry(
|
|
sName, NULL, false, TREELIST_APPEND, NULL,
|
|
SvLBoxButtonKind_enabledCheckbox);
|
|
|
|
SetCheckButtonState(
|
|
pEntry, bCheck ? SV_BUTTON_CHECKED : SV_BUTTON_UNCHECKED);
|
|
}
|
|
else
|
|
CheckEntry( pEntry, bCheck );
|
|
}
|
|
else if ( pEntry )
|
|
RemoveParentKeepChildren( pEntry );
|
|
}
|
|
|
|
SvTreeListEntry* ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
|
|
{
|
|
if ( pParent && GetCheckButtonState( pParent ) == SV_BUTTON_CHECKED )
|
|
nCount++;
|
|
// Iterate over the children
|
|
SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
|
|
while ( pEntry )
|
|
{
|
|
CountCheckedEntries( pEntry, nCount );
|
|
pEntry = NextSibling( pEntry );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
sal_uInt16 ScCheckListBox::GetCheckedEntryCount() const
|
|
{
|
|
sal_uLong nCount = 0;
|
|
CountCheckedEntries( NULL, nCount );
|
|
return nCount;
|
|
}
|
|
|
|
void ScCheckListBox::ExpandChildren( SvTreeListEntry* pParent )
|
|
{
|
|
if ( pParent )
|
|
Expand( pParent );
|
|
// Iterate over the children
|
|
SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
|
|
while ( pEntry )
|
|
{
|
|
ExpandChildren( pEntry );
|
|
pEntry = NextSibling( pEntry );
|
|
}
|
|
}
|
|
|
|
void ScCheckListBox::KeyInput( const KeyEvent& rKEvt )
|
|
{
|
|
const vcl::KeyCode& rKey = rKEvt.GetKeyCode();
|
|
|
|
if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
|
|
{
|
|
SvTreeListEntry* pEntry = GetCurEntry();
|
|
if ( pEntry )
|
|
{
|
|
bool bCheck = ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED );
|
|
CheckEntry( pEntry, !bCheck );
|
|
if ( bCheck != ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) )
|
|
CheckButtonHdl();
|
|
}
|
|
}
|
|
else if ( GetEntryCount() )
|
|
SvTreeListBox::KeyInput( rKEvt );
|
|
}
|
|
|
|
void ScCheckListMenuWindow::initMembers()
|
|
{
|
|
size_t n = maMembers.size();
|
|
size_t nVisMemCount = 0;
|
|
|
|
maChecks->SetUpdateMode(false);
|
|
maChecks->GetModel()->EnableInvalidate(false);
|
|
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (maMembers[i].mbDate)
|
|
{
|
|
maChecks->CheckEntry(maMembers[i].maName, maMembers[i].mpParent, maMembers[i].mbVisible);
|
|
// Expand first node of checked dates
|
|
if (!maMembers[i].mpParent && maChecks->IsChecked(maMembers[i].maName, maMembers[i].mpParent))
|
|
{
|
|
SvTreeListEntry* pEntry = maChecks->FindEntry(NULL, maMembers[i].maName);
|
|
if (pEntry)
|
|
maChecks->Expand(pEntry);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OUString aLabel = maMembers[i].maName;
|
|
if (aLabel.isEmpty())
|
|
aLabel = ScGlobal::GetRscString(STR_EMPTYDATA);
|
|
SvTreeListEntry* pEntry = maChecks->InsertEntry(
|
|
aLabel, NULL, false, TREELIST_APPEND, NULL,
|
|
SvLBoxButtonKind_enabledCheckbox);
|
|
|
|
maChecks->SetCheckButtonState(
|
|
pEntry, maMembers[i].mbVisible ? SV_BUTTON_CHECKED : SV_BUTTON_UNCHECKED);
|
|
}
|
|
|
|
if (maMembers[i].mbVisible)
|
|
++nVisMemCount;
|
|
}
|
|
if (nVisMemCount == n)
|
|
{
|
|
// all members visible
|
|
maChkToggleAll->SetState(TRISTATE_TRUE);
|
|
mePrevToggleAllState = TRISTATE_TRUE;
|
|
}
|
|
else if (nVisMemCount == 0)
|
|
{
|
|
// no members visible
|
|
maChkToggleAll->SetState(TRISTATE_FALSE);
|
|
mePrevToggleAllState = TRISTATE_FALSE;
|
|
}
|
|
else
|
|
{
|
|
maChkToggleAll->SetState(TRISTATE_INDET);
|
|
mePrevToggleAllState = TRISTATE_INDET;
|
|
}
|
|
|
|
maChecks->GetModel()->EnableInvalidate(true);
|
|
maChecks->SetUpdateMode(true);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::setConfig(const Config& rConfig)
|
|
{
|
|
maConfig = rConfig;
|
|
}
|
|
|
|
bool ScCheckListMenuWindow::isAllSelected() const
|
|
{
|
|
return maChkToggleAll->IsChecked();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::getResult(ResultType& rResult)
|
|
{
|
|
ResultType aResult;
|
|
size_t n = maMembers.size();
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if ( maMembers[i].mbLeaf )
|
|
{
|
|
OUString aLabel = maMembers[i].maName;
|
|
if (aLabel.isEmpty())
|
|
aLabel = ScGlobal::GetRscString(STR_EMPTYDATA);
|
|
bool bState = maChecks->IsChecked( aLabel, maMembers[i].mpParent );
|
|
OUString sName;
|
|
if ( maMembers[i].mbDate )
|
|
sName = maMembers[i].maRealName;
|
|
else
|
|
sName = maMembers[i].maName;
|
|
aResult.insert(ResultType::value_type(sName, bState));
|
|
}
|
|
}
|
|
rResult.swap(aResult);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::launch(const Rectangle& rRect)
|
|
{
|
|
packWindow();
|
|
if (!maConfig.mbAllowEmptySet)
|
|
// We need to have at least one member selected.
|
|
maBtnOk->Enable(maChecks->GetCheckedEntryCount() != 0);
|
|
|
|
Rectangle aRect(rRect);
|
|
if (maConfig.mbRTL)
|
|
{
|
|
// In RTL mode, the logical "left" is visual "right".
|
|
long nLeft = aRect.Left() - aRect.GetWidth();
|
|
aRect.Left() = nLeft;
|
|
}
|
|
else if (maWndSize.Width() < aRect.GetWidth())
|
|
{
|
|
// Target rectangle (i.e. cell width) is wider than the window.
|
|
// Simulate right-aligned launch by modifying the target rectangle
|
|
// size.
|
|
long nDiff = aRect.GetWidth() - maWndSize.Width();
|
|
aRect.Left() += nDiff;
|
|
}
|
|
|
|
StartPopupMode(aRect, (FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus));
|
|
cycleFocus(); // Set initial focus to the check list box.
|
|
}
|
|
|
|
void ScCheckListMenuWindow::close(bool bOK)
|
|
{
|
|
if (bOK && mpOKAction.get())
|
|
mpOKAction->execute();
|
|
|
|
EndPopupMode();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::setExtendedData(ExtendedData* p)
|
|
{
|
|
mpExtendedData.reset(p);
|
|
}
|
|
|
|
ScCheckListMenuWindow::ExtendedData* ScCheckListMenuWindow::getExtendedData()
|
|
{
|
|
return mpExtendedData.get();
|
|
}
|
|
|
|
void ScCheckListMenuWindow::setOKAction(Action* p)
|
|
{
|
|
mpOKAction.reset(p);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::setPopupEndAction(Action* p)
|
|
{
|
|
mpPopupEndAction.reset(p);
|
|
}
|
|
|
|
void ScCheckListMenuWindow::handlePopupEnd()
|
|
{
|
|
clearSelectedMenuItem();
|
|
if (mpPopupEndAction)
|
|
mpPopupEndAction->execute();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|