Looking at the original fixed-size arrow painting code replaced
in commit b62c43d120
("Anti-alias
toolbar button drop-downs."), it used some fixed values of 5
and 3 to match the arrow box width of 11.
The new code assumes the width is the expected arrow size, minus
a minimal margin to separate the arrow from the button border,
and there is enough height available. Based on these assumptions,
the code now scales, positions and paints the triangle to fill
the available space.
Change-Id: Ied721e494d105106086ef6252e72ae7395eafe08
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97537
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
4903 lines
160 KiB
C++
4903 lines
160 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 <vcl/toolbox.hxx>
|
|
#include <vcl/commandinfoprovider.hxx>
|
|
#include <vcl/event.hxx>
|
|
#include <vcl/decoview.hxx>
|
|
#include <vcl/accel.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/help.hxx>
|
|
#include <vcl/mnemonic.hxx>
|
|
#include <vcl/gradient.hxx>
|
|
#include <vcl/layout.hxx>
|
|
#include <vcl/menu.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vclstatuslistener.hxx>
|
|
#include <vcl/ptrstyle.hxx>
|
|
#include <bitmaps.hlst>
|
|
|
|
#include <tools/poly.hxx>
|
|
#include <svl/imageitm.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <osl/diagnose.h>
|
|
|
|
#include <svdata.hxx>
|
|
#include <window.h>
|
|
#include <toolbox.h>
|
|
#include <spin.hxx>
|
|
#if defined(_WIN32)
|
|
#include <svsys.h>
|
|
#endif
|
|
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include <math.h>
|
|
|
|
|
|
#define SMALLBUTTON_HSIZE 7
|
|
#define SMALLBUTTON_VSIZE 7
|
|
|
|
#define SMALLBUTTON_OFF_NORMAL_X 3
|
|
#define SMALLBUTTON_OFF_NORMAL_Y 3
|
|
|
|
#define TB_TEXTOFFSET 2
|
|
#define TB_IMAGETEXTOFFSET 3
|
|
#define TB_LINESPACING 3
|
|
#define TB_SPIN_SIZE 14
|
|
#define TB_SPIN_OFFSET 2
|
|
#define TB_BORDER_OFFSET1 4
|
|
#define TB_BORDER_OFFSET2 2
|
|
#define TB_MAXLINES 5
|
|
#define TB_MAXNOSCROLL 32765
|
|
|
|
#define TB_DRAGWIDTH 8 // the default width of the drag grip
|
|
|
|
#define TB_CALCMODE_HORZ 1
|
|
#define TB_CALCMODE_VERT 2
|
|
#define TB_CALCMODE_FLOAT 3
|
|
|
|
#define TB_WBLINESIZING (WB_SIZEABLE | WB_DOCKABLE | WB_SCROLL)
|
|
|
|
#define DOCK_LINEHSIZE (sal_uInt16(0x0001))
|
|
#define DOCK_LINEVSIZE (sal_uInt16(0x0002))
|
|
#define DOCK_LINERIGHT (sal_uInt16(0x1000))
|
|
#define DOCK_LINEBOTTOM (sal_uInt16(0x2000))
|
|
#define DOCK_LINELEFT (sal_uInt16(0x4000))
|
|
#define DOCK_LINETOP (sal_uInt16(0x8000))
|
|
#define DOCK_LINEOFFSET 3
|
|
|
|
class ImplTBDragMgr
|
|
{
|
|
private:
|
|
VclPtr<ToolBox> mpDragBox;
|
|
Point maMouseOff;
|
|
tools::Rectangle maRect;
|
|
tools::Rectangle maStartRect;
|
|
Accelerator maAccel;
|
|
sal_uInt16 mnLineMode;
|
|
ToolBox::ImplToolItems::size_type mnStartLines;
|
|
|
|
ImplTBDragMgr(const ImplTBDragMgr&) = delete;
|
|
ImplTBDragMgr& operator=(const ImplTBDragMgr&) = delete;
|
|
|
|
public:
|
|
ImplTBDragMgr();
|
|
|
|
void StartDragging( ToolBox* pDragBox, const Point& rPos, const tools::Rectangle& rRect, sal_uInt16 nLineMode );
|
|
void Dragging( const Point& rPos );
|
|
void EndDragging( bool bOK = true );
|
|
DECL_LINK( SelectHdl, Accelerator&, void );
|
|
};
|
|
|
|
|
|
static ImplTBDragMgr* ImplGetTBDragMgr()
|
|
{
|
|
ImplSVData* pSVData = ImplGetSVData();
|
|
if ( !pSVData->maCtrlData.mpTBDragMgr )
|
|
pSVData->maCtrlData.mpTBDragMgr = new ImplTBDragMgr;
|
|
return pSVData->maCtrlData.mpTBDragMgr;
|
|
}
|
|
|
|
int ToolBox::ImplGetDragWidth( const vcl::RenderContext& rRenderContext, bool bHorz )
|
|
{
|
|
int nWidth = TB_DRAGWIDTH;
|
|
if( rRenderContext.IsNativeControlSupported( ControlType::Toolbar, ControlPart::Entire ) )
|
|
{
|
|
|
|
ImplControlValue aControlValue;
|
|
tools::Rectangle aContent, aBound;
|
|
tools::Rectangle aArea( Point(), rRenderContext.GetOutputSizePixel() );
|
|
|
|
if ( rRenderContext.GetNativeControlRegion(ControlType::Toolbar,
|
|
bHorz ? ControlPart::ThumbVert : ControlPart::ThumbHorz,
|
|
aArea, ControlState::NONE, aControlValue, aBound, aContent) )
|
|
{
|
|
nWidth = bHorz ? aContent.GetWidth() : aContent.GetHeight();
|
|
}
|
|
}
|
|
|
|
// increase the hit area of the drag handle according to DPI scale factor
|
|
nWidth *= rRenderContext.GetDPIScaleFactor();
|
|
|
|
return nWidth;
|
|
}
|
|
|
|
int ToolBox::ImplGetDragWidth() const
|
|
{
|
|
return ToolBox::ImplGetDragWidth( *this, mbHorz );
|
|
}
|
|
|
|
static ButtonType determineButtonType( ImplToolItem const * pItem, ButtonType defaultType )
|
|
{
|
|
ButtonType tmpButtonType = defaultType;
|
|
ToolBoxItemBits nBits = pItem->mnBits & ( ToolBoxItemBits::TEXT_ONLY | ToolBoxItemBits::ICON_ONLY );
|
|
if ( nBits != ToolBoxItemBits::NONE ) // item has custom setting
|
|
{
|
|
tmpButtonType = ButtonType::SYMBOLTEXT;
|
|
if ( nBits == ToolBoxItemBits::TEXT_ONLY )
|
|
tmpButtonType = ButtonType::TEXT;
|
|
else if ( nBits == ToolBoxItemBits::ICON_ONLY )
|
|
tmpButtonType = ButtonType::SYMBOLONLY;
|
|
}
|
|
return tmpButtonType;
|
|
}
|
|
|
|
void ToolBox::ImplUpdateDragArea() const
|
|
{
|
|
ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
|
|
if( pWrapper )
|
|
{
|
|
if ( ImplIsFloatingMode() || pWrapper->IsLocked() )
|
|
pWrapper->SetDragArea( tools::Rectangle() );
|
|
else
|
|
{
|
|
if( meAlign == WindowAlign::Top || meAlign == WindowAlign::Bottom )
|
|
pWrapper->SetDragArea( tools::Rectangle( 0, 0, ImplGetDragWidth(), GetOutputSizePixel().Height() ) );
|
|
else
|
|
pWrapper->SetDragArea( tools::Rectangle( 0, 0, GetOutputSizePixel().Width(), ImplGetDragWidth() ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplCalcBorder( WindowAlign eAlign, long& rLeft, long& rTop,
|
|
long& rRight, long& rBottom ) const
|
|
{
|
|
if( ImplIsFloatingMode() || !(mnWinStyle & WB_BORDER) )
|
|
{
|
|
// no border in floating mode
|
|
rLeft = rTop = rRight = rBottom = 0;
|
|
return;
|
|
}
|
|
|
|
ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
|
|
|
|
// reserve DragArea only for dockable toolbars
|
|
int dragwidth = ( pWrapper && !pWrapper->IsLocked() ) ? ImplGetDragWidth() : 0;
|
|
|
|
// no shadow border for dockable toolbars and toolbars with WB_NOSHADOW bit set, e.g. Calc's formulabar
|
|
int borderwidth = ( pWrapper || mnWinStyle & WB_NOSHADOW ) ? 0 : 2;
|
|
|
|
if ( eAlign == WindowAlign::Top )
|
|
{
|
|
rLeft = borderwidth+dragwidth;
|
|
rTop = borderwidth;
|
|
rRight = borderwidth;
|
|
rBottom = 0;
|
|
}
|
|
else if ( eAlign == WindowAlign::Left )
|
|
{
|
|
rLeft = borderwidth;
|
|
rTop = borderwidth+dragwidth;
|
|
rRight = 0;
|
|
rBottom = borderwidth;
|
|
}
|
|
else if ( eAlign == WindowAlign::Bottom )
|
|
{
|
|
rLeft = borderwidth+dragwidth;
|
|
rTop = 0;
|
|
rRight = borderwidth;
|
|
rBottom = borderwidth;
|
|
}
|
|
else
|
|
{
|
|
rLeft = 0;
|
|
rTop = borderwidth+dragwidth;
|
|
rRight = borderwidth;
|
|
rBottom = borderwidth;
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplCheckUpdate()
|
|
{
|
|
// remove any pending invalidates to avoid
|
|
// have them triggered when paint is locked (see mpData->mbIsPaintLocked)
|
|
// which would result in erasing the background only and not painting any items
|
|
// this must not be done when we're already in Paint()
|
|
|
|
// this is only required for transparent toolbars (see ImplDrawTransparentBackground() )
|
|
if( !IsBackground() && HasPaintEvent() && !IsInPaint() )
|
|
PaintImmediately();
|
|
}
|
|
|
|
void ToolBox::ImplDrawGrip(vcl::RenderContext& rRenderContext,
|
|
const tools::Rectangle &aDragArea, int nDragWidth, WindowAlign eAlign, bool bHorz)
|
|
{
|
|
bool bNativeOk = false;
|
|
const ControlPart ePart = bHorz ? ControlPart::ThumbVert : ControlPart::ThumbHorz;
|
|
const Size aSz( rRenderContext.GetOutputSizePixel() );
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ePart))
|
|
{
|
|
ToolbarValue aToolbarValue;
|
|
aToolbarValue.maGripRect = aDragArea;
|
|
|
|
tools::Rectangle aCtrlRegion(Point(), aSz);
|
|
|
|
bNativeOk = rRenderContext.DrawNativeControl( ControlType::Toolbar, ePart,
|
|
aCtrlRegion, ControlState::ENABLED, aToolbarValue, OUString() );
|
|
}
|
|
|
|
if( bNativeOk )
|
|
return;
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
|
|
rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
|
|
|
|
float fScaleFactor = rRenderContext.GetDPIScaleFactor();
|
|
|
|
if (eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom)
|
|
{
|
|
int height = static_cast<int>(0.6 * aSz.Height() + 0.5);
|
|
int i = (aSz.Height() - height) / 2;
|
|
height += i;
|
|
while (i <= height)
|
|
{
|
|
int x = nDragWidth / 2;
|
|
rRenderContext.DrawEllipse(tools::Rectangle(Point(x, i), Size(2 * fScaleFactor, 2 * fScaleFactor)));
|
|
i += 4 * fScaleFactor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int width = static_cast<int>(0.6 * aSz.Width() + 0.5);
|
|
int i = (aSz.Width() - width) / 2;
|
|
width += i;
|
|
while (i <= width)
|
|
{
|
|
int y = nDragWidth / 2;
|
|
rRenderContext.DrawEllipse(tools::Rectangle(Point(i, y), Size(2 * fScaleFactor, 2 * fScaleFactor)));
|
|
i += 4 * fScaleFactor;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDrawGrip(vcl::RenderContext& rRenderContext)
|
|
{
|
|
ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper(this);
|
|
if( pWrapper && !pWrapper->GetDragArea().IsEmpty() )
|
|
{
|
|
// execute pending paint requests
|
|
ImplCheckUpdate();
|
|
ImplDrawGrip( rRenderContext, pWrapper->GetDragArea(),
|
|
ImplGetDragWidth(), meAlign, mbHorz );
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDrawGradientBackground(vcl::RenderContext& rRenderContext)
|
|
{
|
|
// draw a nice gradient
|
|
|
|
Color startCol, endCol;
|
|
const StyleSettings rSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
|
|
startCol = rSettings.GetFaceGradientColor();
|
|
endCol = rSettings.GetFaceColor();
|
|
if (rSettings.GetHighContrastMode())
|
|
// no 'extreme' gradient when high contrast
|
|
startCol = endCol;
|
|
|
|
Gradient g;
|
|
g.SetAngle(mbHorz ? 0 : 900);
|
|
g.SetStyle(GradientStyle::Linear);
|
|
|
|
g.SetStartColor(startCol);
|
|
g.SetEndColor(endCol);
|
|
|
|
bool bLineColor = rRenderContext.IsLineColor();
|
|
Color aOldCol = rRenderContext.GetLineColor();
|
|
rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetShadowColor());
|
|
|
|
Size aFullSz(GetOutputSizePixel());
|
|
Size aLineSz(aFullSz);
|
|
|
|
// use the linesize only when floating
|
|
// full window height is used when docked (single line)
|
|
if (ImplIsFloatingMode())
|
|
{
|
|
long nLineSize;
|
|
if (mbHorz)
|
|
{
|
|
nLineSize = mnMaxItemHeight;
|
|
if (mnWinHeight > mnMaxItemHeight)
|
|
nLineSize = mnWinHeight;
|
|
|
|
aLineSz.setHeight( nLineSize );
|
|
}
|
|
else
|
|
{
|
|
nLineSize = mnMaxItemWidth;
|
|
aLineSz.setWidth( nLineSize );
|
|
}
|
|
}
|
|
|
|
long nLeft, nTop, nRight, nBottom;
|
|
ImplCalcBorder(meAlign, nLeft, nTop, nRight, nBottom);
|
|
|
|
Size aTopLineSz(aLineSz);
|
|
Size aBottomLineSz(aLineSz);
|
|
|
|
if (mnWinStyle & WB_BORDER)
|
|
{
|
|
if (mbHorz)
|
|
{
|
|
aTopLineSz.AdjustHeight(TB_BORDER_OFFSET2 + nTop );
|
|
aBottomLineSz.AdjustHeight(TB_BORDER_OFFSET2 + nBottom );
|
|
|
|
if (mnCurLines == 1)
|
|
aTopLineSz.AdjustHeight(TB_BORDER_OFFSET2 + nBottom );
|
|
}
|
|
else
|
|
{
|
|
aTopLineSz.AdjustWidth(TB_BORDER_OFFSET1 + nLeft );
|
|
aBottomLineSz.AdjustWidth(TB_BORDER_OFFSET1 + nRight );
|
|
|
|
if (mnCurLines == 1)
|
|
aTopLineSz.AdjustWidth(TB_BORDER_OFFSET1 + nLeft );
|
|
}
|
|
}
|
|
|
|
if (mbLineSpacing)
|
|
{
|
|
if (mbHorz)
|
|
{
|
|
aLineSz.AdjustHeight(TB_LINESPACING );
|
|
if (mnCurLines > 1)
|
|
aTopLineSz.AdjustHeight(TB_LINESPACING );
|
|
}
|
|
else
|
|
{
|
|
aLineSz.AdjustWidth(TB_LINESPACING );
|
|
if (mnCurLines > 1)
|
|
aTopLineSz.AdjustWidth(TB_LINESPACING );
|
|
}
|
|
}
|
|
|
|
if (mbHorz)
|
|
{
|
|
long y = 0;
|
|
|
|
rRenderContext.DrawGradient(tools::Rectangle(0, y, aTopLineSz.Width(), y + aTopLineSz.Height()), g);
|
|
y += aTopLineSz.Height();
|
|
|
|
while (y < (mnDY - aBottomLineSz.Height()))
|
|
{
|
|
rRenderContext.DrawGradient(tools::Rectangle(0, y, aLineSz.Width(), y + aLineSz.Height()), g);
|
|
y += aLineSz.Height();
|
|
}
|
|
|
|
rRenderContext.DrawGradient(tools::Rectangle(0, y, aBottomLineSz.Width(), y + aBottomLineSz.Height()), g);
|
|
}
|
|
else
|
|
{
|
|
long x = 0;
|
|
|
|
rRenderContext.DrawGradient(tools::Rectangle(x, 0, x + aTopLineSz.Width(), aTopLineSz.Height()), g);
|
|
x += aTopLineSz.Width();
|
|
|
|
while (x < (mnDX - aBottomLineSz.Width()))
|
|
{
|
|
rRenderContext.DrawGradient(tools::Rectangle(x, 0, x + aLineSz.Width(), aLineSz.Height()), g);
|
|
x += aLineSz.Width();
|
|
}
|
|
|
|
rRenderContext.DrawGradient(tools::Rectangle( x, 0, x + aBottomLineSz.Width(), aBottomLineSz.Height()), g);
|
|
}
|
|
|
|
if( bLineColor )
|
|
rRenderContext.SetLineColor( aOldCol );
|
|
|
|
}
|
|
|
|
bool ToolBox::ImplDrawNativeBackground(vcl::RenderContext& rRenderContext)
|
|
{
|
|
// use NWF
|
|
tools::Rectangle aCtrlRegion(Point(), GetOutputSizePixel());
|
|
|
|
return rRenderContext.DrawNativeControl( ControlType::Toolbar, mbHorz ? ControlPart::DrawBackgroundHorz : ControlPart::DrawBackgroundVert,
|
|
aCtrlRegion, ControlState::ENABLED, ImplControlValue(), OUString() );
|
|
}
|
|
|
|
void ToolBox::ImplDrawTransparentBackground(const vcl::Region &rRegion)
|
|
{
|
|
// just invalidate to trigger paint of the parent
|
|
const bool bOldPaintLock = mpData->mbIsPaintLocked;
|
|
mpData->mbIsPaintLocked = true;
|
|
|
|
// send an invalidate to the first opaque parent and invalidate the whole hierarchy from there (noclipchildren)
|
|
Invalidate(rRegion, InvalidateFlags::Update | InvalidateFlags::NoClipChildren);
|
|
|
|
mpData->mbIsPaintLocked = bOldPaintLock;
|
|
}
|
|
|
|
void ToolBox::ImplDrawConstantBackground(vcl::RenderContext& rRenderContext, const vcl::Region &rRegion, bool bIsInPopupMode)
|
|
{
|
|
// draw a constant color
|
|
if (!bIsInPopupMode)
|
|
{
|
|
// default background
|
|
rRenderContext.Erase(rRegion.GetBoundRect());
|
|
}
|
|
else
|
|
{
|
|
// use different color in popupmode
|
|
const StyleSettings rSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
Wallpaper aWallpaper(rSettings.GetFaceGradientColor());
|
|
rRenderContext.DrawWallpaper(rRegion.GetBoundRect(), aWallpaper);
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDrawBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
|
|
{
|
|
// execute pending paint requests
|
|
ImplCheckUpdate();
|
|
|
|
ImplDockingWindowWrapper* pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper(this);
|
|
bool bIsInPopupMode = ImplIsInPopupMode();
|
|
|
|
vcl::Region aPaintRegion(rRect);
|
|
|
|
// make sure we do not invalidate/erase too much
|
|
if (IsInPaint())
|
|
aPaintRegion.Intersect(GetActiveClipRegion());
|
|
|
|
rRenderContext.Push(PushFlags::CLIPREGION);
|
|
rRenderContext.IntersectClipRegion( aPaintRegion );
|
|
|
|
if (!pWrapper)
|
|
{
|
|
// no gradient for ordinary toolbars (not dockable)
|
|
if( !IsBackground() && !IsInPaint() )
|
|
ImplDrawTransparentBackground(aPaintRegion);
|
|
else
|
|
ImplDrawConstantBackground(rRenderContext, aPaintRegion, bIsInPopupMode);
|
|
}
|
|
else
|
|
{
|
|
// toolbars known to the dockingmanager will be drawn using NWF or a gradient
|
|
// docked toolbars are transparent and NWF is already used in the docking area which is their common background
|
|
// so NWF is used here for floating toolbars only
|
|
bool bNativeOk = false;
|
|
if( ImplIsFloatingMode() && rRenderContext.IsNativeControlSupported( ControlType::Toolbar, ControlPart::Entire) )
|
|
bNativeOk = ImplDrawNativeBackground(rRenderContext);
|
|
if (!bNativeOk)
|
|
{
|
|
const StyleSettings rSetting = Application::GetSettings().GetStyleSettings();
|
|
const bool isHeader = GetAlign() == WindowAlign::Top && !rSetting.GetPersonaHeader().IsEmpty();
|
|
const bool isFooter = GetAlign() == WindowAlign::Bottom && !rSetting.GetPersonaFooter().IsEmpty();
|
|
if (!IsBackground() || isHeader || isFooter)
|
|
{
|
|
if (!IsInPaint())
|
|
ImplDrawTransparentBackground(aPaintRegion);
|
|
}
|
|
else
|
|
ImplDrawGradientBackground(rRenderContext);
|
|
}
|
|
}
|
|
|
|
// restore clip region
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
void ToolBox::ImplErase(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, bool bHighlight, bool bHasOpenPopup)
|
|
{
|
|
// the background of non NWF buttons is painted in a constant color
|
|
// to have the same highlight color (transparency in DrawSelectionBackground())
|
|
// items with open popups will also painted using a constant color
|
|
if (!mpData->mbNativeButtons &&
|
|
(bHighlight || !(GetStyle() & WB_3DLOOK)))
|
|
{
|
|
if (GetStyle() & WB_3DLOOK)
|
|
{
|
|
rRenderContext.Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR);
|
|
rRenderContext.SetLineColor();
|
|
if (bHasOpenPopup)
|
|
// choose the same color as the popup will use
|
|
rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetFaceGradientColor());
|
|
else
|
|
rRenderContext.SetFillColor(COL_WHITE);
|
|
|
|
rRenderContext.DrawRect(rRect);
|
|
rRenderContext.Pop();
|
|
}
|
|
else
|
|
ImplDrawBackground(rRenderContext, rRect);
|
|
}
|
|
else
|
|
ImplDrawBackground(rRenderContext, rRect);
|
|
}
|
|
|
|
void ToolBox::ImplDrawBorder(vcl::RenderContext& rRenderContext)
|
|
{
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
long nDX = mnDX;
|
|
long nDY = mnDY;
|
|
|
|
ImplDockingWindowWrapper* pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper(this);
|
|
|
|
// draw borders for ordinary toolbars only (not dockable), do not draw borders for toolbars with WB_NOSHADOW bit set,
|
|
// e.g. Calc's formulabar
|
|
|
|
if( pWrapper || mnWinStyle & WB_NOSHADOW )
|
|
return;
|
|
|
|
if (meAlign == WindowAlign::Bottom)
|
|
{
|
|
// draw bottom border
|
|
rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
|
|
rRenderContext.DrawLine( Point( 0, nDY-2 ), Point( nDX-1, nDY-2 ) );
|
|
rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
|
|
rRenderContext.DrawLine( Point( 0, nDY-1 ), Point( nDX-1, nDY-1 ) );
|
|
}
|
|
else
|
|
{
|
|
// draw top border
|
|
rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
|
|
rRenderContext.DrawLine( Point( 0, 0 ), Point( nDX-1, 0 ) );
|
|
rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
|
|
rRenderContext.DrawLine( Point( 0, 1 ), Point( nDX-1, 1 ) );
|
|
|
|
if (meAlign == WindowAlign::Left || meAlign == WindowAlign::Right)
|
|
{
|
|
if (meAlign == WindowAlign::Left)
|
|
{
|
|
// draw left-bottom border
|
|
rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
|
|
rRenderContext.DrawLine( Point( 0, 0 ), Point( 0, nDY-1 ) );
|
|
rRenderContext.DrawLine( Point( 0, nDY-2 ), Point( nDX-1, nDY-2 ) );
|
|
rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
|
|
rRenderContext.DrawLine( Point( 1, 1 ), Point( 1, nDY-3 ) );
|
|
rRenderContext.DrawLine( Point( 0, nDY-1 ), Point( nDX-1, nDY-1 ) );
|
|
}
|
|
else
|
|
{
|
|
// draw right-bottom border
|
|
rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
|
|
rRenderContext.DrawLine( Point( nDX-2, 0 ), Point( nDX-2, nDY-3 ) );
|
|
rRenderContext.DrawLine( Point( 0, nDY-2 ), Point( nDX-2, nDY-2 ) );
|
|
rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
|
|
rRenderContext.DrawLine( Point( nDX-1, 0 ), Point( nDX-1, nDY-1 ) );
|
|
rRenderContext.DrawLine( Point( 0, nDY-1 ), Point( nDX-1, nDY-1 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( meAlign == WindowAlign::Bottom || meAlign == WindowAlign::Top )
|
|
{
|
|
// draw right border
|
|
rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
|
|
rRenderContext.DrawLine( Point( nDX-2, 0 ), Point( nDX-2, nDY-1 ) );
|
|
rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
|
|
rRenderContext.DrawLine( Point( nDX-1, 0 ), Point( nDX-1, nDY-1 ) );
|
|
}
|
|
}
|
|
|
|
static bool ImplIsFixedControl( const ImplToolItem *pItem )
|
|
{
|
|
return ( pItem->mpWindow &&
|
|
(pItem->mbNonInteractiveWindow ||
|
|
pItem->mpWindow->GetType() == WindowType::FIXEDTEXT ||
|
|
pItem->mpWindow->GetType() == WindowType::FIXEDLINE ||
|
|
pItem->mpWindow->GetType() == WindowType::GROUPBOX) );
|
|
}
|
|
|
|
const ImplToolItem *ToolBox::ImplGetFirstClippedItem() const
|
|
{
|
|
for (auto & item : mpData->m_aItems)
|
|
{
|
|
if( item.IsClipped() )
|
|
return &item;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Size ToolBox::ImplCalcSize( ImplToolItems::size_type nCalcLines, sal_uInt16 nCalcMode )
|
|
{
|
|
long nMax;
|
|
long nLeft = 0;
|
|
long nTop = 0;
|
|
long nRight = 0;
|
|
long nBottom = 0;
|
|
Size aSize;
|
|
WindowAlign eOldAlign = meAlign;
|
|
bool bOldHorz = mbHorz;
|
|
bool bOldAssumeDocked = mpData->mbAssumeDocked;
|
|
bool bOldAssumeFloating = mpData->mbAssumeFloating;
|
|
|
|
if ( nCalcMode )
|
|
{
|
|
bool bOldFloatingMode = ImplIsFloatingMode();
|
|
|
|
mpData->mbAssumeDocked = false;
|
|
mpData->mbAssumeFloating = false;
|
|
|
|
if ( nCalcMode == TB_CALCMODE_HORZ )
|
|
{
|
|
mpData->mbAssumeDocked = true; // force non-floating mode during calculation
|
|
ImplCalcBorder( WindowAlign::Top, nLeft, nTop, nRight, nBottom );
|
|
mbHorz = true;
|
|
if ( mbHorz != bOldHorz )
|
|
meAlign = WindowAlign::Top;
|
|
}
|
|
else if ( nCalcMode == TB_CALCMODE_VERT )
|
|
{
|
|
mpData->mbAssumeDocked = true; // force non-floating mode during calculation
|
|
ImplCalcBorder( WindowAlign::Left, nLeft, nTop, nRight, nBottom );
|
|
mbHorz = false;
|
|
if ( mbHorz != bOldHorz )
|
|
meAlign = WindowAlign::Left;
|
|
}
|
|
else if ( nCalcMode == TB_CALCMODE_FLOAT )
|
|
{
|
|
mpData->mbAssumeFloating = true; // force non-floating mode during calculation
|
|
nLeft = nTop = nRight = nBottom = 0;
|
|
mbHorz = true;
|
|
if ( mbHorz != bOldHorz )
|
|
meAlign = WindowAlign::Top;
|
|
}
|
|
|
|
if ( (meAlign != eOldAlign) || (mbHorz != bOldHorz) ||
|
|
(ImplIsFloatingMode() != bOldFloatingMode ) )
|
|
mbCalc = true;
|
|
}
|
|
else
|
|
ImplCalcBorder( meAlign, nLeft, nTop, nRight, nBottom );
|
|
|
|
ImplCalcItem();
|
|
|
|
if( !nCalcMode && ImplIsFloatingMode() )
|
|
{
|
|
aSize = ImplCalcFloatSize( nCalcLines );
|
|
}
|
|
else
|
|
{
|
|
if ( mbHorz )
|
|
{
|
|
if ( mnWinHeight > mnMaxItemHeight )
|
|
aSize.setHeight( nCalcLines * mnWinHeight );
|
|
else
|
|
aSize.setHeight( nCalcLines * mnMaxItemHeight );
|
|
|
|
if ( mbLineSpacing )
|
|
aSize.AdjustHeight((nCalcLines-1)*TB_LINESPACING );
|
|
|
|
if ( mnWinStyle & WB_BORDER )
|
|
aSize.AdjustHeight((TB_BORDER_OFFSET2*2) + nTop + nBottom );
|
|
|
|
nMax = 0;
|
|
ImplCalcBreaks( TB_MAXNOSCROLL, &nMax, mbHorz );
|
|
if ( nMax )
|
|
aSize.AdjustWidth(nMax );
|
|
|
|
if ( mnWinStyle & WB_BORDER )
|
|
aSize.AdjustWidth((TB_BORDER_OFFSET1*2) + nLeft + nRight );
|
|
}
|
|
else
|
|
{
|
|
aSize.setWidth( nCalcLines * mnMaxItemWidth );
|
|
|
|
if ( mbLineSpacing )
|
|
aSize.AdjustWidth((nCalcLines-1)*TB_LINESPACING );
|
|
|
|
if ( mnWinStyle & WB_BORDER )
|
|
aSize.AdjustWidth((TB_BORDER_OFFSET2*2) + nLeft + nRight );
|
|
|
|
nMax = 0;
|
|
ImplCalcBreaks( TB_MAXNOSCROLL, &nMax, mbHorz );
|
|
if ( nMax )
|
|
aSize.AdjustHeight(nMax );
|
|
|
|
if ( mnWinStyle & WB_BORDER )
|
|
aSize.AdjustHeight((TB_BORDER_OFFSET1*2) + nTop + nBottom );
|
|
}
|
|
}
|
|
// restore previous values
|
|
if ( nCalcMode )
|
|
{
|
|
mpData->mbAssumeDocked = bOldAssumeDocked;
|
|
mpData->mbAssumeFloating = bOldAssumeFloating;
|
|
if ( (meAlign != eOldAlign) || (mbHorz != bOldHorz) )
|
|
{
|
|
meAlign = eOldAlign;
|
|
mbHorz = bOldHorz;
|
|
mbCalc = true;
|
|
}
|
|
}
|
|
|
|
return aSize;
|
|
}
|
|
|
|
void ToolBox::ImplCalcFloatSizes()
|
|
{
|
|
if ( !maFloatSizes.empty() )
|
|
return;
|
|
|
|
// calculate the minimal size, i.e. where the biggest item just fits
|
|
long nCalcSize = 0;
|
|
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if ( item.mbVisible )
|
|
{
|
|
if ( item.mpWindow )
|
|
{
|
|
long nTempSize = item.mpWindow->GetSizePixel().Width();
|
|
if ( nTempSize > nCalcSize )
|
|
nCalcSize = nTempSize;
|
|
}
|
|
else
|
|
{
|
|
if( item.maItemSize.Width() > nCalcSize )
|
|
nCalcSize = item.maItemSize.Width();
|
|
}
|
|
}
|
|
}
|
|
|
|
// calc an upper bound for ImplCalcBreaks below
|
|
long upperBoundWidth = nCalcSize * mpData->m_aItems.size();
|
|
|
|
ImplToolItems::size_type nLines;
|
|
ImplToolItems::size_type nCalcLines;
|
|
ImplToolItems::size_type nTempLines;
|
|
long nMaxLineWidth;
|
|
nCalcLines = ImplCalcBreaks( nCalcSize, &nMaxLineWidth, true );
|
|
|
|
maFloatSizes.reserve( nCalcLines );
|
|
|
|
nTempLines = nLines = nCalcLines;
|
|
while ( nLines )
|
|
{
|
|
long nHeight = ImplCalcSize( nTempLines, TB_CALCMODE_FLOAT ).Height();
|
|
|
|
ImplToolSize aSize;
|
|
aSize.mnWidth = nMaxLineWidth+(TB_BORDER_OFFSET1*2);
|
|
aSize.mnHeight = nHeight;
|
|
aSize.mnLines = nTempLines;
|
|
maFloatSizes.push_back( aSize );
|
|
nLines--;
|
|
if ( nLines )
|
|
{
|
|
do
|
|
{
|
|
nCalcSize += mnMaxItemWidth;
|
|
nTempLines = ImplCalcBreaks( nCalcSize, &nMaxLineWidth, true );
|
|
}
|
|
while ((nCalcSize < upperBoundWidth) && (nLines < nTempLines)); // implies nTempLines>1
|
|
if ( nTempLines < nLines )
|
|
nLines = nTempLines;
|
|
}
|
|
}
|
|
}
|
|
|
|
Size ToolBox::ImplCalcFloatSize( ImplToolItems::size_type& rLines )
|
|
{
|
|
ImplCalcFloatSizes();
|
|
|
|
if ( !rLines )
|
|
{
|
|
rLines = mnFloatLines;
|
|
if ( !rLines )
|
|
rLines = mnLines;
|
|
}
|
|
|
|
sal_uInt16 i = 0;
|
|
while ( i + 1u < maFloatSizes.size() && rLines < maFloatSizes[i].mnLines )
|
|
{
|
|
i++;
|
|
}
|
|
|
|
Size aSize( maFloatSizes[i].mnWidth, maFloatSizes[i].mnHeight );
|
|
rLines = maFloatSizes[i].mnLines;
|
|
|
|
return aSize;
|
|
}
|
|
|
|
void ToolBox::ImplCalcMinMaxFloatSize( Size& rMinSize, Size& rMaxSize )
|
|
{
|
|
ImplCalcFloatSizes();
|
|
|
|
sal_uInt16 i = 0;
|
|
rMinSize = Size( maFloatSizes[i].mnWidth, maFloatSizes[i].mnHeight );
|
|
rMaxSize = Size( maFloatSizes[i].mnWidth, maFloatSizes[i].mnHeight );
|
|
while ( ++i < maFloatSizes.size() )
|
|
{
|
|
if( maFloatSizes[i].mnWidth < rMinSize.Width() )
|
|
rMinSize.setWidth( maFloatSizes[i].mnWidth );
|
|
if( maFloatSizes[i].mnHeight < rMinSize.Height() )
|
|
rMinSize.setHeight( maFloatSizes[i].mnHeight );
|
|
|
|
if( maFloatSizes[i].mnWidth > rMaxSize.Width() )
|
|
rMaxSize.setWidth( maFloatSizes[i].mnWidth );
|
|
if( maFloatSizes[i].mnHeight > rMaxSize.Height() )
|
|
rMaxSize.setHeight( maFloatSizes[i].mnHeight );
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplSetMinMaxFloatSize()
|
|
{
|
|
ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
|
|
Size aMinSize, aMaxSize;
|
|
ImplCalcMinMaxFloatSize( aMinSize, aMaxSize );
|
|
if( pWrapper )
|
|
{
|
|
pWrapper->SetMinOutputSizePixel( aMinSize );
|
|
pWrapper->SetMaxOutputSizePixel( aMaxSize );
|
|
pWrapper->ShowTitleButton( TitleButton::Menu, bool( GetMenuType() & ToolBoxMenuType::Customize) );
|
|
}
|
|
else
|
|
{
|
|
// TODO: change SetMinOutputSizePixel to be not inline
|
|
SetMinOutputSizePixel( aMinSize );
|
|
SetMaxOutputSizePixel( aMaxSize );
|
|
}
|
|
}
|
|
|
|
ToolBox::ImplToolItems::size_type ToolBox::ImplCalcLines( long nToolSize ) const
|
|
{
|
|
long nLineHeight;
|
|
|
|
if ( mbHorz )
|
|
{
|
|
if ( mnWinHeight > mnMaxItemHeight )
|
|
nLineHeight = mnWinHeight;
|
|
else
|
|
nLineHeight = mnMaxItemHeight;
|
|
}
|
|
else
|
|
nLineHeight = mnMaxItemWidth;
|
|
|
|
if ( mnWinStyle & WB_BORDER )
|
|
nToolSize -= TB_BORDER_OFFSET2*2;
|
|
|
|
if ( mbLineSpacing )
|
|
{
|
|
nLineHeight += TB_LINESPACING;
|
|
nToolSize += TB_LINESPACING;
|
|
}
|
|
|
|
// #i91917# always report at least one line
|
|
long nLines = nToolSize/nLineHeight;
|
|
if( nLines < 1 )
|
|
nLines = 1;
|
|
|
|
return nLines;
|
|
}
|
|
|
|
sal_uInt16 ToolBox::ImplTestLineSize( const Point& rPos ) const
|
|
{
|
|
if ( !ImplIsFloatingMode() &&
|
|
(!mbScroll || (mnLines > 1) || (mnCurLines > mnVisLines)) )
|
|
{
|
|
WindowAlign eAlign = GetAlign();
|
|
|
|
if ( eAlign == WindowAlign::Left )
|
|
{
|
|
if ( rPos.X() > mnDX-DOCK_LINEOFFSET )
|
|
return DOCK_LINEHSIZE | DOCK_LINERIGHT;
|
|
}
|
|
else if ( eAlign == WindowAlign::Top )
|
|
{
|
|
if ( rPos.Y() > mnDY-DOCK_LINEOFFSET )
|
|
return DOCK_LINEVSIZE | DOCK_LINEBOTTOM;
|
|
}
|
|
else if ( eAlign == WindowAlign::Right )
|
|
{
|
|
if ( rPos.X() < DOCK_LINEOFFSET )
|
|
return DOCK_LINEHSIZE | DOCK_LINELEFT;
|
|
}
|
|
else if ( eAlign == WindowAlign::Bottom )
|
|
{
|
|
if ( rPos.Y() < DOCK_LINEOFFSET )
|
|
return DOCK_LINEVSIZE | DOCK_LINETOP;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ToolBox::ImplLineSizing( const Point& rPos, tools::Rectangle& rRect, sal_uInt16 nLineMode )
|
|
{
|
|
bool bHorz;
|
|
long nOneLineSize;
|
|
long nCurSize;
|
|
long nMaxSize;
|
|
long nSize;
|
|
Size aSize;
|
|
|
|
if ( nLineMode & DOCK_LINERIGHT )
|
|
{
|
|
nCurSize = rPos.X() - rRect.Left();
|
|
bHorz = false;
|
|
}
|
|
else if ( nLineMode & DOCK_LINEBOTTOM )
|
|
{
|
|
nCurSize = rPos.Y() - rRect.Top();
|
|
bHorz = true;
|
|
}
|
|
else if ( nLineMode & DOCK_LINELEFT )
|
|
{
|
|
nCurSize = rRect.Right() - rPos.X();
|
|
bHorz = false;
|
|
}
|
|
else if ( nLineMode & DOCK_LINETOP )
|
|
{
|
|
nCurSize = rRect.Bottom() - rPos.Y();
|
|
bHorz = true;
|
|
}
|
|
else {
|
|
OSL_FAIL( "ImplLineSizing: Trailing else" );
|
|
nCurSize = 0;
|
|
bHorz = false;
|
|
}
|
|
|
|
Size aWinSize = GetSizePixel();
|
|
ImplToolItems::size_type nMaxLines = std::max(mnLines, mnCurLines);
|
|
if ( nMaxLines > TB_MAXLINES )
|
|
nMaxLines = TB_MAXLINES;
|
|
if ( bHorz )
|
|
{
|
|
nOneLineSize = ImplCalcSize( 1 ).Height();
|
|
nMaxSize = - 20;
|
|
if ( nMaxSize < aWinSize.Height() )
|
|
nMaxSize = aWinSize.Height();
|
|
}
|
|
else
|
|
{
|
|
nOneLineSize = ImplCalcSize( 1 ).Width();
|
|
nMaxSize = - 20;
|
|
if ( nMaxSize < aWinSize.Width() )
|
|
nMaxSize = aWinSize.Width();
|
|
}
|
|
|
|
ImplToolItems::size_type i = 1;
|
|
if ( nCurSize <= nOneLineSize )
|
|
nSize = nOneLineSize;
|
|
else
|
|
{
|
|
nSize = 0;
|
|
while ( (nSize < nCurSize) && (i < nMaxLines) )
|
|
{
|
|
i++;
|
|
aSize = ImplCalcSize( i );
|
|
if ( bHorz )
|
|
nSize = aSize.Height();
|
|
else
|
|
nSize = aSize.Width();
|
|
if ( nSize > nMaxSize )
|
|
{
|
|
i--;
|
|
aSize = ImplCalcSize( i );
|
|
if ( bHorz )
|
|
nSize = aSize.Height();
|
|
else
|
|
nSize = aSize.Width();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( nLineMode & DOCK_LINERIGHT )
|
|
rRect.SetRight( rRect.Left()+nSize-1 );
|
|
else if ( nLineMode & DOCK_LINEBOTTOM )
|
|
rRect.SetBottom( rRect.Top()+nSize-1 );
|
|
else if ( nLineMode & DOCK_LINELEFT )
|
|
rRect.SetLeft( rRect.Right()-nSize );
|
|
else
|
|
rRect.SetTop( rRect.Bottom()-nSize );
|
|
|
|
mnDockLines = i;
|
|
}
|
|
|
|
ImplTBDragMgr::ImplTBDragMgr()
|
|
: mpDragBox(nullptr)
|
|
, mnLineMode(0)
|
|
, mnStartLines(0)
|
|
{
|
|
maAccel.InsertItem( KEY_RETURN, vcl::KeyCode( KEY_RETURN ) );
|
|
maAccel.InsertItem( KEY_ESCAPE, vcl::KeyCode( KEY_ESCAPE ) );
|
|
maAccel.SetSelectHdl( LINK( this, ImplTBDragMgr, SelectHdl ) );
|
|
}
|
|
|
|
void ImplTBDragMgr::StartDragging( ToolBox* pToolBox,
|
|
const Point& rPos, const tools::Rectangle& rRect,
|
|
sal_uInt16 nDragLineMode )
|
|
{
|
|
mpDragBox = pToolBox;
|
|
pToolBox->CaptureMouse();
|
|
pToolBox->mbDragging = true;
|
|
Application::InsertAccel( &maAccel );
|
|
|
|
mnLineMode = nDragLineMode;
|
|
mnStartLines = pToolBox->mnDockLines;
|
|
|
|
// calculate MouseOffset
|
|
maMouseOff.setX( rRect.Left() - rPos.X() );
|
|
maMouseOff.setY( rRect.Top() - rPos.Y() );
|
|
maRect = rRect;
|
|
maStartRect = rRect;
|
|
pToolBox->ShowTracking( maRect );
|
|
}
|
|
|
|
void ImplTBDragMgr::Dragging( const Point& rPos )
|
|
{
|
|
mpDragBox->ImplLineSizing( rPos, maRect, mnLineMode );
|
|
Point aOff = mpDragBox->OutputToScreenPixel( Point() );
|
|
maRect.Move( aOff.X(), aOff.Y() );
|
|
mpDragBox->Docking( rPos, maRect );
|
|
maRect.Move( -aOff.X(), -aOff.Y() );
|
|
mpDragBox->ShowTracking( maRect );
|
|
}
|
|
|
|
void ImplTBDragMgr::EndDragging( bool bOK )
|
|
{
|
|
mpDragBox->HideTracking();
|
|
if (mpDragBox->IsMouseCaptured())
|
|
mpDragBox->ReleaseMouse();
|
|
mpDragBox->mbDragging = false;
|
|
Application::RemoveAccel( &maAccel );
|
|
|
|
if ( !bOK )
|
|
{
|
|
mpDragBox->mnDockLines = mnStartLines;
|
|
mpDragBox->EndDocking( maStartRect, false );
|
|
}
|
|
else
|
|
mpDragBox->EndDocking( maRect, false );
|
|
mnStartLines = 0;
|
|
|
|
mpDragBox = nullptr;
|
|
}
|
|
|
|
IMPL_LINK( ImplTBDragMgr, SelectHdl, Accelerator&, rAccel, void )
|
|
{
|
|
if ( rAccel.GetCurItemId() == KEY_ESCAPE )
|
|
EndDragging( false );
|
|
else
|
|
EndDragging();
|
|
}
|
|
|
|
void ToolBox::ImplInitToolBoxData()
|
|
{
|
|
// initialize variables
|
|
ImplGetWindowImpl()->mbToolBox = true;
|
|
mpData.reset(new ImplToolBoxPrivateData);
|
|
|
|
mpFloatWin = nullptr;
|
|
mnDX = 0;
|
|
mnDY = 0;
|
|
mnMaxItemWidth = 0;
|
|
mnMaxItemHeight = 0;
|
|
mnWinHeight = 0;
|
|
mnLeftBorder = 0;
|
|
mnTopBorder = 0;
|
|
mnRightBorder = 0;
|
|
mnBottomBorder = 0;
|
|
mnLastResizeDY = 0;
|
|
mnOutStyle = TOOLBOX_STYLE_FLAT; // force flat buttons since NWF
|
|
mnHighItemId = 0;
|
|
mnCurItemId = 0;
|
|
mnDownItemId = 0;
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
mnLines = 1;
|
|
mnCurLine = 1;
|
|
mnCurLines = 1;
|
|
mnVisLines = 1;
|
|
mnFloatLines = 0;
|
|
mnDockLines = 0;
|
|
mnMouseModifier = 0;
|
|
mbDrag = false;
|
|
mbUpper = false;
|
|
mbLower = false;
|
|
mbIn = false;
|
|
mbCalc = true;
|
|
mbFormat = false;
|
|
mbFullPaint = false;
|
|
mbHorz = true;
|
|
mbScroll = false;
|
|
mbLastFloatMode = false;
|
|
mbCustomize = false;
|
|
mbDragging = false;
|
|
mbIsKeyEvent = false;
|
|
mbChangingHighlight = false;
|
|
mbImagesMirrored = false;
|
|
mbLineSpacing = false;
|
|
mbIsArranged = false;
|
|
meButtonType = ButtonType::SYMBOLONLY;
|
|
meAlign = WindowAlign::Top;
|
|
meDockAlign = WindowAlign::Top;
|
|
meLastStyle = PointerStyle::Arrow;
|
|
mnWinStyle = 0;
|
|
meLayoutMode = ToolBoxLayoutMode::Normal;
|
|
meTextPosition = ToolBoxTextPosition::Right;
|
|
mnLastFocusItemId = 0;
|
|
mnActivateCount = 0;
|
|
mnImagesRotationAngle = 0;
|
|
|
|
mpStatusListener = new VclStatusListener<ToolBox>(this, ".uno:ImageOrientation");
|
|
mpStatusListener->startListening();
|
|
|
|
mpIdle.reset(new Idle("vcl::ToolBox maIdle update"));
|
|
mpIdle->SetPriority( TaskPriority::RESIZE );
|
|
mpIdle->SetInvokeHandler( LINK( this, ToolBox, ImplUpdateHdl ) );
|
|
|
|
// set timeout and handler for dropdown items
|
|
mpData->maDropdownTimer.SetTimeout( 250 );
|
|
mpData->maDropdownTimer.SetInvokeHandler( LINK( this, ToolBox, ImplDropdownLongClickHdl ) );
|
|
mpData->maDropdownTimer.SetDebugName( "vcl::ToolBox mpData->maDropdownTimer" );
|
|
}
|
|
|
|
void ToolBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
|
|
{
|
|
// initialize variables
|
|
mbScroll = (nStyle & WB_SCROLL) != 0;
|
|
mnWinStyle = nStyle;
|
|
|
|
DockingWindow::ImplInit( pParent, nStyle & ~WB_BORDER );
|
|
|
|
// dockingwindow's ImplInit removes some bits, so restore them here to allow keyboard handling for toolbars
|
|
ImplGetWindowImpl()->mnStyle |= WB_TABSTOP|WB_NODIALOGCONTROL; // always set WB_TABSTOP for ToolBars
|
|
ImplGetWindowImpl()->mnStyle &= ~WB_DIALOGCONTROL;
|
|
|
|
ImplInitSettings(true, true, true);
|
|
}
|
|
|
|
void ToolBox::ApplyForegroundSettings(vcl::RenderContext& rRenderContext, const StyleSettings& rStyleSettings)
|
|
{
|
|
Color aColor;
|
|
if (IsControlForeground())
|
|
aColor = GetControlForeground();
|
|
else if (Window::GetStyle() & WB_3DLOOK)
|
|
aColor = rStyleSettings.GetButtonTextColor();
|
|
else
|
|
aColor = rStyleSettings.GetWindowTextColor();
|
|
rRenderContext.SetTextColor(aColor);
|
|
rRenderContext.SetTextFillColor();
|
|
}
|
|
|
|
void ToolBox::ApplyBackgroundSettings(vcl::RenderContext& rRenderContext, const StyleSettings& rStyleSettings)
|
|
{
|
|
if (IsControlBackground())
|
|
{
|
|
rRenderContext.SetBackground(GetControlBackground());
|
|
SetPaintTransparent(false);
|
|
SetParentClipMode();
|
|
}
|
|
else
|
|
{
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Entire)
|
|
|| (GetAlign() == WindowAlign::Top && !Application::GetSettings().GetStyleSettings().GetPersonaHeader().IsEmpty())
|
|
|| (GetAlign() == WindowAlign::Bottom && !Application::GetSettings().GetStyleSettings().GetPersonaFooter().IsEmpty()))
|
|
{
|
|
rRenderContext.SetBackground();
|
|
rRenderContext.SetTextColor(rStyleSettings.GetToolTextColor());
|
|
SetPaintTransparent(true);
|
|
SetParentClipMode(ParentClipMode::NoClip);
|
|
mpData->maDisplayBackground = Wallpaper(rStyleSettings.GetFaceColor());
|
|
}
|
|
else
|
|
{
|
|
Color aColor;
|
|
if (Window::GetStyle() & WB_3DLOOK)
|
|
aColor = rStyleSettings.GetFaceColor();
|
|
else
|
|
aColor = rStyleSettings.GetWindowColor();
|
|
rRenderContext.SetBackground(aColor);
|
|
SetPaintTransparent(false);
|
|
SetParentClipMode();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ApplySettings(vcl::RenderContext& rRenderContext)
|
|
{
|
|
mpData->mbNativeButtons = rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button);
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
|
|
ApplyControlFont(rRenderContext, rStyleSettings.GetToolFont());
|
|
ApplyForegroundSettings(rRenderContext, rStyleSettings);
|
|
ApplyBackgroundSettings(rRenderContext, rStyleSettings);
|
|
}
|
|
|
|
void ToolBox::ImplInitSettings(bool bFont, bool bForeground, bool bBackground)
|
|
{
|
|
mpData->mbNativeButtons = IsNativeControlSupported( ControlType::Toolbar, ControlPart::Button );
|
|
|
|
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
|
|
|
|
if (bFont)
|
|
ApplyControlFont(*this, rStyleSettings.GetToolFont());
|
|
if (bForeground || bFont)
|
|
ApplyForegroundSettings(*this, rStyleSettings);
|
|
if (bBackground)
|
|
{
|
|
ApplyBackgroundSettings(*this, rStyleSettings);
|
|
EnableChildTransparentMode(IsPaintTransparent());
|
|
}
|
|
}
|
|
|
|
void ToolBox::doDeferredInit(WinBits nBits)
|
|
{
|
|
VclPtr<vcl::Window> pParent = mpDialogParent;
|
|
mpDialogParent = nullptr;
|
|
ImplInit(pParent, nBits);
|
|
mbIsDeferredInit = false;
|
|
}
|
|
|
|
void ToolBox::queue_resize(StateChangedType eReason)
|
|
{
|
|
Window::queue_resize(eReason);
|
|
}
|
|
|
|
ToolBox::ToolBox( vcl::Window* pParent, WinBits nStyle ) :
|
|
DockingWindow( WindowType::TOOLBOX )
|
|
{
|
|
ImplInitToolBoxData();
|
|
ImplInit( pParent, nStyle );
|
|
}
|
|
|
|
ToolBox::ToolBox(vcl::Window* pParent, const OString& rID,
|
|
const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame)
|
|
: DockingWindow(WindowType::TOOLBOX)
|
|
{
|
|
ImplInitToolBoxData();
|
|
|
|
loadUI(pParent, rID, rUIXMLDescription, rFrame);
|
|
|
|
// calculate size of floating windows and switch if the
|
|
// toolbox is initially in floating mode
|
|
if ( ImplIsFloatingMode() )
|
|
mbHorz = true;
|
|
else
|
|
Resize();
|
|
|
|
if (!(GetStyle() & WB_HIDE))
|
|
Show();
|
|
}
|
|
|
|
ToolBox::~ToolBox()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void ToolBox::dispose()
|
|
{
|
|
// #103005# make sure our activate/deactivate balance is right
|
|
while( mnActivateCount > 0 )
|
|
Deactivate();
|
|
|
|
// terminate popupmode if the floating window is
|
|
// still connected
|
|
if ( mpFloatWin )
|
|
mpFloatWin->EndPopupMode( FloatWinPopupEndFlags::Cancel );
|
|
mpFloatWin = nullptr;
|
|
|
|
// delete private data
|
|
mpData.reset();
|
|
|
|
ImplSVData* pSVData = ImplGetSVData();
|
|
delete pSVData->maCtrlData.mpTBDragMgr;
|
|
pSVData->maCtrlData.mpTBDragMgr = nullptr;
|
|
|
|
if (mpStatusListener.is())
|
|
mpStatusListener->dispose();
|
|
|
|
mpFloatWin.clear();
|
|
|
|
mpIdle.reset();
|
|
|
|
DockingWindow::dispose();
|
|
}
|
|
|
|
ImplToolItem* ToolBox::ImplGetItem( sal_uInt16 nItemId ) const
|
|
{
|
|
if (!mpData)
|
|
return nullptr;
|
|
|
|
for (auto & item : mpData->m_aItems)
|
|
{
|
|
if ( item.mnId == nItemId )
|
|
return &item;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void ImplAddButtonBorder( long &rWidth, long& rHeight, bool bNativeButtons )
|
|
{
|
|
rWidth += SMALLBUTTON_HSIZE;
|
|
rHeight += SMALLBUTTON_VSIZE;
|
|
|
|
if( bNativeButtons )
|
|
{
|
|
// give more border space for rounded buttons
|
|
rWidth += 2;
|
|
rHeight += 4;
|
|
}
|
|
}
|
|
|
|
bool ToolBox::ImplCalcItem()
|
|
{
|
|
// recalc required ?
|
|
if ( !mbCalc )
|
|
return false;
|
|
|
|
ImplDisableFlatButtons();
|
|
|
|
OutputDevice *pDefault = Application::GetDefaultDevice();
|
|
float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;
|
|
|
|
long nDefWidth;
|
|
long nDefHeight;
|
|
long nMaxWidth = 0;
|
|
long nMaxHeight = 0;
|
|
long nMinWidth = 6;
|
|
long nMinHeight = 6;
|
|
long nDropDownArrowWidth = TB_DROPDOWNARROWWIDTH * fScaleFactor;
|
|
#ifdef IOS
|
|
nDropDownArrowWidth *= 3;
|
|
#endif
|
|
|
|
// set defaults if image or text is needed but empty
|
|
nDefWidth = GetDefaultImageSize().Width();
|
|
nDefHeight = GetDefaultImageSize().Height();
|
|
|
|
mnWinHeight = 0;
|
|
// determine minimum size necessary in NWF
|
|
{
|
|
tools::Rectangle aRect( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
|
|
tools::Rectangle aReg( aRect );
|
|
ImplControlValue aVal;
|
|
tools::Rectangle aNativeBounds, aNativeContent;
|
|
if( IsNativeControlSupported( ControlType::Toolbar, ControlPart::Button ) )
|
|
{
|
|
if( GetNativeControlRegion( ControlType::Toolbar, ControlPart::Button,
|
|
aReg,
|
|
ControlState::ENABLED | ControlState::ROLLOVER,
|
|
aVal,
|
|
aNativeBounds, aNativeContent ) )
|
|
{
|
|
aRect = aNativeBounds;
|
|
if( aRect.GetWidth() > nMinWidth )
|
|
nMinWidth = aRect.GetWidth();
|
|
if( aRect.GetHeight() > nMinHeight )
|
|
nMinHeight = aRect.GetHeight();
|
|
if( nDropDownArrowWidth < nMinWidth )
|
|
nDropDownArrowWidth = nMinWidth;
|
|
if( nMinWidth > mpData->mnMenuButtonWidth )
|
|
mpData->mnMenuButtonWidth = nMinWidth;
|
|
else if( nMinWidth < TB_MENUBUTTON_SIZE )
|
|
mpData->mnMenuButtonWidth = TB_MENUBUTTON_SIZE;
|
|
}
|
|
}
|
|
|
|
// also calculate the area for comboboxes, drop down list boxes and spinfields
|
|
// as these are often inserted into toolboxes; set mnWinHeight to the
|
|
// greater of those values to prevent toolbar flickering (#i103385#)
|
|
aRect = tools::Rectangle( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
|
|
aReg = aRect;
|
|
if( GetNativeControlRegion( ControlType::Combobox, ControlPart::Entire,
|
|
aReg,
|
|
ControlState::ENABLED | ControlState::ROLLOVER,
|
|
aVal,
|
|
aNativeBounds, aNativeContent ) )
|
|
{
|
|
aRect = aNativeBounds;
|
|
if( aRect.GetHeight() > mnWinHeight )
|
|
mnWinHeight = aRect.GetHeight();
|
|
}
|
|
aRect = tools::Rectangle( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
|
|
aReg = aRect;
|
|
if( GetNativeControlRegion( ControlType::Listbox, ControlPart::Entire,
|
|
aReg,
|
|
ControlState::ENABLED | ControlState::ROLLOVER,
|
|
aVal,
|
|
aNativeBounds, aNativeContent ) )
|
|
{
|
|
aRect = aNativeBounds;
|
|
if( aRect.GetHeight() > mnWinHeight )
|
|
mnWinHeight = aRect.GetHeight();
|
|
}
|
|
aRect = tools::Rectangle( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
|
|
aReg = aRect;
|
|
if( GetNativeControlRegion( ControlType::Spinbox, ControlPart::Entire,
|
|
aReg,
|
|
ControlState::ENABLED | ControlState::ROLLOVER,
|
|
aVal,
|
|
aNativeBounds, aNativeContent ) )
|
|
{
|
|
aRect = aNativeBounds;
|
|
if( aRect.GetHeight() > mnWinHeight )
|
|
mnWinHeight = aRect.GetHeight();
|
|
}
|
|
}
|
|
|
|
if ( ! mpData->m_aItems.empty() )
|
|
{
|
|
for (auto & item : mpData->m_aItems)
|
|
{
|
|
item.mbVisibleText = false; // indicates if text will definitely be drawn, influences dropdown pos
|
|
|
|
if ( item.meType == ToolBoxItemType::BUTTON )
|
|
{
|
|
bool bImage;
|
|
bool bText;
|
|
|
|
// check if image and/or text exists
|
|
bImage = !!item.maImage;
|
|
bText = !item.maText.isEmpty();
|
|
ButtonType tmpButtonType = determineButtonType( &item, meButtonType ); // default to toolbox setting
|
|
if ( bImage || bText )
|
|
{
|
|
|
|
item.mbEmptyBtn = false;
|
|
|
|
if ( tmpButtonType == ButtonType::SYMBOLONLY )
|
|
{
|
|
// we're drawing images only
|
|
if ( bImage || !bText )
|
|
{
|
|
item.maItemSize = item.maImage.GetSizePixel();
|
|
}
|
|
else
|
|
{
|
|
item.maItemSize = Size( GetCtrlTextWidth( item.maText )+TB_TEXTOFFSET,
|
|
GetTextHeight() );
|
|
item.mbVisibleText = true;
|
|
}
|
|
}
|
|
else if ( tmpButtonType == ButtonType::TEXT )
|
|
{
|
|
// we're drawing text only
|
|
if ( bText || !bImage )
|
|
{
|
|
item.maItemSize = Size( GetCtrlTextWidth( item.maText )+TB_TEXTOFFSET,
|
|
GetTextHeight() );
|
|
item.mbVisibleText = true;
|
|
}
|
|
else
|
|
{
|
|
item.maItemSize = item.maImage.GetSizePixel();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// we're drawing images and text
|
|
item.maItemSize.setWidth( bText ? GetCtrlTextWidth( item.maText )+TB_TEXTOFFSET : 0 );
|
|
item.maItemSize.setHeight( bText ? GetTextHeight() : 0 );
|
|
|
|
if ( meTextPosition == ToolBoxTextPosition::Right )
|
|
{
|
|
// leave space between image and text
|
|
if( bText )
|
|
item.maItemSize.AdjustWidth(TB_IMAGETEXTOFFSET );
|
|
|
|
// image and text side by side
|
|
item.maItemSize.AdjustWidth(item.maImage.GetSizePixel().Width() );
|
|
if ( item.maImage.GetSizePixel().Height() > item.maItemSize.Height() )
|
|
item.maItemSize.setHeight( item.maImage.GetSizePixel().Height() );
|
|
}
|
|
else
|
|
{
|
|
// leave space between image and text
|
|
if( bText )
|
|
item.maItemSize.AdjustHeight(TB_IMAGETEXTOFFSET );
|
|
|
|
// text below image
|
|
item.maItemSize.AdjustHeight(item.maImage.GetSizePixel().Height() );
|
|
if ( item.maImage.GetSizePixel().Width() > item.maItemSize.Width() )
|
|
item.maItemSize.setWidth( item.maImage.GetSizePixel().Width() );
|
|
}
|
|
|
|
item.mbVisibleText = bText;
|
|
}
|
|
}
|
|
else
|
|
{ // no image and no text
|
|
item.maItemSize = Size( nDefWidth, nDefHeight );
|
|
item.mbEmptyBtn = true;
|
|
}
|
|
|
|
// save the content size
|
|
item.maContentSize = item.maItemSize;
|
|
|
|
// if required, take window height into consideration
|
|
if ( item.mpWindow )
|
|
{
|
|
long nHeight = item.mpWindow->GetSizePixel().Height();
|
|
if ( nHeight > mnWinHeight )
|
|
mnWinHeight = nHeight;
|
|
}
|
|
|
|
// add in drop down arrow
|
|
if( item.mnBits & ToolBoxItemBits::DROPDOWN )
|
|
{
|
|
item.maItemSize.AdjustWidth(nDropDownArrowWidth );
|
|
item.mnDropDownArrowWidth = nDropDownArrowWidth;
|
|
}
|
|
|
|
// text items will be rotated in vertical mode
|
|
// -> swap width and height
|
|
if( item.mbVisibleText && !mbHorz )
|
|
{
|
|
long tmp = item.maItemSize.Width();
|
|
item.maItemSize.setWidth( item.maItemSize.Height() );
|
|
item.maItemSize.setHeight( tmp );
|
|
|
|
tmp = item.maContentSize.Width();
|
|
item.maContentSize.setWidth( item.maContentSize.Height() );
|
|
item.maContentSize.setHeight( tmp );
|
|
}
|
|
}
|
|
else if ( item.meType == ToolBoxItemType::SPACE )
|
|
{
|
|
item.maItemSize = Size( nDefWidth, nDefHeight );
|
|
item.maContentSize = item.maItemSize;
|
|
}
|
|
|
|
if ( item.meType == ToolBoxItemType::BUTTON || item.meType == ToolBoxItemType::SPACE )
|
|
{
|
|
// add borders
|
|
long w = item.maItemSize.Width();
|
|
long h = item.maItemSize.Height();
|
|
ImplAddButtonBorder( w, h, mpData->mbNativeButtons );
|
|
item.maItemSize.setWidth(w);
|
|
item.maItemSize.setHeight(h);
|
|
|
|
if( item.meType == ToolBoxItemType::BUTTON )
|
|
{
|
|
long nMinW = std::max(nMinWidth, item.maMinimalItemSize.Width());
|
|
long nMinH = std::max(nMinHeight, item.maMinimalItemSize.Height());
|
|
|
|
long nGrowContentWidth = 0;
|
|
long nGrowContentHeight = 0;
|
|
|
|
if( item.maItemSize.Width() < nMinW )
|
|
{
|
|
nGrowContentWidth = nMinW - item.maItemSize.Width();
|
|
item.maItemSize.setWidth( nMinW );
|
|
}
|
|
if( item.maItemSize.Height() < nMinH )
|
|
{
|
|
nGrowContentHeight = nMinH - item.maItemSize.Height();
|
|
item.maItemSize.setHeight( nMinH );
|
|
}
|
|
|
|
// grow the content size by the additional available space
|
|
item.maContentSize.AdjustWidth(nGrowContentWidth );
|
|
item.maContentSize.AdjustHeight(nGrowContentHeight );
|
|
}
|
|
|
|
// keep track of max item size
|
|
if ( item.maItemSize.Width() > nMaxWidth )
|
|
nMaxWidth = item.maItemSize.Width();
|
|
if ( item.maItemSize.Height() > nMaxHeight )
|
|
nMaxHeight = item.maItemSize.Height();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nMaxWidth = nDefWidth;
|
|
nMaxHeight = nDefHeight;
|
|
|
|
ImplAddButtonBorder( nMaxWidth, nMaxHeight, mpData->mbNativeButtons );
|
|
}
|
|
|
|
if( !ImplIsFloatingMode() && GetToolboxButtonSize() != ToolBoxButtonSize::DontCare
|
|
&& ( meTextPosition == ToolBoxTextPosition::Right ) )
|
|
{
|
|
// make sure all vertical toolbars have the same width and horizontal have the same height
|
|
// this depends on the used button sizes
|
|
// as this is used for alignment of multiple toolbars
|
|
// it is only required for docked toolbars
|
|
|
|
long nFixedWidth = nDefWidth+nDropDownArrowWidth;
|
|
long nFixedHeight = nDefHeight;
|
|
ImplAddButtonBorder( nFixedWidth, nFixedHeight, mpData->mbNativeButtons );
|
|
|
|
if( mbHorz )
|
|
nMaxHeight = nFixedHeight;
|
|
else
|
|
nMaxWidth = nFixedWidth;
|
|
}
|
|
|
|
mbCalc = false;
|
|
mbFormat = true;
|
|
|
|
// do we have to recalc the sizes ?
|
|
if ( (nMaxWidth != mnMaxItemWidth) || (nMaxHeight != mnMaxItemHeight) )
|
|
{
|
|
mnMaxItemWidth = nMaxWidth;
|
|
mnMaxItemHeight = nMaxHeight;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
ToolBox::ImplToolItems::size_type ToolBox::ImplCalcBreaks( long nWidth, long* pMaxLineWidth, bool bCalcHorz ) const
|
|
{
|
|
sal_uLong nLineStart = 0;
|
|
sal_uLong nGroupStart = 0;
|
|
long nLineWidth = 0;
|
|
long nCurWidth;
|
|
long nLastGroupLineWidth = 0;
|
|
long nMaxLineWidth = 0;
|
|
ImplToolItems::size_type nLines = 1;
|
|
bool bWindow;
|
|
bool bBreak = false;
|
|
long nWidthTotal = nWidth;
|
|
long nMenuWidth = 0;
|
|
|
|
// when docked the menubutton will be in the first line
|
|
if( IsMenuEnabled() && !ImplIsFloatingMode() )
|
|
nMenuWidth = mpData->maMenubuttonItem.maItemSize.Width();
|
|
|
|
// we need to know which item is the last visible one to be able to add
|
|
// the menu width in case we are unable to show all the items
|
|
ImplToolItems::iterator it, lastVisible;
|
|
for ( it = mpData->m_aItems.begin(); it != mpData->m_aItems.end(); ++it )
|
|
{
|
|
if ( it->mbVisible )
|
|
lastVisible = it;
|
|
}
|
|
|
|
it = mpData->m_aItems.begin();
|
|
while ( it != mpData->m_aItems.end() )
|
|
{
|
|
it->mbBreak = bBreak;
|
|
bBreak = false;
|
|
|
|
if ( it->mbVisible )
|
|
{
|
|
bWindow = false;
|
|
bBreak = false;
|
|
nCurWidth = 0;
|
|
|
|
if ( it->meType == ToolBoxItemType::BUTTON || it->meType == ToolBoxItemType::SPACE )
|
|
{
|
|
if ( bCalcHorz )
|
|
nCurWidth = it->maItemSize.Width();
|
|
else
|
|
nCurWidth = it->maItemSize.Height();
|
|
|
|
if ( it->mpWindow && bCalcHorz )
|
|
{
|
|
long nWinItemWidth = it->mpWindow->GetSizePixel().Width();
|
|
if ( !mbScroll || (nWinItemWidth <= nWidthTotal) )
|
|
{
|
|
nCurWidth = nWinItemWidth;
|
|
bWindow = true;
|
|
}
|
|
else
|
|
{
|
|
if ( it->mbEmptyBtn )
|
|
{
|
|
nCurWidth = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// in case we are able to show all the items, we do not want
|
|
// to show the toolbar's menu; otherwise yes
|
|
if ( ( ( it == lastVisible ) && (nLineWidth+nCurWidth > nWidthTotal) && mbScroll ) ||
|
|
( ( it != lastVisible ) && (nLineWidth+nCurWidth+nMenuWidth > nWidthTotal) && mbScroll ) )
|
|
bBreak = true;
|
|
}
|
|
else if ( it->meType == ToolBoxItemType::SEPARATOR )
|
|
{
|
|
nCurWidth = it->mnSepSize;
|
|
if ( !ImplIsFloatingMode() && ( it != lastVisible ) && (nLineWidth+nCurWidth+nMenuWidth > nWidthTotal) )
|
|
bBreak = true;
|
|
}
|
|
// treat breaks as separators, except when using old style toolbars (ie. no menu button)
|
|
else if ( (it->meType == ToolBoxItemType::BREAK) && !IsMenuEnabled() )
|
|
bBreak = true;
|
|
|
|
if ( bBreak )
|
|
{
|
|
nLines++;
|
|
|
|
// Add break before the entire group or take group apart?
|
|
if ( (it->meType == ToolBoxItemType::BREAK) ||
|
|
(nLineStart == nGroupStart) )
|
|
{
|
|
if ( nLineWidth > nMaxLineWidth )
|
|
nMaxLineWidth = nLineWidth;
|
|
|
|
nLineWidth = 0;
|
|
nLineStart = it - mpData->m_aItems.begin();
|
|
nGroupStart = nLineStart;
|
|
it->mbBreak = true;
|
|
bBreak = false;
|
|
}
|
|
else
|
|
{
|
|
if ( nLastGroupLineWidth > nMaxLineWidth )
|
|
nMaxLineWidth = nLastGroupLineWidth;
|
|
|
|
// if the break is added before the group, set it to
|
|
// beginning of line and re-calculate
|
|
nLineWidth = 0;
|
|
nLineStart = nGroupStart;
|
|
it = mpData->m_aItems.begin() + nGroupStart;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ImplIsFloatingMode() || !IsMenuEnabled() ) // no group breaking when being docked single-line
|
|
{
|
|
if ( (it->meType != ToolBoxItemType::BUTTON) || bWindow )
|
|
{
|
|
// found separator or break
|
|
nLastGroupLineWidth = nLineWidth;
|
|
nGroupStart = it - mpData->m_aItems.begin();
|
|
if ( !bWindow )
|
|
nGroupStart++;
|
|
}
|
|
}
|
|
}
|
|
|
|
nLineWidth += nCurWidth;
|
|
}
|
|
|
|
++it;
|
|
}
|
|
|
|
if ( pMaxLineWidth )
|
|
{
|
|
if ( nLineWidth > nMaxLineWidth )
|
|
nMaxLineWidth = nLineWidth;
|
|
|
|
if( ImplIsFloatingMode() && !ImplIsInPopupMode() )
|
|
{
|
|
// leave enough space to display buttons in the decoration
|
|
long aMinWidth = 2 * GetSettings().GetStyleSettings().GetFloatTitleHeight();
|
|
if( nMaxLineWidth < aMinWidth )
|
|
nMaxLineWidth = aMinWidth;
|
|
}
|
|
*pMaxLineWidth = nMaxLineWidth;
|
|
}
|
|
|
|
return nLines;
|
|
}
|
|
|
|
Size ToolBox::ImplGetOptimalFloatingSize()
|
|
{
|
|
if( !ImplIsFloatingMode() )
|
|
return Size();
|
|
|
|
Size aCurrentSize( mnDX, mnDY );
|
|
Size aSize1( aCurrentSize );
|
|
Size aSize2( aCurrentSize );
|
|
|
|
// try to preserve current height
|
|
|
|
// calc number of floating lines for current window height
|
|
ImplToolItems::size_type nFloatLinesHeight = ImplCalcLines( mnDY );
|
|
// calc window size according to this number
|
|
aSize1 = ImplCalcFloatSize( nFloatLinesHeight );
|
|
|
|
if( aCurrentSize == aSize1 )
|
|
return aSize1;
|
|
|
|
// try to preserve current width
|
|
|
|
long nLineHeight = std::max( mnWinHeight, mnMaxItemHeight );
|
|
int nBorderX = 2*TB_BORDER_OFFSET1 + mnLeftBorder + mnRightBorder;
|
|
int nBorderY = 2*TB_BORDER_OFFSET2 + mnTopBorder + mnBottomBorder;
|
|
Size aSz( aCurrentSize );
|
|
long maxX;
|
|
ImplToolItems::size_type nLines = ImplCalcBreaks( aSz.Width()-nBorderX, &maxX, mbHorz );
|
|
|
|
ImplToolItems::size_type manyLines = 1000;
|
|
Size aMinimalFloatSize = ImplCalcFloatSize( manyLines );
|
|
|
|
aSz.setHeight( nBorderY + nLineHeight * nLines );
|
|
// line space when more than one line
|
|
if ( mbLineSpacing )
|
|
aSz.AdjustHeight((nLines-1)*TB_LINESPACING );
|
|
|
|
aSz.setWidth( nBorderX + maxX );
|
|
|
|
// avoid clipping of any items
|
|
if( aSz.Width() < aMinimalFloatSize.Width() )
|
|
aSize2 = ImplCalcFloatSize( nLines );
|
|
else
|
|
aSize2 = aSz;
|
|
|
|
if( aCurrentSize == aSize2 )
|
|
return aSize2;
|
|
|
|
// set the size with the smallest delta as the current size
|
|
long dx1 = std::abs( mnDX - aSize1.Width() );
|
|
long dy1 = std::abs( mnDY - aSize1.Height() );
|
|
|
|
long dx2 = std::abs( mnDX - aSize2.Width() );
|
|
long dy2 = std::abs( mnDY - aSize2.Height() );
|
|
|
|
if( dx1*dy1 < dx2*dy2 )
|
|
aCurrentSize = aSize1;
|
|
else
|
|
aCurrentSize = aSize2;
|
|
|
|
return aCurrentSize;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void lcl_hideDoubleSeparators( ToolBox::ImplToolItems& rItems )
|
|
{
|
|
bool bLastSep( true );
|
|
ToolBox::ImplToolItems::iterator it;
|
|
for ( it = rItems.begin(); it != rItems.end(); ++it )
|
|
{
|
|
if ( it->meType == ToolBoxItemType::SEPARATOR )
|
|
{
|
|
it->mbVisible = false;
|
|
if ( !bLastSep )
|
|
{
|
|
// check if any visible items have to appear behind it
|
|
if (std::any_of(it + 1, rItems.end(), [](const ImplToolItem& rItem) {
|
|
return (rItem.meType == ToolBoxItemType::BUTTON) && rItem.mbVisible; }))
|
|
it->mbVisible = true;
|
|
}
|
|
bLastSep = true;
|
|
}
|
|
else if ( it->mbVisible )
|
|
bLastSep = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplFormat( bool bResize )
|
|
{
|
|
// Has to re-formatted
|
|
if ( !mbFormat )
|
|
return;
|
|
|
|
mpData->ImplClearLayoutData();
|
|
|
|
// recalculate positions and sizes
|
|
tools::Rectangle aEmptyRect;
|
|
long nLineSize;
|
|
long nLeft;
|
|
long nTop;
|
|
long nMax; // width of layoutarea in pixels
|
|
ImplToolItems::size_type nFormatLine;
|
|
bool bMustFullPaint;
|
|
|
|
ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
|
|
bool bIsInPopupMode = ImplIsInPopupMode();
|
|
|
|
maFloatSizes.clear();
|
|
|
|
// compute border sizes
|
|
ImplCalcBorder( meAlign, mnLeftBorder, mnTopBorder, mnRightBorder, mnBottomBorder );
|
|
|
|
// update drag area (where the 'grip' will be placed)
|
|
tools::Rectangle aOldDragRect;
|
|
if( pWrapper )
|
|
aOldDragRect = pWrapper->GetDragArea();
|
|
ImplUpdateDragArea();
|
|
|
|
bMustFullPaint = ImplCalcItem();
|
|
|
|
// calculate new size during interactive resize or
|
|
// set computed size when formatting only
|
|
if ( ImplIsFloatingMode() )
|
|
{
|
|
if ( bResize )
|
|
mnFloatLines = ImplCalcLines( mnDY );
|
|
else
|
|
SetOutputSizePixel( ImplGetOptimalFloatingSize() );
|
|
}
|
|
|
|
// Horizontal
|
|
if ( mbHorz )
|
|
{
|
|
long nBottom;
|
|
// nLineSize: height of a single line, will fit highest item
|
|
nLineSize = mnMaxItemHeight;
|
|
|
|
if ( mnWinHeight > mnMaxItemHeight )
|
|
nLineSize = mnWinHeight;
|
|
|
|
if ( mbScroll )
|
|
{
|
|
nMax = mnDX;
|
|
mnVisLines = ImplCalcLines( mnDY );
|
|
}
|
|
else
|
|
{
|
|
// layout over all lines
|
|
mnVisLines = mnLines;
|
|
nMax = TB_MAXNOSCROLL;
|
|
}
|
|
|
|
// add in all border offsets
|
|
if ( mnWinStyle & WB_BORDER )
|
|
{
|
|
nLeft = TB_BORDER_OFFSET1 + mnLeftBorder;
|
|
nTop = TB_BORDER_OFFSET2 + mnTopBorder;
|
|
nBottom = TB_BORDER_OFFSET1 + mnBottomBorder;
|
|
nMax -= nLeft + TB_BORDER_OFFSET1 + mnRightBorder;
|
|
}
|
|
else
|
|
{
|
|
nLeft = 0;
|
|
nTop = 0;
|
|
nBottom = 0;
|
|
}
|
|
|
|
// adjust linesize if docked in single-line mode (i.e. when using a clipped item menu)
|
|
// we have to center all items in the window height
|
|
if( IsMenuEnabled() && !ImplIsFloatingMode() )
|
|
{
|
|
long nWinHeight = mnDY - nTop - nBottom;
|
|
if( nWinHeight > nLineSize )
|
|
nLineSize = nWinHeight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
long nRight;
|
|
nLineSize = mnMaxItemWidth;
|
|
|
|
if ( mbScroll )
|
|
{
|
|
mnVisLines = ImplCalcLines( mnDX );
|
|
nMax = mnDY;
|
|
}
|
|
else
|
|
{
|
|
mnVisLines = mnLines;
|
|
nMax = TB_MAXNOSCROLL;
|
|
}
|
|
|
|
if ( mnWinStyle & WB_BORDER )
|
|
{
|
|
nTop = TB_BORDER_OFFSET1 + mnTopBorder;
|
|
nLeft = TB_BORDER_OFFSET2 + mnLeftBorder;
|
|
nRight = TB_BORDER_OFFSET2 + mnRightBorder;
|
|
nMax -= nTop + TB_BORDER_OFFSET1 + mnBottomBorder;
|
|
}
|
|
else
|
|
{
|
|
nLeft = 0;
|
|
nTop = 0;
|
|
nRight = 0;
|
|
}
|
|
|
|
// adjust linesize if docked in single-line mode (i.e. when using a clipped item menu)
|
|
// we have to center all items in the window height
|
|
if( !ImplIsFloatingMode() && IsMenuEnabled() )
|
|
{
|
|
long nWinWidth = mnDX - nLeft - nRight;
|
|
if( nWinWidth > nLineSize )
|
|
nLineSize = nWinWidth;
|
|
}
|
|
}
|
|
|
|
// no calculation if the window has no size (nMax=0)
|
|
// non scrolling toolboxes must be computed though
|
|
if ( (nMax <= 0) && mbScroll )
|
|
{
|
|
mnVisLines = 1;
|
|
mnCurLine = 1;
|
|
mnCurLines = 1;
|
|
|
|
for (auto & item : mpData->m_aItems)
|
|
{
|
|
item.maRect = aEmptyRect;
|
|
}
|
|
|
|
maLowerRect = aEmptyRect;
|
|
maUpperRect = aEmptyRect;
|
|
}
|
|
else
|
|
{
|
|
// init start values
|
|
long nX = nLeft; // top-left offset
|
|
long nY = nTop;
|
|
nFormatLine = 1;
|
|
|
|
// save old scroll rectangles and reset them
|
|
tools::Rectangle aOldLowerRect = maLowerRect;
|
|
tools::Rectangle aOldUpperRect = maUpperRect;
|
|
tools::Rectangle aOldMenubuttonRect = mpData->maMenubuttonItem.maRect;
|
|
maUpperRect = aEmptyRect;
|
|
maLowerRect = aEmptyRect;
|
|
mpData->maMenubuttonItem.maRect = aEmptyRect;
|
|
|
|
// do we have any toolbox items at all ?
|
|
if ( !mpData->m_aItems.empty() || IsMenuEnabled() )
|
|
{
|
|
lcl_hideDoubleSeparators( mpData->m_aItems );
|
|
|
|
// compute line breaks and visible lines give the current window width (nMax)
|
|
// the break indicators will be stored within each item (it->mbBreak)
|
|
mnCurLines = ImplCalcBreaks( nMax, nullptr, mbHorz );
|
|
|
|
// check for scrollbar buttons or dropdown menu
|
|
// (if a menu is enabled, this will be used to store clipped
|
|
// items and no scroll buttons will appear)
|
|
if ( (!ImplIsFloatingMode() && (mnCurLines > mnVisLines) && mbScroll ) ||
|
|
IsMenuEnabled() )
|
|
{
|
|
// compute linebreaks again, incorporating scrollbar buttons
|
|
if( !IsMenuEnabled() )
|
|
{
|
|
nMax -= TB_SPIN_SIZE+TB_SPIN_OFFSET;
|
|
mnCurLines = ImplCalcBreaks( nMax, nullptr, mbHorz );
|
|
}
|
|
|
|
// compute scroll rectangles or menu button
|
|
if ( mbHorz )
|
|
{
|
|
if( IsMenuEnabled() && !ImplHasExternalMenubutton() && !bIsInPopupMode )
|
|
{
|
|
if( !ImplIsFloatingMode() )
|
|
{
|
|
mpData->maMenubuttonItem.maRect.SetRight( mnDX - 2 );
|
|
mpData->maMenubuttonItem.maRect.SetTop( nTop );
|
|
mpData->maMenubuttonItem.maRect.SetBottom( mnDY-mnBottomBorder-TB_BORDER_OFFSET2-1 );
|
|
}
|
|
else
|
|
{
|
|
mpData->maMenubuttonItem.maRect.SetRight( mnDX - mnRightBorder-TB_BORDER_OFFSET1-1 );
|
|
mpData->maMenubuttonItem.maRect.SetTop( nTop );
|
|
mpData->maMenubuttonItem.maRect.SetBottom( mnDY-mnBottomBorder-TB_BORDER_OFFSET2-1 );
|
|
}
|
|
mpData->maMenubuttonItem.maRect.SetLeft( mpData->maMenubuttonItem.maRect.Right() - mpData->mnMenuButtonWidth );
|
|
}
|
|
else
|
|
{
|
|
maUpperRect.SetLeft( nLeft+nMax+TB_SPIN_OFFSET );
|
|
maUpperRect.SetRight( maUpperRect.Left()+TB_SPIN_SIZE-1 );
|
|
maUpperRect.SetTop( nTop );
|
|
maLowerRect.SetBottom( mnDY-mnBottomBorder-TB_BORDER_OFFSET2-1 );
|
|
maLowerRect.SetLeft( maUpperRect.Left() );
|
|
maLowerRect.SetRight( maUpperRect.Right() );
|
|
maUpperRect.SetBottom( maUpperRect.Top() +
|
|
(maLowerRect.Bottom()-maUpperRect.Top())/2 );
|
|
maLowerRect.SetTop( maUpperRect.Bottom() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( IsMenuEnabled() && !ImplHasExternalMenubutton() && !bIsInPopupMode )
|
|
{
|
|
if( !ImplIsFloatingMode() )
|
|
{
|
|
mpData->maMenubuttonItem.maRect.SetBottom( mnDY - 2 );
|
|
mpData->maMenubuttonItem.maRect.SetLeft( nLeft );
|
|
mpData->maMenubuttonItem.maRect.SetRight( mnDX-mnRightBorder-TB_BORDER_OFFSET2-1 );
|
|
}
|
|
else
|
|
{
|
|
mpData->maMenubuttonItem.maRect.SetBottom( mnDY - mnBottomBorder-TB_BORDER_OFFSET1-1 );
|
|
mpData->maMenubuttonItem.maRect.SetLeft( nLeft );
|
|
mpData->maMenubuttonItem.maRect.SetRight( mnDX-mnRightBorder-TB_BORDER_OFFSET2-1 );
|
|
}
|
|
mpData->maMenubuttonItem.maRect.SetTop( mpData->maMenubuttonItem.maRect.Bottom() - mpData->mnMenuButtonWidth );
|
|
}
|
|
else
|
|
{
|
|
maUpperRect.SetTop( nTop+nMax+TB_SPIN_OFFSET );
|
|
maUpperRect.SetBottom( maUpperRect.Top()+TB_SPIN_SIZE-1 );
|
|
maUpperRect.SetLeft( nLeft );
|
|
maLowerRect.SetRight( mnDX-mnRightBorder-TB_BORDER_OFFSET2-1 );
|
|
maLowerRect.SetTop( maUpperRect.Top() );
|
|
maLowerRect.SetBottom( maUpperRect.Bottom() );
|
|
maUpperRect.SetRight( maUpperRect.Left() +
|
|
(maLowerRect.Right()-maUpperRect.Left())/2 );
|
|
maLowerRect.SetLeft( maUpperRect.Right() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// no scrolling when there is a "more"-menu
|
|
// anything will "fit" in a single line then
|
|
if( IsMenuEnabled() )
|
|
mnCurLines = 1;
|
|
|
|
// determine the currently visible line
|
|
if ( mnVisLines >= mnCurLines )
|
|
mnCurLine = 1;
|
|
else if ( mnCurLine+mnVisLines-1 > mnCurLines )
|
|
mnCurLine = mnCurLines - (mnVisLines-1);
|
|
|
|
long firstItemCenter = 0;
|
|
for (auto & item : mpData->m_aItems)
|
|
{
|
|
item.mbShowWindow = false;
|
|
|
|
// check for line break and advance nX/nY accordingly
|
|
if ( item.mbBreak )
|
|
{
|
|
nFormatLine++;
|
|
|
|
// increment starting with the second line
|
|
if ( nFormatLine > mnCurLine )
|
|
{
|
|
if ( mbHorz )
|
|
{
|
|
nX = nLeft;
|
|
if ( mbLineSpacing )
|
|
nY += nLineSize+TB_LINESPACING;
|
|
else
|
|
nY += nLineSize;
|
|
}
|
|
else
|
|
{
|
|
nY = nTop;
|
|
if ( mbLineSpacing )
|
|
nX += nLineSize+TB_LINESPACING;
|
|
else
|
|
nX += nLineSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !item.mbVisible || (nFormatLine < mnCurLine) ||
|
|
(nFormatLine > mnCurLine+mnVisLines-1) )
|
|
// item is not visible
|
|
item.maCalcRect = aEmptyRect;
|
|
else
|
|
{
|
|
// 1. determine current item width/height
|
|
// take window size and orientation into account, because this affects the size of item windows
|
|
|
|
Size aCurrentItemSize( item.GetSize( mbHorz, mbScroll, nMax, Size(mnMaxItemWidth, mnMaxItemHeight) ) );
|
|
|
|
// 2. position item rect and use size from step 1
|
|
// items will be centered horizontally (if mbHorz) or vertically
|
|
// advance nX and nY accordingly
|
|
|
|
if ( mbHorz )
|
|
{
|
|
// In special mode Locked horizontal positions of all items remain unchanged.
|
|
|
|
if ( mbIsArranged && meLayoutMode == ToolBoxLayoutMode::Locked && mnLines == 1 && item.maRect.Left() > 0 )
|
|
nX = item.maRect.Left();
|
|
item.maCalcRect.SetLeft( nX );
|
|
|
|
// In special mode Locked first item's vertical position remains unchanged. Consecutive items vertical
|
|
// positions are centered around first item's vertical position. If an item's height exceeds available
|
|
// space, item's vertical position remains unchanged too.
|
|
|
|
if ( mbIsArranged && meLayoutMode == ToolBoxLayoutMode::Locked && mnLines == 1 )
|
|
if ( firstItemCenter > 0 )
|
|
if ( firstItemCenter-aCurrentItemSize.Height()/2 > nY )
|
|
item.maCalcRect.SetTop( firstItemCenter-aCurrentItemSize.Height()/2 );
|
|
else
|
|
item.maCalcRect.SetTop( item.maRect.Top() );
|
|
else
|
|
{
|
|
item.maCalcRect.SetTop( item.maRect.Top() );
|
|
firstItemCenter = item.maRect.Top()+aCurrentItemSize.Height()/2;
|
|
}
|
|
else
|
|
item.maCalcRect.SetTop( nY+(nLineSize-aCurrentItemSize.Height())/2 );
|
|
item.maCalcRect.SetRight( nX+aCurrentItemSize.Width()-1 );
|
|
item.maCalcRect.SetBottom( item.maCalcRect.Top()+aCurrentItemSize.Height()-1 );
|
|
nX += aCurrentItemSize.Width();
|
|
}
|
|
else
|
|
{
|
|
item.maCalcRect.SetLeft( nX+(nLineSize-aCurrentItemSize.Width())/2 );
|
|
item.maCalcRect.SetTop( nY );
|
|
item.maCalcRect.SetRight( item.maCalcRect.Left()+aCurrentItemSize.Width()-1 );
|
|
item.maCalcRect.SetBottom( nY+aCurrentItemSize.Height()-1 );
|
|
nY += aCurrentItemSize.Height();
|
|
}
|
|
}
|
|
|
|
// position window items into calculated item rect
|
|
if ( item.mpWindow )
|
|
{
|
|
if ( item.mbShowWindow )
|
|
{
|
|
Point aPos( item.maCalcRect.Left(), item.maCalcRect.Top() );
|
|
|
|
assert( item.maCalcRect.Top() >= 0 );
|
|
|
|
item.mpWindow->SetPosPixel( aPos );
|
|
item.mpWindow->Show();
|
|
}
|
|
else
|
|
item.mpWindow->Hide();
|
|
}
|
|
} // end of loop over all items
|
|
mbIsArranged = true;
|
|
}
|
|
else
|
|
// we have no toolbox items
|
|
mnCurLines = 1;
|
|
|
|
if( IsMenuEnabled() && ImplIsFloatingMode() && !ImplHasExternalMenubutton() && !bIsInPopupMode )
|
|
{
|
|
// custom menu will be the last button in floating mode
|
|
ImplToolItem &rIt = mpData->maMenubuttonItem;
|
|
|
|
if ( mbHorz )
|
|
{
|
|
rIt.maRect.SetLeft( nX+TB_MENUBUTTON_OFFSET );
|
|
rIt.maRect.SetTop( nY );
|
|
rIt.maRect.SetRight( rIt.maRect.Left() + mpData->mnMenuButtonWidth );
|
|
rIt.maRect.SetBottom( nY+nLineSize-1 );
|
|
nX += rIt.maItemSize.Width();
|
|
}
|
|
else
|
|
{
|
|
rIt.maRect.SetLeft( nX );
|
|
rIt.maRect.SetTop( nY+TB_MENUBUTTON_OFFSET );
|
|
rIt.maRect.SetRight( nX+nLineSize-1 );
|
|
rIt.maRect.SetBottom( rIt.maRect.Top() + mpData->mnMenuButtonWidth );
|
|
nY += rIt.maItemSize.Height();
|
|
}
|
|
}
|
|
|
|
// if toolbox visible trigger paint for changed regions
|
|
if ( IsVisible() && !mbFullPaint )
|
|
{
|
|
if ( bMustFullPaint )
|
|
{
|
|
maPaintRect = tools::Rectangle( mnLeftBorder, mnTopBorder,
|
|
mnDX-mnRightBorder, mnDY-mnBottomBorder );
|
|
}
|
|
else
|
|
{
|
|
if ( aOldLowerRect != maLowerRect )
|
|
{
|
|
maPaintRect.Union( maLowerRect );
|
|
maPaintRect.Union( aOldLowerRect );
|
|
}
|
|
if ( aOldUpperRect != maUpperRect )
|
|
{
|
|
maPaintRect.Union( maUpperRect );
|
|
maPaintRect.Union( aOldUpperRect );
|
|
}
|
|
if ( aOldMenubuttonRect != mpData->maMenubuttonItem.maRect )
|
|
{
|
|
maPaintRect.Union( mpData->maMenubuttonItem.maRect );
|
|
maPaintRect.Union( aOldMenubuttonRect );
|
|
}
|
|
if ( pWrapper && aOldDragRect != pWrapper->GetDragArea() )
|
|
{
|
|
maPaintRect.Union( pWrapper->GetDragArea() );
|
|
maPaintRect.Union( aOldDragRect );
|
|
}
|
|
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if ( item.maRect != item.maCalcRect )
|
|
{
|
|
maPaintRect.Union( item.maRect );
|
|
maPaintRect.Union( item.maCalcRect );
|
|
}
|
|
}
|
|
}
|
|
|
|
Invalidate( maPaintRect );
|
|
}
|
|
|
|
// store the new calculated item rects
|
|
maPaintRect = aEmptyRect;
|
|
for (auto & item : mpData->m_aItems)
|
|
item.maRect = item.maCalcRect;
|
|
}
|
|
|
|
// indicate formatting is done
|
|
mbFormat = false;
|
|
}
|
|
|
|
IMPL_LINK_NOARG(ToolBox, ImplDropdownLongClickHdl, Timer *, void)
|
|
{
|
|
if (mnCurPos != ITEM_NOTFOUND &&
|
|
(mpData->m_aItems[ mnCurPos ].mnBits & ToolBoxItemBits::DROPDOWN))
|
|
{
|
|
mpData->mbDropDownByKeyboard = false;
|
|
mpData->maDropdownClickHdl.Call( this );
|
|
|
|
// do not reset data if the dropdown handler opened a floating window
|
|
// see ImplFloatControl()
|
|
if( !mpFloatWin )
|
|
{
|
|
// no floater was opened
|
|
Deactivate();
|
|
InvalidateItem(mnCurPos);
|
|
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
mnCurItemId = 0;
|
|
mnDownItemId = 0;
|
|
mnMouseModifier = 0;
|
|
mnHighItemId = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
IMPL_LINK_NOARG(ToolBox, ImplUpdateHdl, Timer *, void)
|
|
{
|
|
|
|
if( mbFormat && mpData )
|
|
ImplFormat();
|
|
}
|
|
|
|
static void ImplDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
|
|
{
|
|
const Image pImage(StockImage::Yes, CHEVRON);
|
|
Size aImageSize = pImage.GetSizePixel();
|
|
long x = rRect.Left() + (rRect.getWidth() - aImageSize.Width())/2;
|
|
long y = rRect.Top() + (rRect.getHeight() - aImageSize.Height())/2;
|
|
DrawImageFlags nImageStyle = DrawImageFlags::NONE;
|
|
|
|
rRenderContext.DrawImage(Point(x,y), pImage, nImageStyle);
|
|
}
|
|
|
|
static void ImplDrawDropdownArrow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rDropDownRect, bool bSetColor, bool bRotate )
|
|
{
|
|
bool bLineColor = rRenderContext.IsLineColor();
|
|
bool bFillColor = rRenderContext.IsFillColor();
|
|
Color aOldFillColor = rRenderContext.GetFillColor();
|
|
Color aOldLineColor = rRenderContext.GetLineColor();
|
|
rRenderContext.SetLineColor();
|
|
|
|
if ( bSetColor )
|
|
{
|
|
if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
|
|
rRenderContext.SetFillColor(COL_WHITE);
|
|
else
|
|
rRenderContext.SetFillColor(COL_BLACK);
|
|
}
|
|
|
|
tools::Polygon aPoly(4);
|
|
|
|
// the assumption is, that the width always specifies the size of the expected arrow.
|
|
const long nMargin = round(2 * rRenderContext.GetDPIScaleFactor());
|
|
const long nSize = rDropDownRect.getWidth() - 2 * nMargin;
|
|
const long nHalfSize = (nSize + 1) / 2;
|
|
const long x = rDropDownRect.Left() + nMargin + (bRotate ? (rDropDownRect.getWidth() - nHalfSize) / 2 : 0);
|
|
const long y = rDropDownRect.Top() + nMargin + (rDropDownRect.getHeight() - (bRotate ? nSize : nHalfSize)) / 2;
|
|
|
|
aPoly.SetPoint(Point(x, y), 0);
|
|
if (bRotate) // >
|
|
{
|
|
aPoly.SetPoint(Point(x, y + nSize), 1);
|
|
aPoly.SetPoint(Point(x + nHalfSize, y + nHalfSize), 2);
|
|
}
|
|
else // v
|
|
{
|
|
aPoly.SetPoint(Point(x + nHalfSize, y + nHalfSize), 1);
|
|
aPoly.SetPoint(Point(x + nSize, y), 2);
|
|
}
|
|
aPoly.SetPoint(Point(x, y), 3);
|
|
|
|
auto aaflags = rRenderContext.GetAntialiasing();
|
|
rRenderContext.SetAntialiasing(AntialiasingFlags::EnableB2dDraw);
|
|
rRenderContext.DrawPolygon( aPoly );
|
|
rRenderContext.SetAntialiasing(aaflags);
|
|
|
|
if( bFillColor )
|
|
rRenderContext.SetFillColor(aOldFillColor);
|
|
else
|
|
rRenderContext.SetFillColor();
|
|
if( bLineColor )
|
|
rRenderContext.SetLineColor(aOldLineColor);
|
|
else
|
|
rRenderContext.SetLineColor();
|
|
}
|
|
|
|
void ToolBox::ImplDrawMenuButton(vcl::RenderContext& rRenderContext, bool bHighlight)
|
|
{
|
|
if (!mpData->maMenubuttonItem.maRect.IsEmpty())
|
|
{
|
|
// #i53937# paint menu button only if necessary
|
|
if (!ImplHasClippedItems())
|
|
return;
|
|
|
|
// execute pending paint requests
|
|
ImplCheckUpdate();
|
|
|
|
rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
|
|
|
|
// draw the 'more' indicator / button (>>)
|
|
ImplErase(rRenderContext, mpData->maMenubuttonItem.maRect, bHighlight);
|
|
|
|
if (bHighlight)
|
|
ImplDrawButton(rRenderContext, mpData->maMenubuttonItem.maRect, 2, false, true, false );
|
|
|
|
if (ImplHasClippedItems())
|
|
ImplDrawMoreIndicator(rRenderContext, mpData->maMenubuttonItem.maRect);
|
|
|
|
// store highlight state
|
|
mpData->mbMenubuttonSelected = bHighlight;
|
|
|
|
// restore colors
|
|
rRenderContext.Pop();
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDrawSpin(vcl::RenderContext& rRenderContext)
|
|
{
|
|
bool bTmpUpper;
|
|
bool bTmpLower;
|
|
|
|
if ( maUpperRect.IsEmpty() || maLowerRect.IsEmpty() )
|
|
return;
|
|
|
|
bTmpUpper = mnCurLine > 1;
|
|
|
|
bTmpLower = mnCurLine+mnVisLines-1 < mnCurLines;
|
|
|
|
if ( !IsEnabled() )
|
|
{
|
|
bTmpUpper = false;
|
|
bTmpLower = false;
|
|
}
|
|
|
|
ImplDrawUpDownButtons(rRenderContext, maUpperRect, maLowerRect,
|
|
false/*bUpperIn*/, false/*bLowerIn*/, bTmpUpper, bTmpLower, !mbHorz);
|
|
}
|
|
|
|
void ToolBox::ImplDrawSeparator(vcl::RenderContext& rRenderContext, ImplToolItems::size_type nPos, const tools::Rectangle& rRect)
|
|
{
|
|
if ( nPos >= mpData->m_aItems.size() - 1 )
|
|
// no separator if it's the last item
|
|
return;
|
|
|
|
ImplToolItem* pItem = &mpData->m_aItems[nPos];
|
|
ImplToolItem* pPreviousItem = &mpData->m_aItems[nPos-1];
|
|
ImplToolItem* pNextItem = &mpData->m_aItems[nPos+1];
|
|
|
|
if ( ( pPreviousItem->mbShowWindow && pNextItem->mbShowWindow ) || pNextItem->mbBreak )
|
|
// no separator between two windows or before a break
|
|
return;
|
|
|
|
bool bNativeOk = false;
|
|
ControlPart nPart = IsHorizontal() ? ControlPart::SeparatorVert : ControlPart::SeparatorHorz;
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, nPart))
|
|
{
|
|
ImplControlValue aControlValue;
|
|
bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, nPart, rRect, ControlState::NONE, aControlValue, OUString());
|
|
}
|
|
|
|
/* Draw the widget only if it can't be drawn natively. */
|
|
if (!bNativeOk)
|
|
{
|
|
long nCenterPos, nSlim;
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
rRenderContext.SetLineColor(rStyleSettings.GetSeparatorColor());
|
|
if (IsHorizontal())
|
|
{
|
|
nSlim = (pItem->maRect.Bottom() - pItem->maRect.Top ()) / 4;
|
|
nCenterPos = pItem->maRect.Center().X();
|
|
rRenderContext.DrawLine(Point(nCenterPos, pItem->maRect.Top() + nSlim),
|
|
Point(nCenterPos, pItem->maRect.Bottom() - nSlim));
|
|
}
|
|
else
|
|
{
|
|
nSlim = (pItem->maRect.Right() - pItem->maRect.Left ()) / 4;
|
|
nCenterPos = pItem->maRect.Center().Y();
|
|
rRenderContext.DrawLine(Point(pItem->maRect.Left() + nSlim, nCenterPos),
|
|
Point(pItem->maRect.Right() - nSlim, nCenterPos));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDrawButton(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, sal_uInt16 highlight,
|
|
bool bChecked, bool bEnabled, bool bIsWindow )
|
|
{
|
|
// draws toolbar button background either native or using a coloured selection
|
|
// if bIsWindow is true, the corresponding item is a control and only a selection border will be drawn
|
|
|
|
bool bNativeOk = false;
|
|
if( !bIsWindow && rRenderContext.IsNativeControlSupported( ControlType::Toolbar, ControlPart::Button ) )
|
|
{
|
|
ImplControlValue aControlValue;
|
|
ControlState nState = ControlState::NONE;
|
|
|
|
if ( highlight == 1 ) nState |= ControlState::PRESSED;
|
|
if ( highlight == 2 ) nState |= ControlState::ROLLOVER;
|
|
if ( bEnabled ) nState |= ControlState::ENABLED;
|
|
|
|
aControlValue.setTristateVal( bChecked ? ButtonValue::On : ButtonValue::Off );
|
|
|
|
bNativeOk = rRenderContext.DrawNativeControl( ControlType::Toolbar, ControlPart::Button,
|
|
rRect, nState, aControlValue, OUString() );
|
|
}
|
|
|
|
if (!bNativeOk)
|
|
vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, rRect, bIsWindow ? 3 : highlight,
|
|
bChecked, true, bIsWindow, nullptr, 2);
|
|
}
|
|
|
|
void ToolBox::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplToolItems::size_type nPos, sal_uInt16 nHighlight)
|
|
{
|
|
if (nPos >= mpData->m_aItems.size())
|
|
return;
|
|
|
|
// execute pending paint requests
|
|
ImplCheckUpdate();
|
|
|
|
ImplDisableFlatButtons();
|
|
|
|
rRenderContext.SetFillColor();
|
|
|
|
ImplToolItem* pItem = &mpData->m_aItems[nPos];
|
|
|
|
if (!pItem->mbEnabled)
|
|
nHighlight = 0;
|
|
|
|
// if the rectangle is outside visible area
|
|
if (pItem->maRect.IsEmpty())
|
|
return;
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
|
|
// no gradient background for items that have a popup open
|
|
bool bHasOpenPopup = mpFloatWin && (mnDownItemId==pItem->mnId);
|
|
|
|
bool bHighContrastWhite = false;
|
|
// check the face color as highcontrast indicator
|
|
// because the toolbox itself might have a gradient
|
|
if (rStyleSettings.GetFaceColor() == COL_WHITE)
|
|
bHighContrastWhite = true;
|
|
|
|
// Compute buttons area.
|
|
Size aBtnSize = pItem->maRect.GetSize();
|
|
|
|
/* Compute the button/separator rectangle here, we'll need it for
|
|
* both the buttons and the separators. */
|
|
tools::Rectangle aButtonRect( pItem->maRect.TopLeft(), aBtnSize );
|
|
long nOffX = SMALLBUTTON_OFF_NORMAL_X;
|
|
long nOffY = SMALLBUTTON_OFF_NORMAL_Y;
|
|
long nImageOffX = 0;
|
|
long nImageOffY = 0;
|
|
DrawButtonFlags nStyle = DrawButtonFlags::NONE;
|
|
|
|
// draw separators in flat style only
|
|
if ( (mnOutStyle & TOOLBOX_STYLE_FLAT) &&
|
|
(pItem->meType == ToolBoxItemType::SEPARATOR) &&
|
|
nPos > 0
|
|
)
|
|
{
|
|
ImplDrawSeparator(rRenderContext, nPos, aButtonRect);
|
|
}
|
|
|
|
// do nothing if item is no button or will be displayed as window
|
|
if ( (pItem->meType != ToolBoxItemType::BUTTON) || pItem->mbShowWindow )
|
|
return;
|
|
|
|
if ( pItem->meState == TRISTATE_TRUE )
|
|
{
|
|
nStyle |= DrawButtonFlags::Checked;
|
|
}
|
|
else if ( pItem->meState == TRISTATE_INDET )
|
|
{
|
|
nStyle |= DrawButtonFlags::DontKnow;
|
|
}
|
|
if ( nHighlight == 1 )
|
|
{
|
|
nStyle |= DrawButtonFlags::Pressed;
|
|
}
|
|
|
|
if ( mnOutStyle & TOOLBOX_STYLE_FLAT )
|
|
{
|
|
ImplErase(rRenderContext, pItem->maRect, nHighlight != 0, bHasOpenPopup );
|
|
}
|
|
else
|
|
{
|
|
DecorationView aDecoView(&rRenderContext);
|
|
aDecoView.DrawButton(aButtonRect, nStyle);
|
|
}
|
|
|
|
nOffX += pItem->maRect.Left();
|
|
nOffY += pItem->maRect.Top();
|
|
|
|
// determine what has to be drawn on the button: image, text or both
|
|
bool bImage;
|
|
bool bText;
|
|
ButtonType tmpButtonType = determineButtonType( pItem, meButtonType ); // default to toolbox setting
|
|
pItem->DetermineButtonDrawStyle( tmpButtonType, bImage, bText );
|
|
|
|
// compute output values
|
|
long nBtnWidth = aBtnSize.Width()-SMALLBUTTON_HSIZE;
|
|
long nBtnHeight = aBtnSize.Height()-SMALLBUTTON_VSIZE;
|
|
Size aImageSize;
|
|
|
|
const bool bDropDown = (pItem->mnBits & ToolBoxItemBits::DROPDOWN) == ToolBoxItemBits::DROPDOWN;
|
|
tools::Rectangle aDropDownRect;
|
|
if (bDropDown)
|
|
aDropDownRect = pItem->GetDropDownRect(mbHorz);
|
|
|
|
if ( bImage )
|
|
{
|
|
const Image* pImage = &(pItem->maImage);
|
|
aImageSize = pImage->GetSizePixel();
|
|
|
|
// determine drawing flags
|
|
DrawImageFlags nImageStyle = DrawImageFlags::NONE;
|
|
|
|
if ( !pItem->mbEnabled || !IsEnabled() )
|
|
nImageStyle |= DrawImageFlags::Disable;
|
|
|
|
// #i35563# the dontknow state indicates different states at the same time
|
|
// which should not be rendered disabled but normal
|
|
|
|
// draw the image
|
|
nImageOffX = nOffX;
|
|
nImageOffY = nOffY;
|
|
if ( ( (pItem->mnBits & (ToolBoxItemBits::LEFT|ToolBoxItemBits::DROPDOWN)) || bText )
|
|
&& ( meTextPosition == ToolBoxTextPosition::Right ) )
|
|
{
|
|
// left align also to leave space for drop down arrow
|
|
// and when drawing text+image
|
|
// just center in y, except for vertical (ie rotated text)
|
|
if( mbHorz || !bText )
|
|
nImageOffY += (nBtnHeight-aImageSize.Height())/2;
|
|
}
|
|
else
|
|
{
|
|
nImageOffX += (nBtnWidth-(bDropDown ? aDropDownRect.getWidth() : 0)+SMALLBUTTON_OFF_NORMAL_X-aImageSize.Width())/2;
|
|
if ( meTextPosition == ToolBoxTextPosition::Right )
|
|
nImageOffY += (nBtnHeight-aImageSize.Height())/2;
|
|
}
|
|
if ( nHighlight != 0 || (pItem->meState == TRISTATE_TRUE) )
|
|
{
|
|
if( bHasOpenPopup )
|
|
ImplDrawFloatwinBorder(rRenderContext, pItem);
|
|
else
|
|
ImplDrawButton(rRenderContext, aButtonRect, nHighlight, pItem->meState == TRISTATE_TRUE,
|
|
pItem->mbEnabled && IsEnabled(), pItem->mbShowWindow);
|
|
|
|
if( nHighlight != 0 )
|
|
{
|
|
if( bHighContrastWhite )
|
|
nImageStyle |= DrawImageFlags::ColorTransform;
|
|
}
|
|
}
|
|
rRenderContext.DrawImage(Point( nImageOffX, nImageOffY ), *pImage, nImageStyle);
|
|
}
|
|
|
|
// draw the text
|
|
bool bRotate = false;
|
|
if ( bText )
|
|
{
|
|
const Size aTxtSize(GetCtrlTextWidth(pItem->maText), GetTextHeight());
|
|
long nTextOffX = nOffX;
|
|
long nTextOffY = nOffY;
|
|
|
|
// rotate text when vertically docked
|
|
vcl::Font aOldFont = rRenderContext.GetFont();
|
|
if( pItem->mbVisibleText && !ImplIsFloatingMode() &&
|
|
((meAlign == WindowAlign::Left) || (meAlign == WindowAlign::Right)) )
|
|
{
|
|
bRotate = true;
|
|
|
|
vcl::Font aRotateFont = aOldFont;
|
|
aRotateFont.SetOrientation( 2700 );
|
|
|
|
// center horizontally
|
|
nTextOffX += aTxtSize.Height();
|
|
nTextOffX += (nBtnWidth-aTxtSize.Height())/2;
|
|
|
|
// add in image offset
|
|
if( bImage )
|
|
nTextOffY = nImageOffY + aImageSize.Height() + TB_IMAGETEXTOFFSET;
|
|
|
|
rRenderContext.SetFont(aRotateFont);
|
|
}
|
|
else
|
|
{
|
|
if ( meTextPosition == ToolBoxTextPosition::Right )
|
|
{
|
|
// center vertically
|
|
nTextOffY += (nBtnHeight-aTxtSize.Height())/2;
|
|
|
|
// add in image offset
|
|
if( bImage )
|
|
nTextOffX = nImageOffX + aImageSize.Width() + TB_IMAGETEXTOFFSET;
|
|
}
|
|
else
|
|
{
|
|
// center horizontally
|
|
nTextOffX += (nBtnWidth-(bDropDown ? aDropDownRect.getWidth() : 0)+SMALLBUTTON_OFF_NORMAL_X-aTxtSize.Width() - TB_IMAGETEXTOFFSET)/2;
|
|
// set vertical position
|
|
nTextOffY += nBtnHeight - aTxtSize.Height();
|
|
}
|
|
}
|
|
|
|
// draw selection only if not already drawn during image output (see above)
|
|
if ( !bImage && (nHighlight != 0 || (pItem->meState == TRISTATE_TRUE) ) )
|
|
{
|
|
if( bHasOpenPopup )
|
|
ImplDrawFloatwinBorder(rRenderContext, pItem);
|
|
else
|
|
ImplDrawButton(rRenderContext, pItem->maRect, nHighlight, pItem->meState == TRISTATE_TRUE,
|
|
pItem->mbEnabled && IsEnabled(), pItem->mbShowWindow );
|
|
}
|
|
|
|
DrawTextFlags nTextStyle = DrawTextFlags::NONE;
|
|
if ( !pItem->mbEnabled )
|
|
nTextStyle |= DrawTextFlags::Disable;
|
|
rRenderContext.DrawCtrlText( Point( nTextOffX, nTextOffY ), pItem->maText,
|
|
0, pItem->maText.getLength(), nTextStyle );
|
|
if ( bRotate )
|
|
SetFont( aOldFont );
|
|
}
|
|
|
|
// paint optional drop down arrow
|
|
if (bDropDown)
|
|
{
|
|
bool bSetColor = true;
|
|
if ( !pItem->mbEnabled || !IsEnabled() )
|
|
{
|
|
bSetColor = false;
|
|
rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
|
|
}
|
|
|
|
// dropdown only will be painted without inner border
|
|
if( (pItem->mnBits & ToolBoxItemBits::DROPDOWNONLY) != ToolBoxItemBits::DROPDOWNONLY )
|
|
{
|
|
ImplErase(rRenderContext, aDropDownRect, nHighlight != 0, bHasOpenPopup);
|
|
|
|
if( nHighlight != 0 || (pItem->meState == TRISTATE_TRUE) )
|
|
{
|
|
if( bHasOpenPopup )
|
|
ImplDrawFloatwinBorder(rRenderContext, pItem);
|
|
else
|
|
ImplDrawButton(rRenderContext, aDropDownRect, nHighlight, pItem->meState == TRISTATE_TRUE,
|
|
pItem->mbEnabled && IsEnabled(), false);
|
|
}
|
|
}
|
|
ImplDrawDropdownArrow(rRenderContext, aDropDownRect, bSetColor, bRotate);
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDrawFloatwinBorder(vcl::RenderContext& rRenderContext, ImplToolItem const * pItem)
|
|
{
|
|
if ( pItem->maRect.IsEmpty() )
|
|
return;
|
|
|
|
tools::Rectangle aRect( mpFloatWin->ImplGetItemEdgeClipRect() );
|
|
aRect.SetPos( AbsoluteScreenToOutputPixel( aRect.TopLeft() ) );
|
|
rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetShadowColor());
|
|
Point p1, p2;
|
|
|
|
p1 = pItem->maRect.TopLeft();
|
|
p1.AdjustX( 1 );
|
|
p2 = pItem->maRect.TopRight();
|
|
p2.AdjustX( -1 );
|
|
rRenderContext.DrawLine( p1, p2);
|
|
p1 = pItem->maRect.BottomLeft();
|
|
p1.AdjustX( 1 );
|
|
p2 = pItem->maRect.BottomRight();
|
|
p2.AdjustX( -1 );
|
|
rRenderContext.DrawLine( p1, p2);
|
|
|
|
p1 = pItem->maRect.TopLeft();
|
|
p1.AdjustY( 1 );
|
|
p2 = pItem->maRect.BottomLeft();
|
|
p2.AdjustY( -1 );
|
|
rRenderContext.DrawLine( p1, p2);
|
|
p1 = pItem->maRect.TopRight();
|
|
p1.AdjustY( 1 );
|
|
p2 = pItem->maRect.BottomRight();
|
|
p2.AdjustY( -1 );
|
|
rRenderContext.DrawLine( p1, p2);
|
|
|
|
}
|
|
|
|
void ToolBox::ImplFloatControl( bool bStart, FloatingWindow* pFloatWindow )
|
|
{
|
|
|
|
if ( bStart )
|
|
{
|
|
mpFloatWin = pFloatWindow;
|
|
|
|
// redraw item, to trigger drawing of a special border
|
|
InvalidateItem(mnCurPos);
|
|
|
|
mbDrag = false;
|
|
EndTracking();
|
|
if (IsMouseCaptured())
|
|
ReleaseMouse();
|
|
}
|
|
else
|
|
{
|
|
mpFloatWin = nullptr;
|
|
|
|
// if focus is still in this toolbox, then the floater was opened by keyboard
|
|
// draw current item with highlight and keep old state
|
|
bool bWasKeyboardActivate = mpData->mbDropDownByKeyboard;
|
|
|
|
if ( mnCurPos != ITEM_NOTFOUND )
|
|
InvalidateItem(mnCurPos);
|
|
Deactivate();
|
|
|
|
if( !bWasKeyboardActivate )
|
|
{
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
mnCurItemId = 0;
|
|
mnHighItemId = 0;
|
|
}
|
|
mnDownItemId = 0;
|
|
|
|
}
|
|
}
|
|
|
|
void ToolBox::ShowLine( bool bNext )
|
|
{
|
|
mbFormat = true;
|
|
|
|
if ( bNext )
|
|
mnCurLine++;
|
|
else
|
|
mnCurLine--;
|
|
|
|
ImplFormat();
|
|
}
|
|
|
|
bool ToolBox::ImplHandleMouseMove( const MouseEvent& rMEvt, bool bRepeat )
|
|
{
|
|
Point aMousePos = rMEvt.GetPosPixel();
|
|
|
|
if ( !mpData )
|
|
return false;
|
|
|
|
// ToolBox active?
|
|
if ( mbDrag && mnCurPos != ITEM_NOTFOUND )
|
|
{
|
|
// is the cursor over the item?
|
|
ImplToolItem* pItem = &mpData->m_aItems[mnCurPos];
|
|
if ( pItem->maRect.IsInside( aMousePos ) )
|
|
{
|
|
if ( !mnCurItemId )
|
|
{
|
|
InvalidateItem(mnCurPos);
|
|
mnCurItemId = pItem->mnId;
|
|
Highlight();
|
|
}
|
|
|
|
if ( (pItem->mnBits & ToolBoxItemBits::REPEAT) && bRepeat )
|
|
Select();
|
|
}
|
|
else
|
|
{
|
|
if ( mnCurItemId )
|
|
{
|
|
InvalidateItem(mnCurPos);
|
|
mnCurItemId = 0;
|
|
InvalidateItem(mnCurPos);
|
|
Highlight();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if ( mbUpper )
|
|
{
|
|
bool bNewIn = maUpperRect.IsInside( aMousePos );
|
|
if ( bNewIn != mbIn )
|
|
{
|
|
mbIn = bNewIn;
|
|
InvalidateSpin(true, false);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ( mbLower )
|
|
{
|
|
bool bNewIn = maLowerRect.IsInside( aMousePos );
|
|
if ( bNewIn != mbIn )
|
|
{
|
|
mbIn = bNewIn;
|
|
InvalidateSpin(false);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ToolBox::ImplHandleMouseButtonUp( const MouseEvent& rMEvt, bool bCancel )
|
|
{
|
|
ImplDisableFlatButtons();
|
|
|
|
if ( !mpData )
|
|
return false;
|
|
|
|
// stop eventual running dropdown timer
|
|
if( mnCurPos < mpData->m_aItems.size() &&
|
|
(mpData->m_aItems[mnCurPos].mnBits & ToolBoxItemBits::DROPDOWN ) )
|
|
{
|
|
mpData->maDropdownTimer.Stop();
|
|
}
|
|
|
|
if ( mbDrag )
|
|
{
|
|
Deactivate();
|
|
|
|
if ( mbDrag )
|
|
mbDrag = false;
|
|
else
|
|
{
|
|
if ( mnCurPos == ITEM_NOTFOUND )
|
|
return true;
|
|
}
|
|
|
|
// has mouse been released on top of item?
|
|
if( mnCurPos < mpData->m_aItems.size() )
|
|
{
|
|
ImplToolItem* pItem = &mpData->m_aItems[mnCurPos];
|
|
if ( pItem->maRect.IsInside( rMEvt.GetPosPixel() ) )
|
|
{
|
|
mnCurItemId = pItem->mnId;
|
|
if ( !bCancel )
|
|
{
|
|
// execute AutoCheck if required
|
|
if ( pItem->mnBits & ToolBoxItemBits::AUTOCHECK )
|
|
{
|
|
if ( pItem->mnBits & ToolBoxItemBits::RADIOCHECK )
|
|
{
|
|
if ( pItem->meState != TRISTATE_TRUE )
|
|
SetItemState( pItem->mnId, TRISTATE_TRUE );
|
|
}
|
|
else
|
|
{
|
|
if ( pItem->meState != TRISTATE_TRUE )
|
|
pItem->meState = TRISTATE_TRUE;
|
|
else
|
|
pItem->meState = TRISTATE_FALSE;
|
|
}
|
|
}
|
|
|
|
// do not call Select when Repeat is active, as in this
|
|
// case that was triggered already in MouseButtonDown
|
|
if ( !(pItem->mnBits & ToolBoxItemBits::REPEAT) )
|
|
{
|
|
// prevent from being destroyed in the select handler
|
|
VclPtr<vcl::Window> xWindow = this;
|
|
Select();
|
|
if ( xWindow->IsDisposed() )
|
|
return true;
|
|
}
|
|
}
|
|
|
|
{
|
|
}
|
|
|
|
// Items not destroyed, in Select handler
|
|
if ( mnCurItemId )
|
|
{
|
|
// Get current pos for the case that items are inserted/removed
|
|
// in the toolBox
|
|
mnCurPos = GetItemPos( mnCurItemId );
|
|
if ( mnCurPos != ITEM_NOTFOUND )
|
|
{
|
|
InvalidateItem(mnCurPos);
|
|
Flush();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
mnCurItemId = 0;
|
|
mnDownItemId = 0;
|
|
mnMouseModifier = 0;
|
|
return true;
|
|
}
|
|
else if ( mbUpper || mbLower )
|
|
{
|
|
if ( mbIn )
|
|
ShowLine( !mbUpper );
|
|
mbUpper = false;
|
|
mbLower = false;
|
|
mbIn = false;
|
|
InvalidateSpin();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ToolBox::MouseMove( const MouseEvent& rMEvt )
|
|
{
|
|
// pressing a modifier generates synthetic mouse moves
|
|
// ignore it if keyboard selection is active
|
|
if( HasFocus() && ( rMEvt.GetMode() & MouseEventModifiers::MODIFIERCHANGED ) )
|
|
return;
|
|
|
|
if ( ImplHandleMouseMove( rMEvt ) )
|
|
return;
|
|
|
|
ImplDisableFlatButtons();
|
|
|
|
Point aMousePos = rMEvt.GetPosPixel();
|
|
|
|
// only highlight when the focus is not inside a child window of a toolbox
|
|
// eg, in an edit control
|
|
// and do not highlight when focus is in a different toolbox
|
|
bool bDrawHotSpot = true;
|
|
vcl::Window *pFocusWin = Application::GetFocusWindow();
|
|
|
|
bool bFocusWindowIsAToolBoxChild = false;
|
|
if (pFocusWin)
|
|
{
|
|
vcl::Window *pWin = pFocusWin->GetParent();
|
|
while (pWin)
|
|
{
|
|
if(pWin->ImplGetWindowImpl()->mbToolBox)
|
|
{
|
|
bFocusWindowIsAToolBoxChild = true;
|
|
break;
|
|
}
|
|
pWin = pWin->GetParent();
|
|
}
|
|
}
|
|
|
|
if( bFocusWindowIsAToolBoxChild || (pFocusWin && pFocusWin->ImplGetWindowImpl()->mbToolBox && pFocusWin != this) )
|
|
bDrawHotSpot = false;
|
|
|
|
if ( mbDragging )
|
|
{
|
|
ImplTBDragMgr* pMgr = ImplGetTBDragMgr();
|
|
pMgr->Dragging( aMousePos );
|
|
return;
|
|
}
|
|
|
|
PointerStyle eStyle = PointerStyle::Arrow;
|
|
|
|
// change mouse cursor over drag area
|
|
ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
|
|
if( pWrapper && pWrapper->GetDragArea().IsInside( rMEvt.GetPosPixel() ) )
|
|
eStyle = PointerStyle::Move;
|
|
|
|
if ( (mnWinStyle & TB_WBLINESIZING) == TB_WBLINESIZING )
|
|
{
|
|
if ( rMEvt.GetMode() & MouseEventModifiers::SIMPLEMOVE )
|
|
{
|
|
sal_uInt16 nLinePtr = ImplTestLineSize( rMEvt.GetPosPixel() );
|
|
if ( nLinePtr & DOCK_LINEHSIZE )
|
|
{
|
|
if ( meAlign == WindowAlign::Left )
|
|
eStyle = PointerStyle::WindowESize;
|
|
else
|
|
eStyle = PointerStyle::WindowWSize;
|
|
}
|
|
else if ( nLinePtr & DOCK_LINEVSIZE )
|
|
{
|
|
if ( meAlign == WindowAlign::Top )
|
|
eStyle = PointerStyle::WindowSSize;
|
|
else
|
|
eStyle = PointerStyle::WindowNSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bDrawHotSpot && ( (mnOutStyle & TOOLBOX_STYLE_FLAT) || !mnOutStyle ) )
|
|
{
|
|
bool bClearHigh = true;
|
|
if ( !rMEvt.IsLeaveWindow() && (mnCurPos == ITEM_NOTFOUND) )
|
|
{
|
|
ImplToolItems::size_type nTempPos = 0;
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if ( item.maRect.IsInside( aMousePos ) )
|
|
{
|
|
if ( (item.meType == ToolBoxItemType::BUTTON) && item.mbEnabled )
|
|
{
|
|
if ( !mnOutStyle || (mnOutStyle & TOOLBOX_STYLE_FLAT) )
|
|
{
|
|
bClearHigh = false;
|
|
if ( mnHighItemId != item.mnId )
|
|
{
|
|
if ( mnHighItemId )
|
|
{
|
|
ImplHideFocus();
|
|
ImplToolItems::size_type nPos = GetItemPos( mnHighItemId );
|
|
InvalidateItem(nPos);
|
|
CallEventListeners( VclEventId::ToolboxHighlightOff, reinterpret_cast< void* >( nPos ) );
|
|
}
|
|
if ( mpData->mbMenubuttonSelected )
|
|
{
|
|
// remove highlight from menubutton
|
|
InvalidateMenuButton();
|
|
}
|
|
mnHighItemId = item.mnId;
|
|
InvalidateItem(nTempPos);
|
|
ImplShowFocus();
|
|
CallEventListeners( VclEventId::ToolboxHighlight );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
++nTempPos;
|
|
}
|
|
}
|
|
|
|
// only clear highlight when focus is not in toolbar
|
|
bool bMenuButtonHit = mpData->maMenubuttonItem.maRect.IsInside( aMousePos ) && ImplHasClippedItems();
|
|
if ( !HasFocus() && (bClearHigh || bMenuButtonHit) )
|
|
{
|
|
if ( !bMenuButtonHit && mpData->mbMenubuttonSelected )
|
|
{
|
|
// remove highlight from menubutton
|
|
InvalidateMenuButton();
|
|
}
|
|
|
|
if( mnHighItemId )
|
|
{
|
|
ImplToolItems::size_type nClearPos = GetItemPos( mnHighItemId );
|
|
if ( nClearPos != ITEM_NOTFOUND )
|
|
{
|
|
InvalidateItem(nClearPos);
|
|
if( nClearPos != mnCurPos )
|
|
CallEventListeners( VclEventId::ToolboxHighlightOff, reinterpret_cast< void* >( nClearPos ) );
|
|
}
|
|
ImplHideFocus();
|
|
mnHighItemId = 0;
|
|
}
|
|
|
|
if( bMenuButtonHit )
|
|
{
|
|
InvalidateMenuButton();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( meLastStyle != eStyle )
|
|
{
|
|
meLastStyle = eStyle;
|
|
SetPointer( eStyle );
|
|
}
|
|
|
|
DockingWindow::MouseMove( rMEvt );
|
|
}
|
|
|
|
void ToolBox::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
// only trigger toolbox for left mouse button and when
|
|
// we're not in normal operation
|
|
if ( rMEvt.IsLeft() && !mbDrag && (mnCurPos == ITEM_NOTFOUND) )
|
|
{
|
|
// call activate already here, as items could
|
|
// be exchanged
|
|
Activate();
|
|
|
|
// update ToolBox here, such that user knows it
|
|
if ( mbFormat )
|
|
{
|
|
ImplFormat();
|
|
PaintImmediately();
|
|
}
|
|
|
|
Point aMousePos = rMEvt.GetPosPixel();
|
|
ImplToolItems::size_type i = 0;
|
|
ImplToolItems::size_type nNewPos = ITEM_NOTFOUND;
|
|
|
|
// search for item that was clicked
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
// is this the item?
|
|
if ( item.maRect.IsInside( aMousePos ) )
|
|
{
|
|
// do nothing if it is a separator or
|
|
// if the item has been disabled
|
|
if ( (item.meType == ToolBoxItemType::BUTTON) &&
|
|
!item.mbShowWindow )
|
|
nNewPos = i;
|
|
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
// item found
|
|
if ( nNewPos != ITEM_NOTFOUND )
|
|
{
|
|
if ( !mpData->m_aItems[nNewPos].mbEnabled )
|
|
{
|
|
Deactivate();
|
|
return;
|
|
}
|
|
|
|
// update actual data
|
|
StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
|
|
mnCurPos = i;
|
|
mnCurItemId = mpData->m_aItems[nNewPos].mnId;
|
|
mnDownItemId = mnCurItemId;
|
|
mnMouseModifier = rMEvt.GetModifier();
|
|
if ( mpData->m_aItems[nNewPos].mnBits & ToolBoxItemBits::REPEAT )
|
|
nTrackFlags |= StartTrackingFlags::ButtonRepeat;
|
|
|
|
// update bDrag here, as it is evaluated in the EndSelection
|
|
mbDrag = true;
|
|
|
|
// on double-click: only call the handler, but do so before the button
|
|
// is hit, as in the handler dragging
|
|
// can be terminated
|
|
if ( rMEvt.GetClicks() == 2 )
|
|
DoubleClick();
|
|
|
|
if ( mbDrag )
|
|
{
|
|
InvalidateItem(mnCurPos);
|
|
Highlight();
|
|
}
|
|
|
|
// was dropdown arrow pressed
|
|
if( mpData->m_aItems[nNewPos].mnBits & ToolBoxItemBits::DROPDOWN )
|
|
{
|
|
if( ( (mpData->m_aItems[nNewPos].mnBits & ToolBoxItemBits::DROPDOWNONLY) == ToolBoxItemBits::DROPDOWNONLY)
|
|
|| mpData->m_aItems[nNewPos].GetDropDownRect( mbHorz ).IsInside( aMousePos ))
|
|
{
|
|
// dropdownonly always triggers the dropdown handler, over the whole button area
|
|
|
|
// the drop down arrow should not trigger the item action
|
|
mpData->mbDropDownByKeyboard = false;
|
|
mpData->maDropdownClickHdl.Call( this );
|
|
|
|
// do not reset data if the dropdown handler opened a floating window
|
|
// see ImplFloatControl()
|
|
if( !mpFloatWin )
|
|
{
|
|
// no floater was opened
|
|
Deactivate();
|
|
InvalidateItem(mnCurPos);
|
|
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
mnCurItemId = 0;
|
|
mnDownItemId = 0;
|
|
mnMouseModifier = 0;
|
|
mnHighItemId = 0;
|
|
}
|
|
return;
|
|
}
|
|
else // activate long click timer
|
|
mpData->maDropdownTimer.Start();
|
|
}
|
|
|
|
// call Click handler
|
|
if ( rMEvt.GetClicks() != 2 )
|
|
Click();
|
|
|
|
// also call Select handler at repeat
|
|
if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
|
|
Select();
|
|
|
|
// if the actions was not aborted in Click handler
|
|
if ( mbDrag )
|
|
StartTracking( nTrackFlags );
|
|
|
|
// if mouse was clicked over an item we
|
|
// can abort here
|
|
return;
|
|
}
|
|
|
|
Deactivate();
|
|
|
|
// menu button hit ?
|
|
if( mpData->maMenubuttonItem.maRect.IsInside( aMousePos ) && ImplHasClippedItems() )
|
|
{
|
|
if ( maMenuButtonHdl.IsSet() )
|
|
maMenuButtonHdl.Call( this );
|
|
else
|
|
ExecuteCustomMenu( mpData->maMenubuttonItem.maRect );
|
|
return;
|
|
}
|
|
|
|
// check scroll- and next-buttons here
|
|
if ( maUpperRect.IsInside( aMousePos ) )
|
|
{
|
|
if ( mnCurLine > 1 )
|
|
{
|
|
StartTracking();
|
|
mbUpper = true;
|
|
mbIn = true;
|
|
InvalidateSpin(true, false);
|
|
}
|
|
return;
|
|
}
|
|
if ( maLowerRect.IsInside( aMousePos ) )
|
|
{
|
|
if ( mnCurLine+mnVisLines-1 < mnCurLines )
|
|
{
|
|
StartTracking();
|
|
mbLower = true;
|
|
mbIn = true;
|
|
InvalidateSpin(false);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Linesizing testen
|
|
if ( (mnWinStyle & TB_WBLINESIZING) == TB_WBLINESIZING )
|
|
{
|
|
sal_uInt16 nLineMode = ImplTestLineSize( aMousePos );
|
|
if ( nLineMode )
|
|
{
|
|
ImplTBDragMgr* pMgr = ImplGetTBDragMgr();
|
|
|
|
// call handler, such that we can set the
|
|
// dock rectangles
|
|
StartDocking();
|
|
|
|
Point aPos = GetParent()->OutputToScreenPixel( GetPosPixel() );
|
|
Size aSize = GetSizePixel();
|
|
aPos = ScreenToOutputPixel( aPos );
|
|
|
|
// start dragging
|
|
pMgr->StartDragging( this, aMousePos, tools::Rectangle( aPos, aSize ),
|
|
nLineMode );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// no item, then only click or double click
|
|
if ( rMEvt.GetClicks() == 2 )
|
|
DoubleClick();
|
|
else
|
|
Click();
|
|
}
|
|
|
|
if ( !mbDrag && (mnCurPos == ITEM_NOTFOUND) )
|
|
DockingWindow::MouseButtonDown( rMEvt );
|
|
}
|
|
|
|
void ToolBox::MouseButtonUp( const MouseEvent& rMEvt )
|
|
{
|
|
if ( ImplHandleMouseButtonUp( rMEvt ) )
|
|
return;
|
|
|
|
if ( mbDragging && rMEvt.IsLeft() )
|
|
{
|
|
ImplTBDragMgr* pMgr = ImplGetTBDragMgr();
|
|
pMgr->EndDragging();
|
|
return;
|
|
}
|
|
|
|
DockingWindow::MouseButtonUp( rMEvt );
|
|
}
|
|
|
|
void ToolBox::Tracking( const TrackingEvent& rTEvt )
|
|
{
|
|
VclPtr<vcl::Window> xWindow = this;
|
|
|
|
if ( rTEvt.IsTrackingEnded() )
|
|
ImplHandleMouseButtonUp( rTEvt.GetMouseEvent(), rTEvt.IsTrackingCanceled() );
|
|
else
|
|
ImplHandleMouseMove( rTEvt.GetMouseEvent(), rTEvt.IsTrackingRepeat() );
|
|
|
|
if ( xWindow->IsDisposed() )
|
|
// toolbox was deleted
|
|
return;
|
|
DockingWindow::Tracking( rTEvt );
|
|
}
|
|
|
|
void ToolBox::InvalidateItem(ImplToolItems::size_type nPosition)
|
|
{
|
|
if (mpData && nPosition < mpData->m_aItems.size())
|
|
{
|
|
ImplToolItem* pItem = &mpData->m_aItems[nPosition];
|
|
Invalidate(pItem->maRect);
|
|
}
|
|
}
|
|
|
|
void ToolBox::InvalidateMenuButton()
|
|
{
|
|
if (!mpData->maMenubuttonItem.maRect.IsEmpty())
|
|
Invalidate(mpData->maMenubuttonItem.maRect);
|
|
}
|
|
|
|
void ToolBox::InvalidateSpin(bool bUpperIn, bool bLowerIn)
|
|
{
|
|
if (bUpperIn && !maUpperRect.IsEmpty())
|
|
Invalidate(maUpperRect);
|
|
|
|
if (bLowerIn && !maLowerRect.IsEmpty())
|
|
Invalidate(maLowerRect);
|
|
}
|
|
|
|
void ToolBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rPaintRect)
|
|
{
|
|
if( mpData->mbIsPaintLocked )
|
|
return;
|
|
|
|
if (rPaintRect == tools::Rectangle(0, 0, mnDX-1, mnDY-1))
|
|
mbFullPaint = true;
|
|
ImplFormat();
|
|
mbFullPaint = false;
|
|
|
|
ImplDrawBackground(rRenderContext, rPaintRect);
|
|
|
|
if ( (mnWinStyle & WB_BORDER) && !ImplIsFloatingMode() )
|
|
ImplDrawBorder(rRenderContext);
|
|
|
|
if( !ImplIsFloatingMode() )
|
|
ImplDrawGrip(rRenderContext);
|
|
|
|
ImplDrawMenuButton(rRenderContext, mpData->mbMenubuttonSelected);
|
|
|
|
// draw SpinButtons
|
|
if (mnWinStyle & WB_SCROLL)
|
|
{
|
|
if (mnCurLines > mnLines)
|
|
ImplDrawSpin(rRenderContext);
|
|
}
|
|
|
|
// draw buttons
|
|
ImplToolItems::size_type nHighPos;
|
|
if ( mnHighItemId )
|
|
nHighPos = GetItemPos( mnHighItemId );
|
|
else
|
|
nHighPos = ITEM_NOTFOUND;
|
|
|
|
ImplToolItems::size_type nCount = mpData->m_aItems.size();
|
|
for( ImplToolItems::size_type i = 0; i < nCount; i++ )
|
|
{
|
|
ImplToolItem* pItem = &mpData->m_aItems[i];
|
|
|
|
// only draw when the rectangle is in the draw rectangle
|
|
if ( !pItem->maRect.IsEmpty() && rPaintRect.IsOver( pItem->maRect ) )
|
|
{
|
|
sal_uInt16 nHighlight = 0;
|
|
if ( i == mnCurPos )
|
|
nHighlight = 1;
|
|
else if ( i == nHighPos )
|
|
nHighlight = 2;
|
|
ImplDrawItem(rRenderContext, i, nHighlight);
|
|
}
|
|
}
|
|
ImplShowFocus();
|
|
}
|
|
|
|
void ToolBox::Resize()
|
|
{
|
|
Size aSize = GetOutputSizePixel();
|
|
// #i31422# some WindowManagers send (0,0) sizes when
|
|
// switching virtual desktops - ignore this and avoid reformatting
|
|
if( !aSize.Width() && !aSize.Height() )
|
|
return;
|
|
|
|
long nOldDX = mnDX;
|
|
long nOldDY = mnDY;
|
|
mnDX = aSize.Width();
|
|
mnDY = aSize.Height();
|
|
|
|
mnLastResizeDY = 0;
|
|
|
|
// invalidate everything to have gradient backgrounds properly drawn
|
|
Invalidate();
|
|
|
|
// If we have any expandable entries, then force a reformat first using
|
|
// their optimal sizes, then share out the excess space evenly across those
|
|
// expandables and reformat again
|
|
std::vector<size_t> aExpandables;
|
|
for (size_t i = 0; i < mpData->m_aItems.size(); ++i)
|
|
{
|
|
if (mpData->m_aItems[i].mbExpand)
|
|
{
|
|
vcl::Window *pWindow = mpData->m_aItems[i].mpWindow;
|
|
SAL_INFO_IF(!pWindow, "vcl.layout", "only tabitems with window supported at the moment");
|
|
if (!pWindow)
|
|
continue;
|
|
Size aWinSize(pWindow->GetSizePixel());
|
|
Size aPrefSize(pWindow->get_preferred_size());
|
|
aWinSize.setWidth( aPrefSize.Width() );
|
|
pWindow->SetSizePixel(aWinSize);
|
|
aExpandables.push_back(i);
|
|
}
|
|
}
|
|
|
|
// re-format or re-draw
|
|
if ( mbScroll || !aExpandables.empty() )
|
|
{
|
|
if ( !mbFormat || !aExpandables.empty() )
|
|
{
|
|
mbFormat = true;
|
|
if( IsReallyVisible() || !aExpandables.empty() )
|
|
{
|
|
ImplFormat(true);
|
|
|
|
if (!aExpandables.empty())
|
|
{
|
|
//Get how big the optimal size is
|
|
tools::Rectangle aBounds;
|
|
for (const ImplToolItem & rItem : mpData->m_aItems)
|
|
{
|
|
aBounds.Union( rItem.maRect );
|
|
}
|
|
|
|
auto nOptimalWidth = aBounds.GetWidth();
|
|
auto nDiff = aSize.Width() - nOptimalWidth;
|
|
decltype(nDiff) nExpandablesSize = aExpandables.size();
|
|
nDiff /= nExpandablesSize;
|
|
|
|
//share out the diff from optimal to real across
|
|
//expandable entries
|
|
for (size_t nIndex : aExpandables)
|
|
{
|
|
vcl::Window *pWindow = mpData->m_aItems[nIndex].mpWindow;
|
|
Size aWinSize(pWindow->GetSizePixel());
|
|
Size aPrefSize(pWindow->get_preferred_size());
|
|
aWinSize.setWidth( aPrefSize.Width() + nDiff );
|
|
pWindow->SetSizePixel(aWinSize);
|
|
}
|
|
|
|
//now reformat with final sizes
|
|
mbFormat = true;
|
|
ImplFormat(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// redraw border
|
|
if ( mnWinStyle & WB_BORDER )
|
|
{
|
|
// as otherwise, when painting we might think we have to re-draw everything
|
|
if ( mbFormat && IsReallyVisible() )
|
|
Invalidate();
|
|
else
|
|
{
|
|
if ( mnRightBorder )
|
|
{
|
|
if ( nOldDX > mnDX )
|
|
Invalidate( tools::Rectangle( mnDX-mnRightBorder-1, 0, mnDX, mnDY ) );
|
|
else
|
|
Invalidate( tools::Rectangle( nOldDX-mnRightBorder-1, 0, nOldDX, nOldDY ) );
|
|
}
|
|
|
|
if ( mnBottomBorder )
|
|
{
|
|
if ( nOldDY > mnDY )
|
|
Invalidate( tools::Rectangle( 0, mnDY-mnBottomBorder-1, mnDX, mnDY ) );
|
|
else
|
|
Invalidate( tools::Rectangle( 0, nOldDY-mnBottomBorder-1, nOldDX, nOldDY ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool DispatchableCommand(const OUString& rName)
|
|
{
|
|
return rName.startsWith(".uno") ||
|
|
rName.startsWith("slot:") ||
|
|
rName.startsWith("macro:") ||
|
|
rName.startsWith("vnd.sun.star.script");
|
|
}
|
|
}
|
|
|
|
const OUString& ToolBox::ImplGetHelpText( sal_uInt16 nItemId ) const
|
|
{
|
|
ImplToolItem* pItem = ImplGetItem( nItemId );
|
|
|
|
assert( pItem );
|
|
|
|
if ( pItem->maHelpText.isEmpty() && ( !pItem->maHelpId.isEmpty() || pItem->maCommandStr.getLength() ))
|
|
{
|
|
Help* pHelp = Application::GetHelp();
|
|
if ( pHelp )
|
|
{
|
|
if (DispatchableCommand(pItem->maCommandStr))
|
|
pItem->maHelpText = pHelp->GetHelpText( pItem->maCommandStr, this );
|
|
if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
|
|
pItem->maHelpText = pHelp->GetHelpText( OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this );
|
|
}
|
|
}
|
|
|
|
return pItem->maHelpText;
|
|
}
|
|
|
|
void ToolBox::RequestHelp( const HelpEvent& rHEvt )
|
|
{
|
|
sal_uInt16 nItemId;
|
|
Point aHelpPos;
|
|
|
|
if( !rHEvt.KeyboardActivated() )
|
|
{
|
|
nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
|
|
aHelpPos = rHEvt.GetMousePosPixel();
|
|
}
|
|
else
|
|
{
|
|
if( !mnHighItemId )
|
|
return;
|
|
else
|
|
nItemId = mnHighItemId;
|
|
tools::Rectangle aRect( GetItemRect( nItemId ) );
|
|
if( aRect.IsEmpty() )
|
|
return;
|
|
else
|
|
aHelpPos = OutputToScreenPixel( aRect.Center() );
|
|
}
|
|
|
|
if ( nItemId )
|
|
{
|
|
if ( rHEvt.GetMode() & (HelpEventMode::BALLOON | HelpEventMode::QUICK) )
|
|
{
|
|
// get rectangle
|
|
tools::Rectangle aTempRect = GetItemRect( nItemId );
|
|
Point aPt = OutputToScreenPixel( aTempRect.TopLeft() );
|
|
aTempRect.SetLeft( aPt.X() );
|
|
aTempRect.SetTop( aPt.Y() );
|
|
aPt = OutputToScreenPixel( aTempRect.BottomRight() );
|
|
aTempRect.SetRight( aPt.X() );
|
|
aTempRect.SetBottom( aPt.Y() );
|
|
|
|
// get text and display it
|
|
OUString aStr = GetQuickHelpText( nItemId );
|
|
if (aStr.isEmpty())
|
|
aStr = MnemonicGenerator::EraseAllMnemonicChars( GetItemText( nItemId ) );
|
|
if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
|
|
{
|
|
const OUString& rHelpStr = GetHelpText( nItemId );
|
|
if (!rHelpStr.isEmpty())
|
|
aStr = rHelpStr;
|
|
Help::ShowBalloon( this, aHelpPos, aTempRect, aStr );
|
|
}
|
|
else
|
|
Help::ShowQuickHelp( this, aTempRect, aStr, QuickHelpFlags::CtrlText );
|
|
return;
|
|
}
|
|
}
|
|
|
|
DockingWindow::RequestHelp( rHEvt );
|
|
}
|
|
|
|
bool ToolBox::EventNotify( NotifyEvent& rNEvt )
|
|
{
|
|
if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
|
|
{
|
|
KeyEvent aKEvt = *rNEvt.GetKeyEvent();
|
|
vcl::KeyCode aKeyCode = aKEvt.GetKeyCode();
|
|
sal_uInt16 nKeyCode = aKeyCode.GetCode();
|
|
switch( nKeyCode )
|
|
{
|
|
case KEY_TAB:
|
|
{
|
|
// internal TAB cycling only if parent is not a dialog or if we are the only child
|
|
// otherwise the dialog control will take over
|
|
vcl::Window *pParent = ImplGetParent();
|
|
bool bOldSchoolContainer =
|
|
((pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL &&
|
|
pParent->GetChildCount() != 1);
|
|
bool bNoTabCycling = bOldSchoolContainer || isContainerWindow(pParent);
|
|
|
|
if( bNoTabCycling )
|
|
return DockingWindow::EventNotify( rNEvt );
|
|
else if( ImplChangeHighlightUpDn( aKeyCode.IsShift() , bNoTabCycling ) )
|
|
return true;
|
|
else
|
|
return DockingWindow::EventNotify( rNEvt );
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
|
|
{
|
|
if( rNEvt.GetWindow() == this )
|
|
{
|
|
// the toolbar itself got the focus
|
|
if( mnLastFocusItemId != 0 || mpData->mbMenubuttonWasLastSelected )
|
|
{
|
|
// restore last item
|
|
if( mpData->mbMenubuttonWasLastSelected )
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
mpData->mbMenubuttonSelected = true;
|
|
InvalidateMenuButton();
|
|
}
|
|
else
|
|
{
|
|
ImplChangeHighlight( ImplGetItem( mnLastFocusItemId ) );
|
|
mnLastFocusItemId = 0;
|
|
}
|
|
}
|
|
else if( (GetGetFocusFlags() & (GetFocusFlags::Backward|GetFocusFlags::Tab) ) == (GetFocusFlags::Backward|GetFocusFlags::Tab))
|
|
// Shift-TAB was pressed in the parent
|
|
ImplChangeHighlightUpDn( false );
|
|
else
|
|
ImplChangeHighlightUpDn( true );
|
|
|
|
mnLastFocusItemId = 0;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// a child window got the focus so update current item to
|
|
// allow for proper lose focus handling in keyboard navigation
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if ( item.mbVisible )
|
|
{
|
|
if ( item.mpWindow && item.mpWindow->ImplIsWindowOrChild( rNEvt.GetWindow() ) )
|
|
{
|
|
mnHighItemId = item.mnId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return DockingWindow::EventNotify( rNEvt );
|
|
}
|
|
}
|
|
else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
|
|
{
|
|
// deselect
|
|
ImplHideFocus();
|
|
mpData->mbMenubuttonWasLastSelected = false;
|
|
mnHighItemId = 0;
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
}
|
|
|
|
return DockingWindow::EventNotify( rNEvt );
|
|
}
|
|
|
|
void ToolBox::Command( const CommandEvent& rCEvt )
|
|
{
|
|
if ( rCEvt.GetCommand() == CommandEventId::Wheel )
|
|
{
|
|
if ( (mnCurLine > 1) || (mnCurLine+mnVisLines-1 < mnCurLines) )
|
|
{
|
|
const CommandWheelData* pData = rCEvt.GetWheelData();
|
|
if ( pData->GetMode() == CommandWheelMode::SCROLL )
|
|
{
|
|
if ( (mnCurLine > 1) && (pData->GetDelta() > 0) )
|
|
ShowLine( false );
|
|
else if ( (mnCurLine+mnVisLines-1 < mnCurLines) && (pData->GetDelta() < 0) )
|
|
ShowLine( true );
|
|
InvalidateSpin();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
|
|
{
|
|
ExecuteCustomMenu( tools::Rectangle( rCEvt.GetMousePosPixel(), rCEvt.GetMousePosPixel() ) );
|
|
return;
|
|
}
|
|
|
|
DockingWindow::Command( rCEvt );
|
|
}
|
|
|
|
void ToolBox::StateChanged( StateChangedType nType )
|
|
{
|
|
DockingWindow::StateChanged( nType );
|
|
|
|
if ( nType == StateChangedType::InitShow )
|
|
ImplFormat();
|
|
else if ( nType == StateChangedType::Enable )
|
|
ImplUpdateItem();
|
|
else if ( nType == StateChangedType::UpdateMode )
|
|
{
|
|
if ( IsUpdateMode() )
|
|
Invalidate();
|
|
}
|
|
else if ( (nType == StateChangedType::Zoom) ||
|
|
(nType == StateChangedType::ControlFont) )
|
|
{
|
|
mbCalc = true;
|
|
mbFormat = true;
|
|
ImplInitSettings( true, false, false );
|
|
Invalidate();
|
|
}
|
|
else if ( nType == StateChangedType::ControlForeground )
|
|
{
|
|
ImplInitSettings( false, true, false );
|
|
Invalidate();
|
|
}
|
|
else if ( nType == StateChangedType::ControlBackground )
|
|
{
|
|
ImplInitSettings( false, false, true ); // font, foreground, background
|
|
Invalidate();
|
|
}
|
|
|
|
maStateChangedHandler.Call( &nType );
|
|
}
|
|
|
|
void ToolBox::DataChanged( const DataChangedEvent& rDCEvt )
|
|
{
|
|
DockingWindow::DataChanged( rDCEvt );
|
|
|
|
if ( (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
|
|
(rDCEvt.GetType() == DataChangedEventType::FONTS) ||
|
|
(rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
|
|
((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
|
|
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
|
|
{
|
|
mbCalc = true;
|
|
mbFormat = true;
|
|
ImplInitSettings( true, true, true );
|
|
Invalidate();
|
|
}
|
|
|
|
maDataChangedHandler.Call( &rDCEvt );
|
|
}
|
|
|
|
void ToolBox::statusChanged( const css::frame::FeatureStateEvent& Event )
|
|
{
|
|
// Update image mirroring/rotation
|
|
if ( Event.FeatureURL.Complete == ".uno:ImageOrientation" )
|
|
{
|
|
SfxImageItem aItem( 1 );
|
|
aItem.PutValue( Event.State, 0 );
|
|
|
|
mbImagesMirrored = aItem.IsMirrored();
|
|
mnImagesRotationAngle = aItem.GetRotation();
|
|
|
|
// update image orientation
|
|
OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(mpStatusListener->getFrame()));
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if (vcl::CommandInfoProvider::IsMirrored(item.maCommandStr, aModuleName))
|
|
SetItemImageMirrorMode(item.mnId, mbImagesMirrored);
|
|
if (vcl::CommandInfoProvider::IsRotated(item.maCommandStr, aModuleName))
|
|
SetItemImageAngle(item.mnId, mnImagesRotationAngle);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::SetStyle(WinBits nNewStyle)
|
|
{
|
|
mnWinStyle = nNewStyle;
|
|
if (!ImplIsFloatingMode())
|
|
{
|
|
bool bOldScroll = mbScroll;
|
|
mbScroll = (mnWinStyle & WB_SCROLL) != 0;
|
|
if (mbScroll != bOldScroll)
|
|
{
|
|
mbFormat = true;
|
|
ImplFormat();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ToggleFloatingMode()
|
|
{
|
|
DockingWindow::ToggleFloatingMode();
|
|
|
|
if (!mpData)
|
|
return;
|
|
|
|
bool bOldHorz = mbHorz;
|
|
|
|
if ( ImplIsFloatingMode() )
|
|
{
|
|
mbHorz = true;
|
|
meAlign = WindowAlign::Top;
|
|
mbScroll = true;
|
|
|
|
if( bOldHorz != mbHorz )
|
|
mbCalc = true; // orientation was changed !
|
|
|
|
ImplSetMinMaxFloatSize();
|
|
SetOutputSizePixel( ImplCalcFloatSize( mnFloatLines ) );
|
|
}
|
|
else
|
|
{
|
|
mbScroll = (mnWinStyle & WB_SCROLL) != 0;
|
|
if ( (meAlign == WindowAlign::Top) || (meAlign == WindowAlign::Bottom) )
|
|
mbHorz = true;
|
|
else
|
|
mbHorz = false;
|
|
|
|
// set focus back to document
|
|
ImplGetFrameWindow()->GetWindow( GetWindowType::Client )->GrabFocus();
|
|
}
|
|
|
|
if( bOldHorz != mbHorz )
|
|
{
|
|
// if orientation changes, the toolbox has to be initialized again
|
|
// to update the direction of the gradient
|
|
mbCalc = true;
|
|
ImplInitSettings( true, true, true );
|
|
}
|
|
|
|
mbFormat = true;
|
|
ImplFormat();
|
|
}
|
|
|
|
void ToolBox::StartDocking()
|
|
{
|
|
meDockAlign = meAlign;
|
|
mnDockLines = mnLines;
|
|
mbLastFloatMode = ImplIsFloatingMode();
|
|
DockingWindow::StartDocking();
|
|
}
|
|
|
|
bool ToolBox::Docking( const Point& rPos, tools::Rectangle& rRect )
|
|
{
|
|
// do nothing during dragging, it was calculated before
|
|
if ( mbDragging )
|
|
return false;
|
|
|
|
bool bFloatMode = false;
|
|
|
|
DockingWindow::Docking( rPos, rRect );
|
|
|
|
// if the mouse is outside the area, it can only become a floating window
|
|
tools::Rectangle aDockingRect( rRect );
|
|
if ( !ImplIsFloatingMode() )
|
|
{
|
|
// don't use tracking rectangle for alignment check, because it will be too large
|
|
// to get a floating mode as result - switch to floating size
|
|
// so the calculation only depends on the position of the rectangle, not the current
|
|
// docking state of the window
|
|
ImplToolItems::size_type nTemp = 0;
|
|
aDockingRect.SetSize( ImplCalcFloatSize( nTemp ) );
|
|
|
|
// in this mode docking is never done by keyboard, so it's OK to use the mouse position
|
|
aDockingRect.SetPos( ImplGetFrameWindow()->GetPointerPosPixel() );
|
|
}
|
|
|
|
bFloatMode = true;
|
|
|
|
meDockAlign = meAlign;
|
|
if ( !mbLastFloatMode )
|
|
{
|
|
ImplToolItems::size_type nTemp = 0;
|
|
aDockingRect.SetSize( ImplCalcFloatSize( nTemp ) );
|
|
}
|
|
|
|
rRect = aDockingRect;
|
|
mbLastFloatMode = bFloatMode;
|
|
|
|
return bFloatMode;
|
|
}
|
|
|
|
void ToolBox::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
|
|
{
|
|
if ( !IsDockingCanceled() )
|
|
{
|
|
if ( mnLines != mnDockLines )
|
|
SetLineCount( mnDockLines );
|
|
if ( meAlign != meDockAlign )
|
|
SetAlign( meDockAlign );
|
|
}
|
|
if ( bFloatMode || (bFloatMode != ImplIsFloatingMode()) )
|
|
DockingWindow::EndDocking( rRect, bFloatMode );
|
|
}
|
|
|
|
void ToolBox::Resizing( Size& rSize )
|
|
{
|
|
ImplToolItems::size_type nCalcLines;
|
|
ImplToolItems::size_type nTemp;
|
|
|
|
// calculate all floating sizes
|
|
ImplCalcFloatSizes();
|
|
|
|
if ( !mnLastResizeDY )
|
|
mnLastResizeDY = mnDY;
|
|
|
|
// is vertical resizing needed
|
|
if ( (mnLastResizeDY != rSize.Height()) && (mnDY != rSize.Height()) )
|
|
{
|
|
nCalcLines = ImplCalcLines( rSize.Height() );
|
|
if ( nCalcLines < 1 )
|
|
nCalcLines = 1;
|
|
rSize = ImplCalcFloatSize( nCalcLines );
|
|
}
|
|
else
|
|
{
|
|
nCalcLines = 1;
|
|
nTemp = nCalcLines;
|
|
Size aTempSize = ImplCalcFloatSize( nTemp );
|
|
while ( (aTempSize.Width() > rSize.Width()) &&
|
|
(nCalcLines <= maFloatSizes[0].mnLines) )
|
|
{
|
|
nCalcLines++;
|
|
nTemp = nCalcLines;
|
|
aTempSize = ImplCalcFloatSize( nTemp );
|
|
}
|
|
rSize = aTempSize;
|
|
}
|
|
|
|
mnLastResizeDY = rSize.Height();
|
|
}
|
|
|
|
Size ToolBox::GetOptimalSize() const
|
|
{
|
|
// If we have any expandable entries, then force them to their
|
|
// optimal sizes, then reset them afterwards
|
|
std::map<vcl::Window*, Size> aExpandables;
|
|
for (const ImplToolItem & rItem : mpData->m_aItems)
|
|
{
|
|
if (rItem.mbExpand)
|
|
{
|
|
vcl::Window *pWindow = rItem.mpWindow;
|
|
SAL_INFO_IF(!pWindow, "vcl.layout", "only tabitems with window supported at the moment");
|
|
if (!pWindow)
|
|
continue;
|
|
Size aWinSize(pWindow->GetSizePixel());
|
|
aExpandables[pWindow] = aWinSize;
|
|
Size aPrefSize(pWindow->get_preferred_size());
|
|
aWinSize.setWidth( aPrefSize.Width() );
|
|
pWindow->SetSizePixel(aWinSize);
|
|
}
|
|
}
|
|
|
|
Size aSize(const_cast<ToolBox *>(this)->ImplCalcSize( mnLines ));
|
|
|
|
for (auto const& expandable : aExpandables)
|
|
{
|
|
vcl::Window *pWindow = expandable.first;
|
|
Size aWinSize = expandable.second;
|
|
pWindow->SetSizePixel(aWinSize);
|
|
}
|
|
|
|
return aSize;
|
|
}
|
|
|
|
Size ToolBox::CalcWindowSizePixel( ImplToolItems::size_type nCalcLines )
|
|
{
|
|
return ImplCalcSize( nCalcLines );
|
|
}
|
|
|
|
Size ToolBox::CalcWindowSizePixel( ImplToolItems::size_type nCalcLines, WindowAlign eAlign )
|
|
{
|
|
return ImplCalcSize( nCalcLines,
|
|
(eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom) ? TB_CALCMODE_HORZ : TB_CALCMODE_VERT );
|
|
}
|
|
|
|
ToolBox::ImplToolItems::size_type ToolBox::ImplCountLineBreaks() const
|
|
{
|
|
ImplToolItems::size_type nLines = 0;
|
|
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if( item.meType == ToolBoxItemType::BREAK )
|
|
++nLines;
|
|
}
|
|
return nLines;
|
|
}
|
|
|
|
Size ToolBox::CalcPopupWindowSizePixel()
|
|
{
|
|
// count number of breaks and calc corresponding floating window size
|
|
ImplToolItems::size_type nLines = ImplCountLineBreaks();
|
|
|
|
if( nLines )
|
|
++nLines; // add the first line
|
|
else
|
|
{
|
|
// no breaks found: use quadratic layout
|
|
nLines = static_cast<ImplToolItems::size_type>(ceil( sqrt( static_cast<double>(GetItemCount()) ) ));
|
|
}
|
|
|
|
bool bPopup = mpData->mbAssumePopupMode;
|
|
mpData->mbAssumePopupMode = true;
|
|
|
|
Size aSize = CalcFloatingWindowSizePixel( nLines );
|
|
|
|
mpData->mbAssumePopupMode = bPopup;
|
|
return aSize;
|
|
}
|
|
|
|
Size ToolBox::CalcFloatingWindowSizePixel()
|
|
{
|
|
ImplToolItems::size_type nLines = ImplCountLineBreaks();
|
|
++nLines; // add the first line
|
|
return CalcFloatingWindowSizePixel( nLines );
|
|
}
|
|
|
|
Size ToolBox::CalcFloatingWindowSizePixel( ImplToolItems::size_type nCalcLines )
|
|
{
|
|
bool bFloat = mpData->mbAssumeFloating;
|
|
bool bDocking = mpData->mbAssumeDocked;
|
|
|
|
// simulate floating mode and force reformat before calculating
|
|
mpData->mbAssumeFloating = true;
|
|
mpData->mbAssumeDocked = false;
|
|
|
|
Size aSize = ImplCalcFloatSize( nCalcLines );
|
|
|
|
mbFormat = true;
|
|
mpData->mbAssumeFloating = bFloat;
|
|
mpData->mbAssumeDocked = bDocking;
|
|
|
|
return aSize;
|
|
}
|
|
|
|
Size ToolBox::CalcMinimumWindowSizePixel()
|
|
{
|
|
if( ImplIsFloatingMode() )
|
|
return ImplCalcSize( mnFloatLines );
|
|
else
|
|
{
|
|
// create dummy toolbox for measurements
|
|
VclPtrInstance< ToolBox > pToolBox( GetParent(), GetStyle() );
|
|
|
|
// copy until first useful item
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
pToolBox->CopyItem( *this, item.mnId );
|
|
if( (item.meType == ToolBoxItemType::BUTTON) &&
|
|
item.mbVisible && !ImplIsFixedControl( &item ) )
|
|
break;
|
|
}
|
|
|
|
// add to docking manager if required to obtain a drag area
|
|
// (which is accounted for in calcwindowsizepixel)
|
|
if( ImplGetDockingManager()->GetDockingWindowWrapper( this ) )
|
|
ImplGetDockingManager()->AddWindow( pToolBox );
|
|
|
|
// account for menu
|
|
if( IsMenuEnabled() )
|
|
pToolBox->SetMenuType( GetMenuType() );
|
|
|
|
pToolBox->SetAlign( GetAlign() );
|
|
Size aSize = pToolBox->CalcWindowSizePixel( 1 );
|
|
|
|
ImplGetDockingManager()->RemoveWindow( pToolBox );
|
|
pToolBox->Clear();
|
|
|
|
pToolBox.disposeAndClear();
|
|
|
|
return aSize;
|
|
}
|
|
}
|
|
|
|
void ToolBox::EnableCustomize( bool bEnable )
|
|
{
|
|
mbCustomize = bEnable;
|
|
}
|
|
|
|
void ToolBox::LoseFocus()
|
|
{
|
|
ImplChangeHighlight( nullptr, true );
|
|
|
|
DockingWindow::LoseFocus();
|
|
}
|
|
|
|
// performs the action associated with an item, ie simulates clicking the item
|
|
void ToolBox::TriggerItem( sal_uInt16 nItemId )
|
|
{
|
|
mnHighItemId = nItemId;
|
|
vcl::KeyCode aKeyCode( 0, 0 );
|
|
ImplActivateItem( aKeyCode );
|
|
}
|
|
|
|
// calls the button's action handler
|
|
// returns true if action was called
|
|
bool ToolBox::ImplActivateItem( vcl::KeyCode aKeyCode )
|
|
{
|
|
bool bRet = true;
|
|
if( mnHighItemId )
|
|
{
|
|
ImplToolItem *pToolItem = ImplGetItem( mnHighItemId );
|
|
|
|
// #107712#, activate can also be called for disabled entries
|
|
if( pToolItem && !pToolItem->mbEnabled )
|
|
return true;
|
|
|
|
if( pToolItem && pToolItem->mpWindow && HasFocus() )
|
|
{
|
|
ImplHideFocus();
|
|
mbChangingHighlight = true; // avoid focus change due to loss of focus
|
|
pToolItem->mpWindow->ImplControlFocus( GetFocusFlags::Tab );
|
|
mbChangingHighlight = false;
|
|
}
|
|
else
|
|
{
|
|
mnDownItemId = mnCurItemId = mnHighItemId;
|
|
if (pToolItem && (pToolItem->mnBits & ToolBoxItemBits::AUTOCHECK))
|
|
{
|
|
if ( pToolItem->mnBits & ToolBoxItemBits::RADIOCHECK )
|
|
{
|
|
if ( pToolItem->meState != TRISTATE_TRUE )
|
|
SetItemState( pToolItem->mnId, TRISTATE_TRUE );
|
|
}
|
|
else
|
|
{
|
|
if ( pToolItem->meState != TRISTATE_TRUE )
|
|
pToolItem->meState = TRISTATE_TRUE;
|
|
else
|
|
pToolItem->meState = TRISTATE_FALSE;
|
|
}
|
|
}
|
|
mnMouseModifier = aKeyCode.GetModifier();
|
|
mbIsKeyEvent = true;
|
|
Activate();
|
|
Click();
|
|
|
|
// #107776# we might be destroyed in the selecthandler
|
|
VclPtr<vcl::Window> xWindow = this;
|
|
Select();
|
|
if ( xWindow->IsDisposed() )
|
|
return bRet;
|
|
|
|
Deactivate();
|
|
mbIsKeyEvent = false;
|
|
mnMouseModifier = 0;
|
|
}
|
|
}
|
|
else
|
|
bRet = false;
|
|
return bRet;
|
|
}
|
|
|
|
static bool ImplCloseLastPopup( vcl::Window const *pParent )
|
|
{
|
|
// close last popup toolbox (see also:
|
|
// ImplHandleMouseFloatMode(...) in winproc.cxx )
|
|
|
|
if (ImplGetSVData()->mpWinData->mpFirstFloat)
|
|
{
|
|
FloatingWindow* pLastLevelFloat = ImplGetSVData()->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
|
|
// only close the floater if it is not our direct parent, which would kill ourself
|
|
if( pLastLevelFloat && pLastLevelFloat != pParent )
|
|
{
|
|
pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// opens a drop down toolbox item
|
|
// returns true if item was opened
|
|
bool ToolBox::ImplOpenItem( vcl::KeyCode aKeyCode )
|
|
{
|
|
sal_uInt16 nCode = aKeyCode.GetCode();
|
|
bool bRet = true;
|
|
|
|
// arrow keys should work only in the opposite direction of alignment (to not break cursor travelling)
|
|
if ( ((nCode == KEY_LEFT || nCode == KEY_RIGHT) && IsHorizontal())
|
|
|| ((nCode == KEY_UP || nCode == KEY_DOWN) && !IsHorizontal()) )
|
|
return false;
|
|
|
|
if( mpData->mbMenubuttonSelected )
|
|
{
|
|
if( ImplCloseLastPopup( GetParent() ) )
|
|
return bRet;
|
|
mbIsKeyEvent = true;
|
|
if ( maMenuButtonHdl.IsSet() )
|
|
maMenuButtonHdl.Call( this );
|
|
else
|
|
ExecuteCustomMenu( mpData->maMenubuttonItem.maRect );
|
|
mpData->mbMenubuttonWasLastSelected = true;
|
|
mbIsKeyEvent = false;
|
|
}
|
|
else if( mnHighItemId && ImplGetItem( mnHighItemId ) &&
|
|
(ImplGetItem( mnHighItemId )->mnBits & ToolBoxItemBits::DROPDOWN) )
|
|
{
|
|
mnDownItemId = mnCurItemId = mnHighItemId;
|
|
mnCurPos = GetItemPos( mnCurItemId );
|
|
mnLastFocusItemId = mnCurItemId; // save item id for possible later focus restore
|
|
mnMouseModifier = aKeyCode.GetModifier();
|
|
mbIsKeyEvent = true;
|
|
Activate();
|
|
|
|
mpData->mbDropDownByKeyboard = true;
|
|
mpData->maDropdownClickHdl.Call( this );
|
|
|
|
mbIsKeyEvent = false;
|
|
mnMouseModifier = 0;
|
|
}
|
|
else
|
|
bRet = false;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void ToolBox::KeyInput( const KeyEvent& rKEvt )
|
|
{
|
|
vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
|
|
sal_uInt16 nCode = aKeyCode.GetCode();
|
|
|
|
vcl::Window *pParent = ImplGetParent();
|
|
bool bOldSchoolContainer = ((pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL);
|
|
bool bParentIsContainer = bOldSchoolContainer || isContainerWindow(pParent);
|
|
|
|
bool bForwardKey = false;
|
|
bool bGrabFocusToDocument = false;
|
|
|
|
// #107776# we might be destroyed in the keyhandler
|
|
VclPtr<vcl::Window> xWindow = this;
|
|
|
|
switch ( nCode )
|
|
{
|
|
case KEY_UP:
|
|
{
|
|
// Ctrl-Cursor activates next toolbox, indicated by a blue arrow pointing to the left/up
|
|
if( aKeyCode.GetModifier() ) // allow only pure cursor keys
|
|
break;
|
|
if( !IsHorizontal() )
|
|
ImplChangeHighlightUpDn( true );
|
|
else
|
|
ImplOpenItem( aKeyCode );
|
|
}
|
|
break;
|
|
case KEY_LEFT:
|
|
{
|
|
if( aKeyCode.GetModifier() ) // allow only pure cursor keys
|
|
break;
|
|
if( IsHorizontal() )
|
|
ImplChangeHighlightUpDn( true );
|
|
else
|
|
ImplOpenItem( aKeyCode );
|
|
}
|
|
break;
|
|
case KEY_DOWN:
|
|
{
|
|
if( aKeyCode.GetModifier() ) // allow only pure cursor keys
|
|
break;
|
|
if( !IsHorizontal() )
|
|
ImplChangeHighlightUpDn( false );
|
|
else
|
|
ImplOpenItem( aKeyCode );
|
|
}
|
|
break;
|
|
case KEY_RIGHT:
|
|
{
|
|
if( aKeyCode.GetModifier() ) // allow only pure cursor keys
|
|
break;
|
|
if( IsHorizontal() )
|
|
ImplChangeHighlightUpDn( false );
|
|
else
|
|
ImplOpenItem( aKeyCode );
|
|
}
|
|
break;
|
|
case KEY_PAGEUP:
|
|
if ( mnCurLine > 1 )
|
|
{
|
|
if( mnCurLine > mnVisLines )
|
|
mnCurLine = mnCurLine - mnVisLines;
|
|
else
|
|
mnCurLine = 1;
|
|
mbFormat = true;
|
|
ImplFormat();
|
|
InvalidateSpin();
|
|
ImplChangeHighlight( ImplGetFirstValidItem( mnCurLine ) );
|
|
}
|
|
break;
|
|
case KEY_PAGEDOWN:
|
|
if ( mnCurLine+mnVisLines-1 < mnCurLines )
|
|
{
|
|
if( mnCurLine + 2*mnVisLines-1 < mnCurLines )
|
|
mnCurLine = mnCurLine + mnVisLines;
|
|
else
|
|
mnCurLine = mnCurLines;
|
|
mbFormat = true;
|
|
ImplFormat();
|
|
InvalidateSpin();
|
|
ImplChangeHighlight( ImplGetFirstValidItem( mnCurLine ) );
|
|
}
|
|
break;
|
|
case KEY_END:
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
ImplChangeHighlightUpDn( false );
|
|
}
|
|
break;
|
|
case KEY_HOME:
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
ImplChangeHighlightUpDn( true );
|
|
}
|
|
break;
|
|
case KEY_ESCAPE:
|
|
{
|
|
if( !ImplIsFloatingMode() && bParentIsContainer )
|
|
DockingWindow::KeyInput( rKEvt );
|
|
else
|
|
{
|
|
// send focus to document pane
|
|
vcl::Window *pWin = this;
|
|
while( pWin )
|
|
{
|
|
if( !pWin->GetParent() )
|
|
{
|
|
pWin->ImplGetFrameWindow()->GetWindow( GetWindowType::Client )->GrabFocus();
|
|
break;
|
|
}
|
|
pWin = pWin->GetParent();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_RETURN:
|
|
{
|
|
// #107712#, disabled entries are selectable now
|
|
// leave toolbox and move focus to document
|
|
if( mnHighItemId )
|
|
{
|
|
ImplToolItem *pItem = ImplGetItem(mnHighItemId);
|
|
if (!pItem || !pItem->mbEnabled)
|
|
{
|
|
bGrabFocusToDocument = true;
|
|
}
|
|
}
|
|
if( !bGrabFocusToDocument )
|
|
bForwardKey = !ImplActivateItem( aKeyCode );
|
|
}
|
|
break;
|
|
case KEY_SPACE:
|
|
{
|
|
ImplOpenItem( aKeyCode );
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
sal_uInt16 aKeyGroup = aKeyCode.GetGroup();
|
|
ImplToolItem *pItem = nullptr;
|
|
if( mnHighItemId )
|
|
pItem = ImplGetItem( mnHighItemId );
|
|
// #i13931# forward alphanum keyinput into embedded control
|
|
if( (aKeyGroup == KEYGROUP_NUM || aKeyGroup == KEYGROUP_ALPHA ) && pItem && pItem->mpWindow && pItem->mbEnabled )
|
|
{
|
|
vcl::Window *pFocusWindow = Application::GetFocusWindow();
|
|
ImplHideFocus();
|
|
mbChangingHighlight = true; // avoid focus change due to loss of focus
|
|
pItem->mpWindow->ImplControlFocus( GetFocusFlags::Tab );
|
|
mbChangingHighlight = false;
|
|
if( pFocusWindow != Application::GetFocusWindow() )
|
|
Application::GetFocusWindow()->KeyInput( rKEvt );
|
|
}
|
|
else
|
|
{
|
|
// do nothing to avoid key presses going into the document
|
|
// while the toolbox has the focus
|
|
// just forward function and special keys and combinations with Alt-key
|
|
if( aKeyGroup == KEYGROUP_FKEYS || aKeyGroup == KEYGROUP_MISC || aKeyCode.IsMod2() )
|
|
bForwardKey = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( xWindow->IsDisposed() )
|
|
return;
|
|
|
|
// #107251# move focus away if this toolbox was disabled during keyinput
|
|
if (HasFocus() && mpData->mbKeyInputDisabled && bParentIsContainer)
|
|
{
|
|
vcl::Window *pFocusControl = pParent->ImplGetDlgWindow( 0, GetDlgWindowType::First );
|
|
if ( pFocusControl && pFocusControl != this )
|
|
pFocusControl->ImplControlFocus( GetFocusFlags::Init );
|
|
}
|
|
|
|
// #107712#, leave toolbox
|
|
if( bGrabFocusToDocument )
|
|
{
|
|
GrabFocusToDocument();
|
|
return;
|
|
}
|
|
|
|
if( bForwardKey )
|
|
DockingWindow::KeyInput( rKEvt );
|
|
}
|
|
|
|
// returns the current toolbox line of the item
|
|
ToolBox::ImplToolItems::size_type ToolBox::ImplGetItemLine( ImplToolItem const * pCurrentItem )
|
|
{
|
|
ImplToolItems::size_type nLine = 1;
|
|
for (auto const& item : mpData->m_aItems)
|
|
{
|
|
if ( item.mbBreak )
|
|
++nLine;
|
|
if( &item == pCurrentItem)
|
|
break;
|
|
}
|
|
return nLine;
|
|
}
|
|
|
|
// returns the first displayable item in the given line
|
|
ImplToolItem* ToolBox::ImplGetFirstValidItem( ImplToolItems::size_type nLine )
|
|
{
|
|
if( !nLine || nLine > mnCurLines )
|
|
return nullptr;
|
|
|
|
nLine--;
|
|
|
|
ImplToolItems::iterator it = mpData->m_aItems.begin();
|
|
while( it != mpData->m_aItems.end() )
|
|
{
|
|
// find correct line
|
|
if ( it->mbBreak )
|
|
nLine--;
|
|
if( !nLine )
|
|
{
|
|
// find first useful item
|
|
while( it != mpData->m_aItems.end() && ((it->meType != ToolBoxItemType::BUTTON) ||
|
|
/*!it->mbEnabled ||*/ !it->mbVisible || ImplIsFixedControl( &(*it) )) )
|
|
{
|
|
++it;
|
|
if( it == mpData->m_aItems.end() || it->mbBreak )
|
|
return nullptr; // no valid items in this line
|
|
}
|
|
return &(*it);
|
|
}
|
|
++it;
|
|
}
|
|
|
|
return (it == mpData->m_aItems.end()) ? nullptr : &(*it);
|
|
}
|
|
|
|
ToolBox::ImplToolItems::size_type ToolBox::ImplFindItemPos( const ImplToolItem* pItem, const ImplToolItems& rList )
|
|
{
|
|
if( pItem )
|
|
{
|
|
for( ImplToolItems::size_type nPos = 0; nPos < rList.size(); ++nPos )
|
|
if( &rList[ nPos ] == pItem )
|
|
return nPos;
|
|
}
|
|
return ITEM_NOTFOUND;
|
|
}
|
|
|
|
void ToolBox::ChangeHighlight( ImplToolItems::size_type nPos )
|
|
{
|
|
if ( nPos < GetItemCount() ) {
|
|
ImplGrabFocus( GetFocusFlags::NONE );
|
|
ImplChangeHighlight ( ImplGetItem ( GetItemId ( nPos ) ) );
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplChangeHighlight( ImplToolItem const * pItem, bool bNoGrabFocus )
|
|
{
|
|
// avoid recursion due to focus change
|
|
if( mbChangingHighlight )
|
|
return;
|
|
|
|
mbChangingHighlight = true;
|
|
|
|
ImplToolItem* pOldItem = nullptr;
|
|
|
|
if ( mnHighItemId )
|
|
{
|
|
ImplHideFocus();
|
|
ImplToolItems::size_type nPos = GetItemPos( mnHighItemId );
|
|
pOldItem = ImplGetItem( mnHighItemId );
|
|
// #i89962# ImplDrawItem can cause Invalidate/Update
|
|
// which will in turn ImplShowFocus again
|
|
// set mnHighItemId to 0 already to prevent this hen/egg problem
|
|
mnHighItemId = 0;
|
|
InvalidateItem(nPos);
|
|
CallEventListeners( VclEventId::ToolboxHighlightOff, reinterpret_cast< void* >( nPos ) );
|
|
}
|
|
|
|
if( !bNoGrabFocus && pItem != pOldItem && pOldItem && pOldItem->mpWindow )
|
|
{
|
|
// move focus into toolbox
|
|
GrabFocus();
|
|
}
|
|
|
|
if( pItem )
|
|
{
|
|
ImplToolItems::size_type aPos = ToolBox::ImplFindItemPos( pItem, mpData->m_aItems );
|
|
if( aPos != ITEM_NOTFOUND)
|
|
{
|
|
// check for line breaks
|
|
ImplToolItems::size_type nLine = ImplGetItemLine( pItem );
|
|
|
|
if( nLine >= mnCurLine + mnVisLines )
|
|
{
|
|
mnCurLine = nLine - mnVisLines + 1;
|
|
mbFormat = true;
|
|
}
|
|
else if ( nLine < mnCurLine )
|
|
{
|
|
mnCurLine = nLine;
|
|
mbFormat = true;
|
|
}
|
|
|
|
if( mbFormat )
|
|
{
|
|
ImplFormat();
|
|
}
|
|
|
|
mnHighItemId = pItem->mnId;
|
|
InvalidateItem(aPos);
|
|
|
|
ImplShowFocus();
|
|
|
|
if( pItem->mpWindow )
|
|
pItem->mpWindow->GrabFocus();
|
|
if( pItem != pOldItem )
|
|
CallEventListeners( VclEventId::ToolboxHighlight );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImplHideFocus();
|
|
mnHighItemId = 0;
|
|
mnCurPos = ITEM_NOTFOUND;
|
|
}
|
|
|
|
mbChangingHighlight = false;
|
|
}
|
|
|
|
// check for keyboard accessible items
|
|
static bool ImplIsValidItem( const ImplToolItem* pItem, bool bNotClipped )
|
|
{
|
|
bool bValid = (pItem && pItem->meType == ToolBoxItemType::BUTTON && pItem->mbVisible && !ImplIsFixedControl( pItem )
|
|
&& pItem->mbEnabled);
|
|
if( bValid && bNotClipped && pItem->IsClipped() )
|
|
bValid = false;
|
|
return bValid;
|
|
}
|
|
|
|
bool ToolBox::ImplChangeHighlightUpDn( bool bUp, bool bNoCycle )
|
|
{
|
|
ImplToolItem* pToolItem = ImplGetItem( mnHighItemId );
|
|
|
|
if( !pToolItem || !mnHighItemId )
|
|
{
|
|
// menubutton highlighted ?
|
|
if( mpData->mbMenubuttonSelected )
|
|
{
|
|
mpData->mbMenubuttonSelected = false;
|
|
if( bUp )
|
|
{
|
|
// select last valid non-clipped item
|
|
ImplToolItem* pItem = nullptr;
|
|
auto it = std::find_if(mpData->m_aItems.rbegin(), mpData->m_aItems.rend(),
|
|
[](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, true ); });
|
|
if( it != mpData->m_aItems.rend() )
|
|
pItem = &(*it);
|
|
|
|
InvalidateMenuButton();
|
|
ImplChangeHighlight( pItem );
|
|
}
|
|
else
|
|
{
|
|
// select first valid non-clipped item
|
|
ImplToolItems::iterator it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
|
|
[](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, true ); });
|
|
if( it != mpData->m_aItems.end() )
|
|
{
|
|
InvalidateMenuButton();
|
|
ImplChangeHighlight( &(*it) );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if( bUp )
|
|
{
|
|
// Select first valid item
|
|
ImplToolItems::iterator it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
|
|
[](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, false ); });
|
|
|
|
// select the menu button if a clipped item would be selected
|
|
if( (it != mpData->m_aItems.end() && &(*it) == ImplGetFirstClippedItem()) && IsMenuEnabled() )
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
mpData->mbMenubuttonSelected = true;
|
|
InvalidateMenuButton();
|
|
}
|
|
else
|
|
ImplChangeHighlight( (it != mpData->m_aItems.end()) ? &(*it) : nullptr );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Select last valid item
|
|
|
|
// docked toolbars have the menubutton as last item - if this button is enabled
|
|
if( ImplHasClippedItems() && IsMenuEnabled() && !ImplIsFloatingMode() )
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
mpData->mbMenubuttonSelected = true;
|
|
InvalidateMenuButton();
|
|
}
|
|
else
|
|
{
|
|
ImplToolItem* pItem = nullptr;
|
|
auto it = std::find_if(mpData->m_aItems.rbegin(), mpData->m_aItems.rend(),
|
|
[](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, false ); });
|
|
if( it != mpData->m_aItems.rend() )
|
|
pItem = &(*it);
|
|
|
|
ImplChangeHighlight( pItem );
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
assert(pToolItem);
|
|
|
|
ImplToolItems::size_type pos = ToolBox::ImplFindItemPos( pToolItem, mpData->m_aItems );
|
|
ImplToolItems::size_type nCount = mpData->m_aItems.size();
|
|
|
|
ImplToolItems::size_type i=0;
|
|
do
|
|
{
|
|
if( bUp )
|
|
{
|
|
if( !pos-- )
|
|
{
|
|
if( bNoCycle )
|
|
return false;
|
|
|
|
// highlight the menu button if it is the last item
|
|
if( ImplHasClippedItems() && IsMenuEnabled() && !ImplIsFloatingMode() )
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
mpData->mbMenubuttonSelected = true;
|
|
InvalidateMenuButton();
|
|
return true;
|
|
}
|
|
else
|
|
pos = nCount-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ++pos >= nCount )
|
|
{
|
|
if( bNoCycle )
|
|
return false;
|
|
|
|
// highlight the menu button if it is the last item
|
|
if( ImplHasClippedItems() && IsMenuEnabled() && !ImplIsFloatingMode() )
|
|
{
|
|
ImplChangeHighlight( nullptr );
|
|
mpData->mbMenubuttonSelected = true;
|
|
InvalidateMenuButton();
|
|
return true;
|
|
}
|
|
else
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
pToolItem = &mpData->m_aItems[pos];
|
|
|
|
if ( ImplIsValidItem( pToolItem, false ) )
|
|
break;
|
|
|
|
} while( ++i < nCount);
|
|
|
|
if( pToolItem->IsClipped() && IsMenuEnabled() )
|
|
{
|
|
// select the menu button if a clipped item would be selected
|
|
ImplChangeHighlight( nullptr );
|
|
mpData->mbMenubuttonSelected = true;
|
|
InvalidateMenuButton();
|
|
}
|
|
else if( i != nCount )
|
|
ImplChangeHighlight( pToolItem );
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ToolBox::ImplShowFocus()
|
|
{
|
|
if( mnHighItemId && HasFocus() )
|
|
{
|
|
ImplToolItem* pItem = ImplGetItem( mnHighItemId );
|
|
if (pItem && pItem->mpWindow && !pItem->mpWindow->IsDisposed())
|
|
{
|
|
vcl::Window *pWin = pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow ? pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow.get() : pItem->mpWindow.get();
|
|
pWin->ImplGetWindowImpl()->mbDrawSelectionBackground = true;
|
|
pWin->Invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplHideFocus()
|
|
{
|
|
if( mnHighItemId )
|
|
{
|
|
mpData->mbMenubuttonWasLastSelected = false;
|
|
ImplToolItem* pItem = ImplGetItem( mnHighItemId );
|
|
if( pItem && pItem->mpWindow )
|
|
{
|
|
vcl::Window *pWin = pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow ? pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow.get() : pItem->mpWindow.get();
|
|
pWin->ImplGetWindowImpl()->mbDrawSelectionBackground = false;
|
|
pWin->Invalidate();
|
|
}
|
|
}
|
|
|
|
if ( mpData && mpData->mbMenubuttonSelected )
|
|
{
|
|
mpData->mbMenubuttonWasLastSelected = true;
|
|
// remove highlight from menubutton
|
|
mpData->mbMenubuttonSelected = false;
|
|
InvalidateMenuButton();
|
|
}
|
|
}
|
|
|
|
void ToolBox::ImplDisableFlatButtons()
|
|
{
|
|
#ifdef _WIN32 // Check in the Windows registry if an AT tool wants no flat toolboxes
|
|
static bool bInit = false, bValue = false;
|
|
if( ! bInit )
|
|
{
|
|
bInit = true;
|
|
HKEY hkey;
|
|
|
|
if( ERROR_SUCCESS == RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\LibreOffice\\Accessibility\\AtToolSupport", &hkey) )
|
|
{
|
|
DWORD dwType = 0;
|
|
wchar_t Data[6]; // possible values: "true", "false", "1", "0", DWORD
|
|
DWORD cbData = sizeof(Data);
|
|
|
|
if( ERROR_SUCCESS == RegQueryValueExW(hkey, L"DisableFlatToolboxButtons",
|
|
nullptr, &dwType, reinterpret_cast<LPBYTE>(Data), &cbData) )
|
|
{
|
|
switch (dwType)
|
|
{
|
|
case REG_SZ:
|
|
bValue = ((0 == wcsicmp(Data, L"1")) || (0 == wcsicmp(Data, L"true")));
|
|
break;
|
|
case REG_DWORD:
|
|
bValue = static_cast<bool>(reinterpret_cast<DWORD *>(Data)[0]);
|
|
break;
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
if( bValue )
|
|
mnOutStyle &= ~TOOLBOX_STYLE_FLAT;
|
|
#else
|
|
(void) this; // loplugin:staticmethods
|
|
#endif
|
|
}
|
|
|
|
void ToolBox::SetToolbarLayoutMode( ToolBoxLayoutMode eLayout )
|
|
{
|
|
if ( meLayoutMode != eLayout )
|
|
meLayoutMode = eLayout;
|
|
}
|
|
|
|
void ToolBox::SetToolBoxTextPosition( ToolBoxTextPosition ePosition )
|
|
{
|
|
meTextPosition = ePosition;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|