Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/window/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 157 kB image not shown  

Quelle  toolbox.cxx   Sprache: 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/event.hxx>
#include <vcl/decoview.hxx>
#include <vcl/toolkit/floatwin.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 <vcl/ptrstyle.hxx>
#include <bitmaps.hlst>
#include <toolbarvalue.hxx>

#include <tools/poly.hxx>
#include <sal/log.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>

#include <accel.hxx>
#include <svdata.hxx>
#include <window.h>
#include <toolbox.h>
#include <spin.hxx>
#if defined(_WIN32)
#include <svsys.h>
#endif

#include <cstdlib>
#include <map>
#include <string_view>
#include <vector>
#include <math.h>

#include "impldockingwrapper.hxx"

#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::Window& rWindow, bool bHorz )
{
    return ImplGetDragWidth(*rWindow.GetOutDev(), bHorz);
}
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, tools::Long& rLeft, tools::Long&&nbsp;rTop,
                              tools::Long& rRight, tools::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(Degree10(mbHorz ? 0 : 900));
    g.SetStyle(css::awt::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())
    {
        tools::Long nLineSize;
        if (mbHorz)
        {
            nLineSize = mnMaxItemHeight;
            if (mnWinHeight > mnMaxItemHeight)
                nLineSize = mnWinHeight;

            aLineSz.setHeight( nLineSize );
        }
        else
        {
            nLineSize = mnMaxItemWidth;
            aLineSz.setWidth( nLineSize );
        }
    }

    tools::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)
    {
        tools::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
    {
        tools::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) const
{
    // 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(GetOutDev()->GetActiveClipRegion());

    rRenderContext.Push(vcl::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();
            if (!IsBackground())
            {
                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(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
            rRenderContext.SetLineColor();
            if (bHasOpenPopup)
                // choose the same color as the popup will use
                rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetFaceColor());
            else
                rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor());

            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();
    tools::Long nDX = mnDX;
    tools::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 )
{
    sal_Int32            nMax;
    tools::Long            nLeft = 0;
    tools::Long            nTop = 0;
    tools::Long            nRight = 0;
    tools::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
    tools::Long            nCalcSize = 0;

    for (auto const& item : mpData->m_aItems)
    {
        if ( item.mbVisible )
        {
            if ( item.mpWindow )
            {
                tools::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
    tools::Long upperBoundWidth = nCalcSize * mpData->m_aItems.size();

    ImplToolItems::size_type nLines;
    ImplToolItems::size_type nCalcLines;
    ImplToolItems::size_type nTempLines;
    sal_Int32    nMaxLineWidth;
    nCalcLines = ImplCalcBreaks( nCalcSize, &nMaxLineWidth, true );

    maFloatSizes.reserve( nCalcLines );

    nTempLines = nLines = nCalcLines;
    while ( nLines )
    {
        tools::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->ShowMenuTitleButton( bool( GetMenuType() & ToolBoxMenuType::Customize) );
    }
    else
    {
        // TODO: change SetMinOutputSizePixel to be not inline
        SetMinOutputSizePixel( aMinSize );
        SetMaxOutputSizePixel( aMaxSize );
    }
}

ToolBox::ImplToolItems::size_type ToolBox::ImplCalcLines( tools::Long nToolSize ) const
{
    tools::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
    tools::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;
    tools::Long    nOneLineSize;
    tools::Long    nCurSize;
    tools::Long    nMaxSize;
    tools::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;
    mnHighItemId          = ToolBoxItemId(0);
    mnCurItemId           = ToolBoxItemId(0);
    mnDownItemId          = ToolBoxItemId(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;
    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     = ToolBoxItemId(0);
    mnActivateCount       = 0;

    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 ) );
}

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(truetruetrue);
}

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))
        {
            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(*GetOutDev(), rStyleSettings.GetToolFont());
    if (bForeground || bFont)
        ApplyForegroundSettings(*GetOutDev(), rStyleSettings);
    if (bBackground)
    {
        ApplyBackgroundSettings(*GetOutDev(), 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, "vcl::ToolBox maLayoutIdle" )
{
    ImplInitToolBoxData();
    ImplInit( pParent, nStyle );
}

ToolBox::ToolBox(vcl::Window* pParent, const OUString& rID,
    const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame)
    : DockingWindow(WindowType::TOOLBOX, "vcl::ToolBox maLayoutIdle")
{
    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;

    mpFloatWin.reset();

    mpIdle.reset();

    DockingWindow::dispose();
}

ImplToolItem* ToolBox::ImplGetItem( ToolBoxItemId nItemId ) const
{
    if (!mpData)
        return nullptr;

    for (auto & item : mpData->m_aItems)
    {
        if ( item.mnId == nItemId )
            return &item;
    }

    return nullptr;
}

static void ImplAddButtonBorder( tools::Long &rWidth, tools::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;

    OutputDevice *pDefault = Application::GetDefaultDevice();
    float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;

    tools::Long            nDefWidth;
    tools::Long            nDefHeight;
    tools::Long            nMaxWidth = 0;
    tools::Long            nMaxHeight = 0;
    tools::Long            nMinWidth   = 6;
    tools::Long            nMinHeight  = 6;
    tools::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( GetOutDev()->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( GetOutDev()->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 ? GetOutDev()->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 )
                {
                    tools::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 )
                {
                    tools::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
                tools::Long w = item.maItemSize.Width();
                tools::Long h = item.maItemSize.Height();
                ImplAddButtonBorder( w, h, mpData->mbNativeButtons );
                item.maItemSize.setWidth(w);
                item.maItemSize.setHeight(h);

                if( item.meType == ToolBoxItemType::BUTTON )
                {
                    tools::Long nMinW = std::max(nMinWidth, item.maMinimalItemSize.Width());
                    tools::Long nMinH = std::max(nMinHeight, item.maMinimalItemSize.Height());

                    tools::Long nGrowContentWidth = 0;
                    tools::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

        tools::Long nFixedWidth = nDefWidth+nDropDownArrowWidth;
        tools::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( tools::Long nWidth, sal_Int32* pMaxLineWidth, bool bCalcHorz ) const
{
    sal_uLong           nLineStart = 0;
    sal_uLong           nGroupStart = 0;
    tools::Long            nLineWidth = 0;
    tools::Long            nCurWidth;
    tools::Long            nLastGroupLineWidth = 0;
    tools::Long            nMaxLineWidth = 0;
    ImplToolItems::size_type nLines = 1;
    bool            bWindow;
    bool            bBreak = false;
    tools::Long            nWidthTotal = nWidth;
    tools::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 )
                {
                    tools::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
            tools::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

    tools::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 );
    sal_Int32 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
    tools::Long dx1 = std::abs( mnDX - aSize1.Width() );
    tools::Long dy1 = std::abs( mnDY - aSize1.Height() );

    tools::Long dx2 = std::abs( mnDX - aSize2.Width() );
    tools::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;
    tools::Long            nLineSize;
    tools::Long            nLeft;
    tools::Long            nTop;
    tools::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 )
    {
        tools::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 )
        {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=94 G=92

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.