diff options
Diffstat (limited to 'vcl/source/treelist/headbar.cxx')
-rw-r--r-- | vcl/source/treelist/headbar.cxx | 1302 |
1 files changed, 1302 insertions, 0 deletions
diff --git a/vcl/source/treelist/headbar.cxx b/vcl/source/treelist/headbar.cxx new file mode 100644 index 000000000..3bd6bbaa3 --- /dev/null +++ b/vcl/source/treelist/headbar.cxx @@ -0,0 +1,1302 @@ +/* -*- 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/headbar.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/debug.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <vcl/image.hxx> +#include <vcl/salnativewidgets.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/ptrstyle.hxx> + +#include <com/sun/star/accessibility/XAccessible.hpp> + +class ImplHeadItem +{ +public: + sal_uInt16 mnId; + HeaderBarItemBits mnBits; + tools::Long mnSize; + OString maHelpId; + Image maImage; + OUString maOutText; + OUString maText; + OUString maHelpText; +}; + +#define HEAD_ARROWSIZE1 4 +#define HEAD_ARROWSIZE2 7 + +#define HEADERBAR_TEXTOFF 2 +#define HEADERBAR_ARROWOFF 5 +#define HEADERBAR_SPLITOFF 3 + +#define HEADERBAR_DRAGOUTOFF 15 + +#define HEAD_HITTEST_ITEM (sal_uInt16(0x0001)) +#define HEAD_HITTEST_DIVIDER (sal_uInt16(0x0002)) + +void HeaderBar::ImplInit( WinBits nWinStyle ) +{ + mnBorderOff1 = 0; + mnBorderOff2 = 0; + mnOffset = 0; + mnDX = 0; + mnDY = 0; + mnDragSize = 0; + mnStartPos = 0; + mnDragPos = 0; + mnMouseOff = 0; + mnCurItemId = 0; + mnItemDragPos = HEADERBAR_ITEM_NOTFOUND; + mbDrag = false; + mbItemDrag = false; + mbOutDrag = false; + mbItemMode = false; + + // evaluate StyleBits + if ( nWinStyle & WB_DRAG ) + mbDragable = true; + else + mbDragable = false; + if ( nWinStyle & WB_BUTTONSTYLE ) + mbButtonStyle = true; + else + mbButtonStyle = false; + if ( nWinStyle & WB_BORDER ) + { + mnBorderOff1 = 1; + mnBorderOff2 = 1; + } + else + { + if ( nWinStyle & WB_BOTTOMBORDER ) + mnBorderOff2 = 1; + } + + ImplInitSettings( true, true, true ); +} + +HeaderBar::HeaderBar(vcl::Window* pParent, WinBits nWinStyle) + : Window(pParent, nWinStyle & WB_3DLOOK) +{ + SetType(WindowType::HEADERBAR); + ImplInit(nWinStyle); + SetSizePixel( CalcWindowSizePixel() ); +} + +Size HeaderBar::GetOptimalSize() const +{ + return CalcWindowSizePixel(); +} + +HeaderBar::~HeaderBar() = default; + +void HeaderBar::ApplySettings(vcl::RenderContext& rRenderContext) +{ + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + ApplyControlFont(rRenderContext, rStyleSettings.GetToolFont()); + + ApplyControlForeground(rRenderContext, rStyleSettings.GetButtonTextColor()); + SetTextFillColor(); + + ApplyControlBackground(rRenderContext, rStyleSettings.GetFaceColor()); +} + +void HeaderBar::ImplInitSettings(bool bFont, bool bForeground, bool bBackground) +{ + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + if (bFont) + ApplyControlFont(*GetOutDev(), rStyleSettings.GetToolFont()); + + if (bForeground || bFont) + { + ApplyControlForeground(*GetOutDev(), rStyleSettings.GetButtonTextColor()); + SetTextFillColor(); + } + + if (bBackground) + ApplyControlBackground(*GetOutDev(), rStyleSettings.GetFaceColor()); +} + +tools::Long HeaderBar::ImplGetItemPos( sal_uInt16 nPos ) const +{ + tools::Long nX = -mnOffset; + for ( size_t i = 0; i < nPos; i++ ) + nX += mvItemList[ i ]->mnSize; + return nX; +} + +tools::Rectangle HeaderBar::ImplGetItemRect( sal_uInt16 nPos ) const +{ + tools::Rectangle aRect( ImplGetItemPos( nPos ), 0, 0, mnDY-1 ); + aRect.SetRight( aRect.Left() + mvItemList[ nPos ]->mnSize - 1 ); + // check for overflow on various systems + if ( aRect.Right() > 16000 ) + aRect.SetRight( 16000 ); + return aRect; +} + +sal_uInt16 HeaderBar::ImplDoHitTest( const Point& rPos, + tools::Long& nMouseOff, sal_uInt16& nPos ) const +{ + size_t nCount = static_cast<sal_uInt16>(mvItemList.size()); + bool bLastFixed = true; + tools::Long nX = -mnOffset; + + for ( size_t i = 0; i < nCount; i++ ) + { + auto& pItem = mvItemList[ i ]; + + if ( rPos.X() < (nX+pItem->mnSize) ) + { + sal_uInt16 nMode; + + if ( !bLastFixed && (rPos.X() < (nX+HEADERBAR_SPLITOFF)) ) + { + nMode = HEAD_HITTEST_DIVIDER; + nPos = i-1; + nMouseOff = rPos.X()-nX+1; + } + else + { + nPos = i; + + if ( rPos.X() >= (nX+pItem->mnSize-HEADERBAR_SPLITOFF) ) + { + nMode = HEAD_HITTEST_DIVIDER; + nMouseOff = rPos.X()-(nX+pItem->mnSize); + } + else + { + nMode = HEAD_HITTEST_ITEM; + nMouseOff = rPos.X()-nX; + } + } + + return nMode; + } + + bLastFixed = false; + + nX += pItem->mnSize; + } + + if ( !bLastFixed ) + { + auto& pItem = mvItemList[ nCount-1 ]; + if ( (pItem->mnSize < 4) && (rPos.X() < (nX+HEADERBAR_SPLITOFF)) ) + { + nPos = nCount-1; + nMouseOff = rPos.X()-nX+1; + return HEAD_HITTEST_DIVIDER; + } + } + + return 0; +} + +void HeaderBar::ImplInvertDrag( sal_uInt16 nStartPos, sal_uInt16 nEndPos ) +{ + tools::Rectangle aRect1 = ImplGetItemRect( nStartPos ); + tools::Rectangle aRect2 = ImplGetItemRect( nEndPos ); + Point aStartPos = aRect1.Center(); + Point aEndPos = aStartPos; + tools::Rectangle aStartRect( aStartPos.X()-2, aStartPos.Y()-2, + aStartPos.X()+2, aStartPos.Y()+2 ); + + if ( nEndPos > nStartPos ) + { + aStartPos.AdjustX(3 ); + aEndPos.setX( aRect2.Right()-6 ); + } + else + { + aStartPos.AdjustX( -3 ); + aEndPos.setX( aRect2.Left()+6 ); + } + + GetOutDev()->SetRasterOp( RasterOp::Invert ); + GetOutDev()->DrawRect( aStartRect ); + GetOutDev()->DrawLine( aStartPos, aEndPos ); + if ( nEndPos > nStartPos ) + { + GetOutDev()->DrawLine( Point( aEndPos.X()+1, aEndPos.Y()-3 ), + Point( aEndPos.X()+1, aEndPos.Y()+3 ) ); + GetOutDev()->DrawLine( Point( aEndPos.X()+2, aEndPos.Y()-2 ), + Point( aEndPos.X()+2, aEndPos.Y()+2 ) ); + GetOutDev()->DrawLine( Point( aEndPos.X()+3, aEndPos.Y()-1 ), + Point( aEndPos.X()+3, aEndPos.Y()+1 ) ); + GetOutDev()->DrawPixel( Point( aEndPos.X()+4, aEndPos.Y() ) ); + } + else + { + GetOutDev()->DrawLine( Point( aEndPos.X()-1, aEndPos.Y()-3 ), + Point( aEndPos.X()-1, aEndPos.Y()+3 ) ); + GetOutDev()->DrawLine( Point( aEndPos.X()-2, aEndPos.Y()-2 ), + Point( aEndPos.X()-2, aEndPos.Y()+2 ) ); + GetOutDev()->DrawLine( Point( aEndPos.X()-3, aEndPos.Y()-1 ), + Point( aEndPos.X()-3, aEndPos.Y()+1 ) ); + GetOutDev()->DrawPixel( Point( aEndPos.X()-4, aEndPos.Y() ) ); + } + GetOutDev()->SetRasterOp( RasterOp::OverPaint ); +} + +void HeaderBar::ImplDrawItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos, bool bHigh, + const tools::Rectangle& rItemRect, const tools::Rectangle* pRect ) +{ + ImplControlValue aControlValue(0); + tools::Rectangle aCtrlRegion; + ControlState nState(ControlState::NONE); + + tools::Rectangle aRect = rItemRect; + + // do not display if there is no space + if (aRect.GetWidth() <= 1) + return; + + // check of rectangle is visible + if (pRect) + { + if (aRect.Right() < pRect->Left()) + return; + else if (aRect.Left() > pRect->Right()) + return; + } + else + { + if (aRect.Right() < 0) + return; + else if (aRect.Left() > mnDX) + return; + } + + auto& pItem = mvItemList[nPos]; + HeaderBarItemBits nBits = pItem->mnBits; + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + + if (rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::Entire)) + { + aCtrlRegion = aRect; + rRenderContext.DrawNativeControl(ControlType::WindowBackground, ControlPart::Entire, + aCtrlRegion, nState, aControlValue, OUString()); + + } + else + { + // do not draw border + aRect.AdjustTop(mnBorderOff1 ); + aRect.AdjustBottom( -mnBorderOff2 ); + + // delete background + if ( !pRect ) + { + rRenderContext.DrawWallpaper(aRect, rRenderContext.GetBackground()); + } + } + + Color aSelectionTextColor(COL_TRANSPARENT); + + if (rRenderContext.IsNativeControlSupported(ControlType::ListHeader, ControlPart::Button)) + { + aCtrlRegion = aRect; + aControlValue.setTristateVal(ButtonValue::On); + nState |= ControlState::ENABLED; + if (bHigh) + nState |= ControlState::PRESSED; + rRenderContext.DrawNativeControl(ControlType::ListHeader, ControlPart::Button, + aCtrlRegion, nState, aControlValue, OUString()); + } + else + { + // draw separation line + rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor()); + rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top()), Point(aRect.Right(), aRect.Bottom())); + + // draw ButtonStyle + // avoid 3D borders + if (bHigh) + vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, aRect, 1, true, false, false, &aSelectionTextColor); + else if (!mbButtonStyle || (nBits & HeaderBarItemBits::FLAT)) + vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, aRect, 0, true, false, false, &aSelectionTextColor); + } + + // do not draw if there is no space + if (aRect.GetWidth() < 1) + return; + + // calculate size and position and draw content + pItem->maOutText = pItem->maText; + Size aImageSize = pItem->maImage.GetSizePixel(); + Size aTxtSize(rRenderContext.GetTextWidth(pItem->maOutText), 0); + if (!pItem->maOutText.isEmpty()) + aTxtSize.setHeight( rRenderContext.GetTextHeight() ); + tools::Long nArrowWidth = 0; + if (nBits & (HeaderBarItemBits::UPARROW | HeaderBarItemBits::DOWNARROW)) + nArrowWidth = HEAD_ARROWSIZE2 + HEADERBAR_ARROWOFF; + + // do not draw if there is not enough space for the image + tools::Long nTestHeight = aImageSize.Height(); + if (!(nBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE))) + nTestHeight += aTxtSize.Height(); + if ((aImageSize.Width() > aRect.GetWidth()) || (nTestHeight > aRect.GetHeight())) + { + aImageSize.setWidth( 0 ); + aImageSize.setHeight( 0 ); + } + + // cut text to correct length + bool bLeftText = false; + tools::Long nMaxTxtWidth = aRect.GetWidth() - (HEADERBAR_TEXTOFF * 2) - nArrowWidth; + if (nBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE)) + nMaxTxtWidth -= aImageSize.Width(); + tools::Long nTxtWidth = aTxtSize.Width(); + if (nTxtWidth > nMaxTxtWidth) + { + bLeftText = true; + OUStringBuffer aBuf(pItem->maOutText); + aBuf.append("..."); + do + { + aBuf.remove(aBuf.getLength() - 3 - 1, 1); + nTxtWidth = rRenderContext.GetTextWidth(aBuf.toString()); + } + while ((nTxtWidth > nMaxTxtWidth) && (aBuf.getLength() > 3)); + pItem->maOutText = aBuf.makeStringAndClear(); + if (pItem->maOutText.getLength() == 3) + { + nTxtWidth = 0; + pItem->maOutText.clear(); + } + } + + // calculate text/imageposition + tools::Long nTxtPos; + if (!bLeftText && (nBits & HeaderBarItemBits::RIGHT)) + { + nTxtPos = aRect.Right() - nTxtWidth - HEADERBAR_TEXTOFF; + if (nBits & HeaderBarItemBits::RIGHTIMAGE) + nTxtPos -= aImageSize.Width(); + } + else if (!bLeftText && (nBits & HeaderBarItemBits::CENTER)) + { + tools::Long nTempWidth = nTxtWidth; + if (nBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE)) + nTempWidth += aImageSize.Width(); + nTxtPos = aRect.Left() + (aRect.GetWidth() - nTempWidth) / 2; + if (nBits & HeaderBarItemBits::LEFTIMAGE) + nTxtPos += aImageSize.Width(); + if (nArrowWidth) + { + if (nTxtPos + nTxtWidth + nArrowWidth >= aRect.Right()) + { + nTxtPos = aRect.Left() + HEADERBAR_TEXTOFF; + if (nBits & HeaderBarItemBits::LEFTIMAGE) + nTxtPos += aImageSize.Width(); + } + } + } + else + { + nTxtPos = aRect.Left() + HEADERBAR_TEXTOFF; + if (nBits & HeaderBarItemBits::LEFTIMAGE) + nTxtPos += aImageSize.Width(); + if (nBits & HeaderBarItemBits::RIGHT) + nTxtPos += nArrowWidth; + } + + // calculate text/imageposition + tools::Long nTxtPosY = 0; + if (!pItem->maOutText.isEmpty() || (nArrowWidth && aTxtSize.Height())) + { + tools::Long nTempHeight = aTxtSize.Height(); + nTempHeight += aImageSize.Height(); + nTxtPosY = aRect.Top()+((aRect.GetHeight()-nTempHeight)/2); + if (!(nBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE))) + nTxtPosY += aImageSize.Height(); + } + + // display text + if (!pItem->maOutText.isEmpty()) + { + if (aSelectionTextColor != COL_TRANSPARENT) + { + rRenderContext.Push(vcl::PushFlags::TEXTCOLOR); + rRenderContext.SetTextColor(aSelectionTextColor); + } + if (IsEnabled()) + rRenderContext.DrawText(Point(nTxtPos, nTxtPosY), pItem->maOutText); + else + rRenderContext.DrawCtrlText(Point(nTxtPos, nTxtPosY), pItem->maOutText, 0, pItem->maOutText.getLength(), DrawTextFlags::Disable); + if (aSelectionTextColor != COL_TRANSPARENT) + rRenderContext.Pop(); + } + + // calculate the position and draw image if it is available + tools::Long nImagePosY = 0; + if (aImageSize.Width() && aImageSize.Height()) + { + tools::Long nImagePos = nTxtPos; + if (nBits & HeaderBarItemBits::LEFTIMAGE) + { + nImagePos -= aImageSize.Width(); + if (nBits & HeaderBarItemBits::RIGHT) + nImagePos -= nArrowWidth; + } + else if (nBits & HeaderBarItemBits::RIGHTIMAGE) + { + nImagePos += nTxtWidth; + if (!(nBits & HeaderBarItemBits::RIGHT)) + nImagePos += nArrowWidth; + } + else + { + if (nBits & HeaderBarItemBits::RIGHT ) + nImagePos = aRect.Right()-aImageSize.Width(); + else if (nBits & HeaderBarItemBits::CENTER) + nImagePos = aRect.Left() + (aRect.GetWidth() - aImageSize.Width()) / 2; + else + nImagePos = aRect.Left() + HEADERBAR_TEXTOFF; + } + + tools::Long nTempHeight = aImageSize.Height(); + if (!(nBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE))) + nTempHeight += aTxtSize.Height(); + nImagePosY = aRect.Top() + ((aRect.GetHeight() - nTempHeight) / 2); + + if (nImagePos + aImageSize.Width() <= aRect.Right()) + { + DrawImageFlags nStyle = DrawImageFlags::NONE; + if (!IsEnabled()) + nStyle |= DrawImageFlags::Disable; + rRenderContext.DrawImage(Point(nImagePos, nImagePosY), pItem->maImage, nStyle); + } + } + + if (!(nBits & (HeaderBarItemBits::UPARROW | HeaderBarItemBits::DOWNARROW))) + return; + + tools::Long nArrowX = nTxtPos; + if (nBits & HeaderBarItemBits::RIGHT) + nArrowX -= nArrowWidth; + else + nArrowX += nTxtWidth + HEADERBAR_ARROWOFF; + if (!(nBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE)) && pItem->maText.isEmpty()) + { + if (nBits & HeaderBarItemBits::RIGHT) + nArrowX -= aImageSize.Width(); + else + nArrowX += aImageSize.Width(); + } + + // is there enough space to draw the item? + bool bDraw = true; + if (nArrowX < aRect.Left() + HEADERBAR_TEXTOFF) + bDraw = false; + else if (nArrowX + HEAD_ARROWSIZE2 > aRect.Right()) + bDraw = false; + + if (!bDraw) + return; + + if (rRenderContext.IsNativeControlSupported(ControlType::ListHeader, ControlPart::Arrow)) + { + aCtrlRegion = tools::Rectangle(Point(nArrowX, aRect.Top()), Size(nArrowWidth, aRect.GetHeight())); + // control value passes 1 if arrow points down, 0 otherwise + aControlValue.setNumericVal((nBits & HeaderBarItemBits::DOWNARROW) ? 1 : 0); + nState |= ControlState::ENABLED; + if (bHigh) + nState |= ControlState::PRESSED; + rRenderContext.DrawNativeControl(ControlType::ListHeader, ControlPart::Arrow, aCtrlRegion, + nState, aControlValue, OUString()); + } + else + { + tools::Long nArrowY; + if (aTxtSize.Height()) + nArrowY = nTxtPosY + (aTxtSize.Height() / 2); + else if (aImageSize.Width() && aImageSize.Height()) + nArrowY = nImagePosY + (aImageSize.Height() / 2); + else + nArrowY = aRect.Top() + ((aRect.GetHeight() - HEAD_ARROWSIZE2) / 2); + nArrowY -= HEAD_ARROWSIZE1 - 1; + if (nBits & HeaderBarItemBits::DOWNARROW) + { + rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); + rRenderContext.DrawLine(Point(nArrowX, nArrowY), + Point(nArrowX + HEAD_ARROWSIZE2, nArrowY)); + rRenderContext.DrawLine(Point(nArrowX, nArrowY), + Point(nArrowX + HEAD_ARROWSIZE1, nArrowY + HEAD_ARROWSIZE2)); + rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); + rRenderContext.DrawLine(Point(nArrowX + HEAD_ARROWSIZE1, nArrowY + HEAD_ARROWSIZE2), + Point(nArrowX + HEAD_ARROWSIZE2, nArrowY)); + } + else + { + rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); + rRenderContext.DrawLine(Point(nArrowX, nArrowY + HEAD_ARROWSIZE2), + Point(nArrowX + HEAD_ARROWSIZE1, nArrowY)); + rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); + rRenderContext.DrawLine(Point(nArrowX, nArrowY + HEAD_ARROWSIZE2), + Point(nArrowX + HEAD_ARROWSIZE2, nArrowY + HEAD_ARROWSIZE2)); + rRenderContext.DrawLine(Point(nArrowX + HEAD_ARROWSIZE2, nArrowY + HEAD_ARROWSIZE2), + Point(nArrowX + HEAD_ARROWSIZE1, nArrowY)); + } + } +} + +void HeaderBar::ImplDrawItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos, + bool bHigh, const tools::Rectangle* pRect ) +{ + tools::Rectangle aRect = ImplGetItemRect(nPos); + ImplDrawItem(rRenderContext, nPos, bHigh, aRect, pRect ); +} + +void HeaderBar::ImplUpdate(sal_uInt16 nPos, bool bEnd) +{ + if (!(IsVisible() && IsUpdateMode())) + return; + + tools::Rectangle aRect; + size_t nItemCount = mvItemList.size(); + if (nPos < nItemCount) + aRect = ImplGetItemRect(nPos); + else + { + aRect.SetBottom( mnDY - 1 ); + if (nItemCount) + aRect.SetLeft( ImplGetItemRect(nItemCount - 1).Right() ); + } + if (bEnd) + aRect.SetRight( mnDX - 1 ); + aRect.AdjustTop(mnBorderOff1 ); + aRect.AdjustBottom( -mnBorderOff2 ); + Invalidate(aRect); +} + +void HeaderBar::ImplStartDrag( const Point& rMousePos, bool bCommand ) +{ + sal_uInt16 nPos; + sal_uInt16 nHitTest = ImplDoHitTest( rMousePos, mnMouseOff, nPos ); + if ( !nHitTest ) + return; + + mbDrag = false; + auto& pItem = mvItemList[ nPos ]; + if ( nHitTest & HEAD_HITTEST_DIVIDER ) + mbDrag = true; + else + { + if ( ((pItem->mnBits & HeaderBarItemBits::CLICKABLE) && !(pItem->mnBits & HeaderBarItemBits::FLAT)) || + mbDragable ) + { + mbItemMode = true; + mbDrag = true; + if ( bCommand ) + { + if ( mbDragable ) + mbItemDrag = true; + else + { + mbItemMode = false; + mbDrag = false; + } + } + } + else + { + if ( !bCommand ) + { + mnCurItemId = pItem->mnId; + Select(); + mnCurItemId = 0; + } + } + } + + if ( mbDrag ) + { + mbOutDrag = false; + mnCurItemId = pItem->mnId; + mnItemDragPos = nPos; + StartTracking(); + mnStartPos = rMousePos.X()-mnMouseOff; + mnDragPos = mnStartPos; + maStartDragHdl.Call( this ); + if (mbItemMode) + Invalidate(); + else + { + tools::Rectangle aSizeRect( mnDragPos, 0, mnDragPos, mnDragSize+mnDY ); + ShowTracking( aSizeRect, ShowTrackFlags::Split ); + } + } + else + mnMouseOff = 0; +} + +void HeaderBar::ImplDrag( const Point& rMousePos ) +{ + sal_uInt16 nPos = GetItemPos( mnCurItemId ); + + mnDragPos = rMousePos.X()-mnMouseOff; + if ( mbItemMode ) + { + bool bNewOutDrag; + + tools::Rectangle aItemRect = ImplGetItemRect( nPos ); + bNewOutDrag = !aItemRect.Contains( rMousePos ); + + // if needed switch on ItemDrag + if ( bNewOutDrag && mbDragable && !mbItemDrag ) + { + if ( (rMousePos.Y() >= aItemRect.Top()) && (rMousePos.Y() <= aItemRect.Bottom()) ) + { + mbItemDrag = true; + Invalidate(); + } + } + + sal_uInt16 nOldItemDragPos = mnItemDragPos; + if ( mbItemDrag ) + { + bNewOutDrag = (rMousePos.Y() < -HEADERBAR_DRAGOUTOFF) || (rMousePos.Y() > mnDY+HEADERBAR_DRAGOUTOFF); + + if ( bNewOutDrag ) + mnItemDragPos = HEADERBAR_ITEM_NOTFOUND; + else + { + sal_uInt16 nTempId = GetItemId( Point( rMousePos.X(), 2 ) ); + if ( nTempId ) + mnItemDragPos = GetItemPos( nTempId ); + else + { + if ( rMousePos.X() <= 0 ) + mnItemDragPos = 0; + else + mnItemDragPos = GetItemCount()-1; + } + } + + if ( (mnItemDragPos != nOldItemDragPos) && + (nOldItemDragPos != nPos) && + (nOldItemDragPos != HEADERBAR_ITEM_NOTFOUND) ) + { + ImplInvertDrag( nPos, nOldItemDragPos ); + Invalidate(); + } + } + + if ( bNewOutDrag != mbOutDrag ) + Invalidate(); + + if ( mbItemDrag ) + { + if ( (mnItemDragPos != nOldItemDragPos) && + (mnItemDragPos != nPos) && + (mnItemDragPos != HEADERBAR_ITEM_NOTFOUND) ) + { + Invalidate(); + ImplInvertDrag( nPos, mnItemDragPos ); + } + } + + mbOutDrag = bNewOutDrag; + } + else + { + tools::Rectangle aItemRect = ImplGetItemRect( nPos ); + if ( mnDragPos < aItemRect.Left() ) + mnDragPos = aItemRect.Left(); + if ( (mnDragPos < 0) || (mnDragPos > mnDX-1) ) + HideTracking(); + else + { + tools::Rectangle aSizeRect( mnDragPos, 0, mnDragPos, mnDragSize+mnDY ); + ShowTracking( aSizeRect, ShowTrackFlags::Split ); + } + } +} + +void HeaderBar::ImplEndDrag( bool bCancel ) +{ + HideTracking(); + + if ( bCancel || mbOutDrag ) + { + if ( mbItemMode && (!mbOutDrag || mbItemDrag) ) + { + Invalidate(); + } + + mnCurItemId = 0; + } + else + { + sal_uInt16 nPos = GetItemPos( mnCurItemId ); + if ( mbItemMode ) + { + if ( mbItemDrag ) + { + SetPointer( PointerStyle::Arrow ); + if ( (mnItemDragPos != nPos) && + (mnItemDragPos != HEADERBAR_ITEM_NOTFOUND) ) + { + ImplInvertDrag( nPos, mnItemDragPos ); + MoveItem( mnCurItemId, mnItemDragPos ); + } + else + Invalidate(); + } + else + { + Select(); + ImplUpdate( nPos ); + } + } + else + { + tools::Long nDelta = mnDragPos - mnStartPos; + if ( nDelta ) + { + auto& pItem = mvItemList[ nPos ]; + pItem->mnSize += nDelta; + ImplUpdate( nPos, true ); + } + } + } + + mbDrag = false; + EndDrag(); + mnCurItemId = 0; + mnItemDragPos = HEADERBAR_ITEM_NOTFOUND; + mbOutDrag = false; + mbItemMode = false; + mbItemDrag = false; +} + +void HeaderBar::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( !rMEvt.IsLeft() ) + return; + + if ( rMEvt.GetClicks() == 2 ) + { + tools::Long nTemp; + sal_uInt16 nPos; + sal_uInt16 nHitTest = ImplDoHitTest( rMEvt.GetPosPixel(), nTemp, nPos ); + if ( nHitTest ) + { + auto& pItem = mvItemList[ nPos ]; + if ( nHitTest & HEAD_HITTEST_DIVIDER ) + mbItemMode = false; + else + mbItemMode = true; + mnCurItemId = pItem->mnId; + DoubleClick(); + mbItemMode = false; + mnCurItemId = 0; + } + } + else + ImplStartDrag( rMEvt.GetPosPixel(), false ); +} + +void HeaderBar::MouseMove( const MouseEvent& rMEvt ) +{ + tools::Long nTemp1; + sal_uInt16 nTemp2; + PointerStyle eStyle = PointerStyle::Arrow; + sal_uInt16 nHitTest = ImplDoHitTest( rMEvt.GetPosPixel(), nTemp1, nTemp2 ); + + if ( nHitTest & HEAD_HITTEST_DIVIDER ) + eStyle = PointerStyle::HSizeBar; + SetPointer( eStyle ); +} + +void HeaderBar::Tracking( const TrackingEvent& rTEvt ) +{ + Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel(); + + if ( rTEvt.IsTrackingEnded() ) + ImplEndDrag( rTEvt.IsTrackingCanceled() ); + else + ImplDrag( aMousePos ); +} + +void HeaderBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (mnBorderOff1 || mnBorderOff2) + { + rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetDarkShadowColor()); + if (mnBorderOff1) + rRenderContext.DrawLine(Point(0, 0), Point(mnDX - 1, 0)); + if (mnBorderOff2) + rRenderContext.DrawLine(Point(0, mnDY - 1), Point(mnDX - 1, mnDY - 1)); + // #i40393# draw left and right border, if WB_BORDER was set in ImplInit() + if (mnBorderOff1 && mnBorderOff2) + { + rRenderContext.DrawLine(Point(0, 0), Point(0, mnDY - 1)); + rRenderContext.DrawLine(Point(mnDX - 1, 0), Point(mnDX - 1, mnDY - 1)); + } + } + + sal_uInt16 nCurItemPos; + if (mbDrag) + nCurItemPos = GetItemPos(mnCurItemId); + else + nCurItemPos = HEADERBAR_ITEM_NOTFOUND; + sal_uInt16 nItemCount = static_cast<sal_uInt16>(mvItemList.size()); + for (sal_uInt16 i = 0; i < nItemCount; i++) + ImplDrawItem(rRenderContext, i, (i == nCurItemPos), &rRect); +} + +void HeaderBar::Draw( OutputDevice* pDev, const Point& rPos, + SystemTextColorFlags nFlags ) +{ + Point aPos = pDev->LogicToPixel( rPos ); + Size aSize = GetSizePixel(); + tools::Rectangle aRect( aPos, aSize ); + vcl::Font aFont = GetDrawPixelFont( pDev ); + + pDev->Push(); + pDev->SetMapMode(); + pDev->SetFont( aFont ); + if ( nFlags & SystemTextColorFlags::Mono ) + pDev->SetTextColor( COL_BLACK ); + else + pDev->SetTextColor( GetTextColor() ); + pDev->SetTextFillColor(); + + // draw background + { + pDev->DrawWallpaper( aRect, GetBackground() ); + if ( mnBorderOff1 || mnBorderOff2 ) + { + pDev->SetLineColor( GetSettings().GetStyleSettings().GetDarkShadowColor() ); + if ( mnBorderOff1 ) + pDev->DrawLine( aRect.TopLeft(), Point( aRect.Right(), aRect.Top() ) ); + if ( mnBorderOff2 ) + pDev->DrawLine( Point( aRect.Left(), aRect.Bottom() ), Point( aRect.Right(), aRect.Bottom() ) ); + // #i40393# draw left and right border, if WB_BORDER was set in ImplInit() + if ( mnBorderOff1 && mnBorderOff2 ) + { + pDev->DrawLine( aRect.TopLeft(), Point( aRect.Left(), aRect.Bottom() ) ); + pDev->DrawLine( Point( aRect.Right(), aRect.Top() ), Point( aRect.Right(), aRect.Bottom() ) ); + } + } + } + + tools::Rectangle aItemRect( aRect ); + size_t nItemCount = mvItemList.size(); + for ( size_t i = 0; i < nItemCount; i++ ) + { + aItemRect.SetLeft( aRect.Left()+ImplGetItemPos( i ) ); + aItemRect.SetRight( aItemRect.Left() + mvItemList[ i ]->mnSize - 1 ); + // check for overflow on some systems + if ( aItemRect.Right() > 16000 ) + aItemRect.SetRight( 16000 ); + vcl::Region aRegion( aRect ); + pDev->SetClipRegion( aRegion ); + ImplDrawItem(*pDev, i, false, aItemRect, &aRect ); + pDev->SetClipRegion(); + } + + pDev->Pop(); +} + +void HeaderBar::Resize() +{ + Size aSize = GetOutputSizePixel(); + if ( IsVisible() && (mnDY != aSize.Height()) ) + Invalidate(); + mnDX = aSize.Width(); + mnDY = aSize.Height(); +} + +void HeaderBar::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.IsMouseEvent() && (rCEvt.GetCommand() == CommandEventId::StartDrag) && !mbDrag ) + { + ImplStartDrag( rCEvt.GetMousePosPixel(), true ); + return; + } + + Window::Command( rCEvt ); +} + +void HeaderBar::RequestHelp( const HelpEvent& rHEvt ) +{ + sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) ); + if ( nItemId ) + { + if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) ) + { + tools::Rectangle aItemRect = GetItemRect( nItemId ); + Point aPt = OutputToScreenPixel( aItemRect.TopLeft() ); + aItemRect.SetLeft( aPt.X() ); + aItemRect.SetTop( aPt.Y() ); + aPt = OutputToScreenPixel( aItemRect.BottomRight() ); + aItemRect.SetRight( aPt.X() ); + aItemRect.SetBottom( aPt.Y() ); + + OUString aStr = GetHelpText( nItemId ); + if ( aStr.isEmpty() || !(rHEvt.GetMode() & HelpEventMode::BALLOON) ) + { + auto& pItem = mvItemList[ GetItemPos( nItemId ) ]; + // Quick-help is only displayed if the text is not fully visible. + // Otherwise we display Helptext only if the items do not contain text + if ( pItem->maOutText != pItem->maText ) + aStr = pItem->maText; + else if (!pItem->maText.isEmpty()) + aStr.clear(); + } + + if (!aStr.isEmpty()) + { + if ( rHEvt.GetMode() & HelpEventMode::BALLOON ) + Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr ); + else + Help::ShowQuickHelp( this, aItemRect, aStr ); + return; + } + } + } + + Window::RequestHelp( rHEvt ); +} + +void HeaderBar::StateChanged( StateChangedType nType ) +{ + Window::StateChanged( nType ); + + if ( nType == StateChangedType::Enable ) + Invalidate(); + else if ( (nType == StateChangedType::Zoom) || + (nType == StateChangedType::ControlFont) ) + { + ImplInitSettings( true, false, false ); + Invalidate(); + } + else if ( nType == StateChangedType::ControlForeground ) + { + ImplInitSettings( false, true, false ); + Invalidate(); + } + else if ( nType == StateChangedType::ControlBackground ) + { + ImplInitSettings( false, false, true ); + Invalidate(); + } +} + +void HeaderBar::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + ImplInitSettings( true, true, true ); + Invalidate(); + } +} + +void HeaderBar::EndDrag() +{ + maEndDragHdl.Call( this ); +} + +void HeaderBar::Select() +{ + maSelectHdl.Call( this ); +} + +void HeaderBar::DoubleClick() +{ +} + +void HeaderBar::InsertItem( sal_uInt16 nItemId, const OUString& rText, + tools::Long nSize, HeaderBarItemBits nBits, sal_uInt16 nPos ) +{ + DBG_ASSERT( nItemId, "HeaderBar::InsertItem(): ItemId == 0" ); + DBG_ASSERT( GetItemPos( nItemId ) == HEADERBAR_ITEM_NOTFOUND, + "HeaderBar::InsertItem(): ItemId already exists" ); + + // create item and insert in the list + std::unique_ptr<ImplHeadItem> pItem(new ImplHeadItem); + pItem->mnId = nItemId; + pItem->mnBits = nBits; + pItem->mnSize = nSize; + pItem->maText = rText; + if ( nPos < mvItemList.size() ) { + auto it = mvItemList.begin(); + it += nPos; + mvItemList.insert( it, std::move(pItem) ); + } else { + mvItemList.push_back( std::move(pItem) ); + } + + // update display + ImplUpdate( nPos, true ); +} + +void HeaderBar::RemoveItem( sal_uInt16 nItemId ) +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + { + if ( nPos < mvItemList.size() ) { + auto it = mvItemList.begin(); + it += nPos; + mvItemList.erase( it ); + } + } +} + +void HeaderBar::MoveItem( sal_uInt16 nItemId, sal_uInt16 nNewPos ) +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos == HEADERBAR_ITEM_NOTFOUND ) + return; + + if ( nPos == nNewPos ) + return; + + auto it = mvItemList.begin(); + it += nPos; + std::unique_ptr<ImplHeadItem> pItem = std::move(*it); + mvItemList.erase( it ); + if ( nNewPos < nPos ) + nPos = nNewPos; + it = mvItemList.begin(); + it += nNewPos; + mvItemList.insert( it, std::move(pItem) ); + ImplUpdate( nPos, true); +} + +void HeaderBar::Clear() +{ + // delete all items + mvItemList.clear(); + + ImplUpdate( 0, true ); +} + +void HeaderBar::SetOffset( tools::Long nNewOffset ) +{ + // tdf#129856 (see also #i40393#) invalidate old left and right border area if WB_BORDER was set in ImplInit() + if (mnBorderOff1 && mnBorderOff2) + { + Invalidate(tools::Rectangle(0, 0, 1, mnDY)); + Invalidate(tools::Rectangle(mnDX - 1, 0, mnDX, mnDY)); + } + + // move area + tools::Rectangle aRect( 0, mnBorderOff1, mnDX-1, mnDY-mnBorderOff1-mnBorderOff2 ); + tools::Long nDelta = mnOffset-nNewOffset; + mnOffset = nNewOffset; + Scroll( nDelta, 0, aRect ); +} + +sal_uInt16 HeaderBar::GetItemCount() const +{ + return static_cast<sal_uInt16>(mvItemList.size()); +} + +sal_uInt16 HeaderBar::GetItemPos( sal_uInt16 nItemId ) const +{ + for ( size_t i = 0, n = mvItemList.size(); i < n; ++i ) { + auto& pItem = mvItemList[ i ]; + if ( pItem->mnId == nItemId ) + return static_cast<sal_uInt16>(i); + } + return HEADERBAR_ITEM_NOTFOUND; +} + +sal_uInt16 HeaderBar::GetItemId( sal_uInt16 nPos ) const +{ + ImplHeadItem* pItem = (nPos < mvItemList.size() ) ? mvItemList[ nPos ].get() : nullptr; + if ( pItem ) + return pItem->mnId; + else + return 0; +} + +sal_uInt16 HeaderBar::GetItemId( const Point& rPos ) const +{ + for ( size_t i = 0, n = mvItemList.size(); i < n; ++i ) { + if ( ImplGetItemRect( i ).Contains( rPos ) ) { + return GetItemId( i ); + } + } + return 0; +} + +tools::Rectangle HeaderBar::GetItemRect( sal_uInt16 nItemId ) const +{ + tools::Rectangle aRect; + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + aRect = ImplGetItemRect( nPos ); + return aRect; +} + +void HeaderBar::SetItemSize( sal_uInt16 nItemId, tools::Long nNewSize ) +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + { + auto& pItem = mvItemList[ nPos ]; + if ( pItem->mnSize != nNewSize ) + { + pItem->mnSize = nNewSize; + ImplUpdate( nPos, true ); + } + } +} + +tools::Long HeaderBar::GetItemSize( sal_uInt16 nItemId ) const +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + return mvItemList[ nPos ]->mnSize; + else + return 0; +} + +void HeaderBar::SetItemBits( sal_uInt16 nItemId, HeaderBarItemBits nNewBits ) +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + { + auto& pItem = mvItemList[ nPos ]; + if ( pItem->mnBits != nNewBits ) + { + pItem->mnBits = nNewBits; + ImplUpdate( nPos ); + } + } +} + +HeaderBarItemBits HeaderBar::GetItemBits( sal_uInt16 nItemId ) const +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + return mvItemList[ nPos ]->mnBits; + else + return HeaderBarItemBits::NONE; +} + +void HeaderBar::SetItemText( sal_uInt16 nItemId, const OUString& rText ) +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + { + mvItemList[ nPos ]->maText = rText; + ImplUpdate( nPos ); + } +} + +OUString HeaderBar::GetItemText( sal_uInt16 nItemId ) const +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + return mvItemList[ nPos ]->maText; + return OUString(); +} + +OUString HeaderBar::GetHelpText( sal_uInt16 nItemId ) const +{ + sal_uInt16 nPos = GetItemPos( nItemId ); + if ( nPos != HEADERBAR_ITEM_NOTFOUND ) + { + auto& pItem = mvItemList[ nPos ]; + if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() ) + { + Help* pHelp = Application::GetHelp(); + if ( pHelp ) + pItem->maHelpText = pHelp->GetHelpText( OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this ); + } + + return pItem->maHelpText; + } + + return OUString(); +} + +Size HeaderBar::CalcWindowSizePixel() const +{ + tools::Long nMaxImageSize = 0; + Size aSize( 0, GetTextHeight() ); + + for (auto& pItem : mvItemList) + { + // take image size into account + tools::Long nImageHeight = pItem->maImage.GetSizePixel().Height(); + if ( !(pItem->mnBits & (HeaderBarItemBits::LEFTIMAGE | HeaderBarItemBits::RIGHTIMAGE)) && !pItem->maText.isEmpty() ) + nImageHeight += aSize.Height(); + if ( nImageHeight > nMaxImageSize ) + nMaxImageSize = nImageHeight; + + // add width + aSize.AdjustWidth(pItem->mnSize ); + } + + if ( nMaxImageSize > aSize.Height() ) + aSize.setHeight( nMaxImageSize ); + + // add border + if ( mbButtonStyle ) + aSize.AdjustHeight(4 ); + else + aSize.AdjustHeight(2 ); + aSize.AdjustHeight(mnBorderOff1+mnBorderOff2 ); + + return aSize; +} + +css::uno::Reference< css::accessibility::XAccessible > HeaderBar::CreateAccessible() +{ + if ( !mxAccessible.is() ) + { + maCreateAccessibleHdl.Call( this ); + + if ( !mxAccessible.is() ) + mxAccessible = Window::CreateAccessible(); + } + + return mxAccessible; +} + +void HeaderBar::SetAccessible( const css::uno::Reference< css::accessibility::XAccessible >& _xAccessible ) +{ + mxAccessible = _xAccessible; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |