1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sal/log.hxx>
22 #include <comphelper/string.hxx>
23 #include <vcl/event.hxx>
24 #include <vcl/decoview.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/help.hxx>
27 #include <vcl/vcllayout.hxx>
28 #include <vcl/status.hxx>
29 #include <vcl/virdev.hxx>
30 #include <vcl/settings.hxx>
31 #include <config_features.h>
32 #if HAVE_FEATURE_OPENGL
33 #include <vcl/opengl/OpenGLWrapper.hxx>
38 #define STATUSBAR_OFFSET_X STATUSBAR_OFFSET
39 #define STATUSBAR_OFFSET_Y 2
40 #define STATUSBAR_OFFSET_TEXTY 3
42 #define STATUSBAR_PRGS_OFFSET 3
43 #define STATUSBAR_PRGS_COUNT 100
44 #define STATUSBAR_PRGS_MIN 5
46 class StatusBar::ImplData
51 VclPtr
<VirtualDevice
> mpVirDev
;
54 StatusBar::ImplData::ImplData()
62 StatusBarItemBits mnBits
;
69 OUString maQuickHelpText
;
73 OUString maAccessibleName
;
75 std::unique_ptr
<SalLayout
> mxLayoutCache
;
78 static long ImplCalcProgressWidth( sal_uInt16 nMax
, long nSize
)
80 return ((nMax
*(nSize
+(nSize
/2)))-(nSize
/2)+(STATUSBAR_PRGS_OFFSET
*2));
83 static Point
ImplGetItemTextPos( const Size
& rRectSize
, const Size
& rTextSize
,
84 StatusBarItemBits nStyle
)
88 long delta
= (rTextSize
.Height()/4) + 1;
89 if( delta
+ rTextSize
.Width() > rRectSize
.Width() )
92 if ( nStyle
& StatusBarItemBits::Left
)
94 else if ( nStyle
& StatusBarItemBits::Right
)
95 nX
= rRectSize
.Width()-rTextSize
.Width()-delta
;
96 else // StatusBarItemBits::Center
97 nX
= (rRectSize
.Width()-rTextSize
.Width())/2;
98 nY
= (rRectSize
.Height()-rTextSize
.Height())/2 + 1;
99 return Point( nX
, nY
);
102 bool StatusBar::ImplIsItemUpdate()
104 return !mbProgressMode
&& IsReallyVisible() && IsUpdateMode();
107 void StatusBar::ImplInit( vcl::Window
* pParent
, WinBits nStyle
)
109 mpImplData
.reset(new ImplData
);
111 // default: RightAlign
112 if ( !(nStyle
& (WB_LEFT
| WB_RIGHT
)) )
115 Window::ImplInit( pParent
, nStyle
& ~WB_BORDER
, nullptr );
118 mpImplData
->mpVirDev
= VclPtr
<VirtualDevice
>::Create( *this );
121 mbProgressMode
= false;
122 mbInUserDraw
= false;
123 mbAdjustHiDPI
= false;
124 mnItemsWidth
= STATUSBAR_OFFSET_X
;
128 mnTextY
= STATUSBAR_OFFSET_TEXTY
;
132 SetOutputSizePixel( CalcWindowSizePixel() );
135 StatusBar::StatusBar( vcl::Window
* pParent
, WinBits nStyle
) :
136 Window( WindowType::STATUSBAR
),
137 mnLastProgressPaint_ms(osl_getGlobalTimer())
139 ImplInit( pParent
, nStyle
);
142 StatusBar::~StatusBar()
147 void StatusBar::dispose()
152 // delete VirtualDevice
153 mpImplData
->mpVirDev
.disposeAndClear();
158 void StatusBar::AdjustItemWidthsForHiDPI()
160 mbAdjustHiDPI
= true;
163 void StatusBar::ApplySettings(vcl::RenderContext
& rRenderContext
)
165 rRenderContext
.SetLineColor();
167 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
168 ApplyControlFont(rRenderContext
, rStyleSettings
.GetToolFont());
171 if (IsControlForeground())
172 aColor
= GetControlForeground();
173 else if (GetStyle() & WB_3DLOOK
)
174 aColor
= rStyleSettings
.GetButtonTextColor();
176 aColor
= rStyleSettings
.GetWindowTextColor();
177 rRenderContext
.SetTextColor(aColor
);
179 rRenderContext
.SetTextFillColor();
181 if (IsControlBackground())
182 aColor
= GetControlBackground();
183 else if (GetStyle() & WB_3DLOOK
)
184 aColor
= rStyleSettings
.GetFaceColor();
186 aColor
= rStyleSettings
.GetWindowColor();
187 rRenderContext
.SetBackground(aColor
);
190 if (!IsControlBackground() &&
191 rRenderContext
.IsNativeControlSupported(ControlType::WindowBackground
, ControlPart::BackgroundWindow
))
193 ImplGetWindowImpl()->mnNativeBackground
= ControlPart::BackgroundWindow
;
194 EnableChildTransparentMode();
198 void StatusBar::ImplInitSettings()
200 ApplySettings(*this);
202 mpImplData
->mpVirDev
->SetFont(GetFont());
203 mpImplData
->mpVirDev
->SetTextColor(GetTextColor());
204 mpImplData
->mpVirDev
->SetTextAlign(GetTextAlign());
205 mpImplData
->mpVirDev
->SetTextFillColor();
206 mpImplData
->mpVirDev
->SetBackground(GetBackground());
209 void StatusBar::ImplFormat()
214 sal_uInt16 nAutoSizeItems
;
220 mnItemsWidth
= STATUSBAR_OFFSET_X
;
223 for ( const auto & pItem
: mvItemList
) {
224 if ( pItem
->mbVisible
)
226 if ( pItem
->mnBits
& StatusBarItemBits::AutoSize
) {
230 mnItemsWidth
+= pItem
->mnWidth
+ nOffset
;
231 nOffset
= pItem
->mnOffset
;
235 if ( mnDX
> 0 && mnDX
< mnItemsWidth
)
237 // Total width of items is more than available width
238 // Try to hide secondary elements, if any
239 for ( auto & pItem
: mvItemList
)
241 if ( pItem
->mbVisible
&& !(pItem
->mnBits
& StatusBarItemBits::Mandatory
) )
243 pItem
->mbVisible
= false;
249 else if ( mnDX
> mnItemsWidth
)
251 // Width of statusbar is sufficient.
252 // Try to restore hidden items, if any
253 for ( auto & pItem
: mvItemList
)
255 if ( !pItem
->mbVisible
&&
256 !(pItem
->mnBits
& StatusBarItemBits::Mandatory
) &&
257 pItem
->mnWidth
+ nOffset
+ mnItemsWidth
< mnDX
)
259 pItem
->mbVisible
= true;
265 } while ( bChanged
);
267 if ( GetStyle() & WB_RIGHT
)
269 // AutoSize isn't computed for right-alignment,
270 // because we show the text that is declared by SetText on the left side
271 nX
= mnDX
- mnItemsWidth
;
277 mnItemsWidth
+= STATUSBAR_OFFSET_X
;
279 // calling AutoSize is potentially necessary for left-aligned text,
280 if ( nAutoSizeItems
&& (mnDX
> (mnItemsWidth
- STATUSBAR_OFFSET
)) )
282 nExtraWidth
= (mnDX
- mnItemsWidth
- 1) / nAutoSizeItems
;
283 nExtraWidth2
= (mnDX
- mnItemsWidth
- 1) % nAutoSizeItems
;
290 nX
= STATUSBAR_OFFSET_X
;
292 if( HasMirroredGraphics() && IsRTLEnabled() )
293 nX
+= ImplGetSVData()->maNWFData
.mnStatusBarLowerRightOffset
;
296 for (auto & pItem
: mvItemList
) {
297 if ( pItem
->mbVisible
) {
298 if ( pItem
->mnBits
& StatusBarItemBits::AutoSize
) {
299 pItem
->mnExtraWidth
= nExtraWidth
;
300 if ( nExtraWidth2
) {
301 pItem
->mnExtraWidth
++;
305 pItem
->mnExtraWidth
= 0;
309 nX
+= pItem
->mnWidth
+ pItem
->mnExtraWidth
+ pItem
->mnOffset
;
316 tools::Rectangle
StatusBar::ImplGetItemRectPos( sal_uInt16 nPos
) const
318 tools::Rectangle aRect
;
319 ImplStatusItem
* pItem
= ( nPos
< mvItemList
.size() ) ? mvItemList
[ nPos
].get() : nullptr;
320 if ( pItem
&& pItem
->mbVisible
)
322 aRect
.SetLeft( pItem
->mnX
);
323 aRect
.SetRight( aRect
.Left() + pItem
->mnWidth
+ pItem
->mnExtraWidth
);
324 aRect
.SetTop( STATUSBAR_OFFSET_Y
);
325 aRect
.SetBottom( mnCalcHeight
- STATUSBAR_OFFSET_Y
);
331 sal_uInt16
StatusBar::ImplGetFirstVisiblePos() const
333 for( size_t nPos
= 0; nPos
< mvItemList
.size(); nPos
++ )
335 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
336 if ( pItem
->mbVisible
)
337 return sal_uInt16(nPos
);
340 return SAL_MAX_UINT16
;
343 void StatusBar::ImplDrawText(vcl::RenderContext
& rRenderContext
)
345 // prevent item box from being overwritten
346 tools::Rectangle aTextRect
;
347 aTextRect
.SetLeft( STATUSBAR_OFFSET_X
+ 1 );
348 aTextRect
.SetTop( mnTextY
);
349 if (GetStyle() & WB_RIGHT
)
350 aTextRect
.SetRight( mnDX
- mnItemsWidth
- 1 );
352 aTextRect
.SetRight( mnDX
- 1 );
353 if (aTextRect
.Right() > aTextRect
.Left())
356 OUString aStr
= GetText();
357 sal_Int32 nPos
= aStr
.indexOf('\n');
359 aStr
= aStr
.copy(0, nPos
);
361 aTextRect
.SetBottom( aTextRect
.Top()+GetTextHeight()+1 );
363 rRenderContext
.DrawText(aTextRect
, aStr
, DrawTextFlags::Left
| DrawTextFlags::Top
| DrawTextFlags::Clip
| DrawTextFlags::EndEllipsis
);
367 void StatusBar::ImplDrawItem(vcl::RenderContext
& rRenderContext
, bool bOffScreen
, sal_uInt16 nPos
)
369 tools::Rectangle aRect
= ImplGetItemRectPos(nPos
);
374 // compute output region
375 ImplStatusItem
* pItem
= mvItemList
[nPos
].get();
377 tools::Rectangle
aTextRect(aRect
.Left() + nW
, aRect
.Top() + nW
,
378 aRect
.Right() - nW
, aRect
.Bottom() - nW
);
380 Size
aTextRectSize(aTextRect
.GetSize());
384 mpImplData
->mpVirDev
->SetOutputSizePixel(aTextRectSize
);
388 vcl::Region
aRegion(aTextRect
);
389 rRenderContext
.SetClipRegion(aRegion
);
392 // if the framework code is drawing status, let it do all the work
393 if (!(pItem
->mnBits
& StatusBarItemBits::UserDraw
))
395 SalLayout
* pLayoutCache
= pItem
->mxLayoutCache
.get();
400 pItem
->mxLayoutCache
= rRenderContext
.ImplLayout(pItem
->maText
, 0, -1);
401 pLayoutCache
= pItem
->mxLayoutCache
.get();
404 const SalLayoutGlyphs
* pGlyphs
= pLayoutCache
? pLayoutCache
->GetGlyphs() : nullptr;
405 Size
aTextSize(rRenderContext
.GetTextWidth(pItem
->maText
,0,-1,nullptr,pGlyphs
), rRenderContext
.GetTextHeight());
406 Point aTextPos
= ImplGetItemTextPos(aTextRectSize
, aTextSize
, pItem
->mnBits
);
410 mpImplData
->mpVirDev
->DrawText(
413 0, -1, nullptr, nullptr,
418 aTextPos
.AdjustX(aTextRect
.Left() );
419 aTextPos
.AdjustY(aTextRect
.Top() );
420 rRenderContext
.DrawText(
423 0, -1, nullptr, nullptr,
428 // call DrawItem if necessary
429 if (pItem
->mnBits
& StatusBarItemBits::UserDraw
)
434 mpImplData
->mpVirDev
->EnableRTL( IsRTLEnabled() );
435 UserDrawEvent
aODEvt(this, mpImplData
->mpVirDev
, tools::Rectangle(Point(), aTextRectSize
), pItem
->mnId
);
437 mpImplData
->mpVirDev
->EnableRTL(false);
438 mbInUserDraw
= false;
442 UserDrawEvent
aODEvt(this, &rRenderContext
, aTextRect
, pItem
->mnId
);
448 rRenderContext
.DrawOutDev(aTextRect
.TopLeft(), aTextRectSize
, Point(), aTextRectSize
, *mpImplData
->mpVirDev
);
450 rRenderContext
.SetClipRegion();
452 if (nPos
!= ImplGetFirstVisiblePos())
455 Point
aFrom(aRect
.TopLeft());
458 Point
aTo(aRect
.BottomLeft());
462 DecorationView
aDecoView(&rRenderContext
);
463 aDecoView
.DrawSeparator(aFrom
, aTo
);
466 if (!rRenderContext
.ImplIsRecordLayout())
467 CallEventListeners(VclEventId::StatusbarDrawItem
, reinterpret_cast<void*>(pItem
->mnId
));
470 void DrawProgress(vcl::Window
* pWindow
, vcl::RenderContext
& rRenderContext
, const Point
& rPos
,
471 long nOffset
, long nPrgsWidth
, long nPrgsHeight
,
472 sal_uInt16 nPercent1
, sal_uInt16 nPercent2
, sal_uInt16 nPercentCount
,
473 const tools::Rectangle
& rFramePosSize
)
475 if (rRenderContext
.IsNativeControlSupported(ControlType::Progress
, ControlPart::Entire
))
477 bool bNeedErase
= ImplGetSVData()->maNWFData
.mbProgressNeedsErase
;
479 long nFullWidth
= (nPrgsWidth
+ nOffset
) * (10000 / nPercentCount
);
480 long nPerc
= std::min
<sal_uInt16
>(nPercent2
, 10000);
481 ImplControlValue
aValue(nFullWidth
* nPerc
/ 10000);
482 tools::Rectangle
aDrawRect(rPos
, Size(nFullWidth
, nPrgsHeight
));
483 tools::Rectangle
aControlRegion(aDrawRect
);
487 vcl::Window
* pEraseWindow
= pWindow
;
488 while (pEraseWindow
->IsPaintTransparent() && !pEraseWindow
->ImplGetWindowImpl()->mbFrame
)
490 pEraseWindow
= pEraseWindow
->ImplGetWindowImpl()->mpParent
;
493 if (pEraseWindow
== pWindow
)
495 // restore background of pWindow
496 rRenderContext
.Erase(rFramePosSize
);
500 // restore transparent background
501 Point
aTL(pWindow
->OutputToAbsoluteScreenPixel(rFramePosSize
.TopLeft()));
502 aTL
= pEraseWindow
->AbsoluteScreenToOutputPixel(aTL
);
503 tools::Rectangle
aRect(aTL
, rFramePosSize
.GetSize());
504 pEraseWindow
->Invalidate(aRect
, InvalidateFlags::NoChildren
|
505 InvalidateFlags::NoClipChildren
|
506 InvalidateFlags::Transparent
);
507 pEraseWindow
->Update();
509 rRenderContext
.Push(PushFlags::CLIPREGION
);
510 rRenderContext
.IntersectClipRegion(rFramePosSize
);
513 bool bNativeOK
= rRenderContext
.DrawNativeControl(ControlType::Progress
, ControlPart::Entire
, aControlRegion
,
514 ControlState::ENABLED
, aValue
, OUString());
516 rRenderContext
.Pop();
522 sal_uInt16 nPerc1
= nPercent1
/ nPercentCount
;
523 sal_uInt16 nPerc2
= nPercent2
/ nPercentCount
;
527 // support progress that can also decrease
530 long nDX
= nPrgsWidth
+ nOffset
;
531 long nLeft
= rPos
.X() + ((nPerc1
- 1) * nDX
);
532 tools::Rectangle
aRect(nLeft
, rPos
.Y(), nLeft
+ nPrgsWidth
, rPos
.Y() + nPrgsHeight
);
536 rRenderContext
.Erase(aRect
);
537 aRect
.AdjustLeft( -nDX
);
538 aRect
.AdjustRight( -nDX
);
541 while (nPerc1
> nPerc2
);
543 else if (nPerc1
< nPerc2
)
545 // draw Percent rectangle
546 // if Percent2 greater than 100%, adapt values
547 if (nPercent2
> 10000)
549 nPerc2
= 10000 / nPercentCount
;
550 if (nPerc1
>= nPerc2
)
555 long nDX
= nPrgsWidth
+ nOffset
;
556 long nLeft
= rPos
.X() + (nPerc1
* nDX
);
557 tools::Rectangle
aRect(nLeft
, rPos
.Y(), nLeft
+ nPrgsWidth
, rPos
.Y() + nPrgsHeight
);
561 rRenderContext
.DrawRect(aRect
);
562 aRect
.AdjustLeft(nDX
);
563 aRect
.AdjustRight(nDX
);
566 while (nPerc1
< nPerc2
);
568 // if greater than 100%, set rectangle to blink
569 if (nPercent2
> 10000)
571 // define on/off status
572 if (((nPercent2
/ nPercentCount
) & 0x01) == (nPercentCount
& 0x01))
574 aRect
.AdjustLeft( -nDX
);
575 aRect
.AdjustRight( -nDX
);
576 rRenderContext
.Erase(aRect
);
582 void StatusBar::ImplDrawProgress(vcl::RenderContext
& rRenderContext
, sal_uInt16 nPercent2
)
584 bool bNative
= rRenderContext
.IsNativeControlSupported(ControlType::Progress
, ControlPart::Entire
);
585 // bPaint: draw text also, else only update progress
586 rRenderContext
.DrawText(maPrgsTxtPos
, maPrgsTxt
);
589 DecorationView
aDecoView(&rRenderContext
);
590 aDecoView
.DrawFrame(maPrgsFrameRect
, DrawFrameStyle::In
);
593 Point
aPos(maPrgsFrameRect
.Left() + STATUSBAR_PRGS_OFFSET
,
594 maPrgsFrameRect
.Top() + STATUSBAR_PRGS_OFFSET
);
595 long nPrgsHeight
= mnPrgsSize
;
598 aPos
= maPrgsFrameRect
.TopLeft();
599 nPrgsHeight
= maPrgsFrameRect
.GetHeight();
601 DrawProgress(this, rRenderContext
, aPos
, mnPrgsSize
/ 2, mnPrgsSize
, nPrgsHeight
,
602 0, nPercent2
* 100, mnPercentCount
, maPrgsFrameRect
);
605 void StatusBar::ImplCalcProgressRect()
607 // calculate text size
608 Size
aPrgsTxtSize( GetTextWidth( maPrgsTxt
), GetTextHeight() );
609 maPrgsTxtPos
.setX( STATUSBAR_OFFSET_X
+1 );
611 // calculate progress frame
612 maPrgsFrameRect
.SetLeft( maPrgsTxtPos
.X()+aPrgsTxtSize
.Width()+STATUSBAR_OFFSET
);
613 maPrgsFrameRect
.SetTop( STATUSBAR_OFFSET_Y
);
614 maPrgsFrameRect
.SetBottom( mnCalcHeight
- STATUSBAR_OFFSET_Y
);
616 // calculate size of progress rects
617 mnPrgsSize
= maPrgsFrameRect
.Bottom()-maPrgsFrameRect
.Top()-(STATUSBAR_PRGS_OFFSET
*2);
618 sal_uInt16 nMaxPercent
= STATUSBAR_PRGS_COUNT
;
620 long nMaxWidth
= mnDX
-STATUSBAR_OFFSET
-1;
622 // make smaller if there are too many rects
623 while ( maPrgsFrameRect
.Left()+ImplCalcProgressWidth( nMaxPercent
, mnPrgsSize
) > nMaxWidth
)
626 if ( nMaxPercent
<= STATUSBAR_PRGS_MIN
)
629 maPrgsFrameRect
.SetRight( maPrgsFrameRect
.Left() + ImplCalcProgressWidth( nMaxPercent
, mnPrgsSize
) );
631 // save the divisor for later
632 mnPercentCount
= 10000 / nMaxPercent
;
633 bool bNativeOK
= false;
634 if( IsNativeControlSupported( ControlType::Progress
, ControlPart::Entire
) )
636 ImplControlValue aValue
;
637 tools::Rectangle
aControlRegion( tools::Rectangle( Point(), maPrgsFrameRect
.GetSize() ) );
638 tools::Rectangle aNativeControlRegion
, aNativeContentRegion
;
639 if( (bNativeOK
= GetNativeControlRegion( ControlType::Progress
, ControlPart::Entire
, aControlRegion
,
640 ControlState::ENABLED
, aValue
,
641 aNativeControlRegion
, aNativeContentRegion
) ) )
643 long nProgressHeight
= aNativeControlRegion
.GetHeight();
644 if( nProgressHeight
> maPrgsFrameRect
.GetHeight() )
646 long nDelta
= nProgressHeight
- maPrgsFrameRect
.GetHeight();
647 maPrgsFrameRect
.AdjustTop( -(nDelta
- nDelta
/2) );
648 maPrgsFrameRect
.AdjustBottom(nDelta
/2 );
650 maPrgsTxtPos
.setY( maPrgsFrameRect
.Top() + (nProgressHeight
- GetTextHeight())/2 );
654 maPrgsTxtPos
.setY( mnTextY
);
657 void StatusBar::MouseButtonDown( const MouseEvent
& rMEvt
)
659 // trigger toolbox only for left mouse button
660 if ( rMEvt
.IsLeft() )
662 Point aMousePos
= rMEvt
.GetPosPixel();
664 // search for clicked item
665 for ( size_t i
= 0; i
< mvItemList
.size(); ++i
)
667 ImplStatusItem
* pItem
= mvItemList
[ i
].get();
668 // check item for being clicked
669 if ( ImplGetItemRectPos( sal_uInt16(i
) ).IsInside( aMousePos
) )
671 mnCurItemId
= pItem
->mnId
;
672 if ( rMEvt
.GetClicks() == 2 )
683 // if there's no item, trigger Click or DoubleClick
684 if ( rMEvt
.GetClicks() == 2 )
691 void StatusBar::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
696 sal_uInt16 nItemCount
= sal_uInt16( mvItemList
.size() );
700 rRenderContext
.Push(PushFlags::FILLCOLOR
| PushFlags::LINECOLOR
);
702 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
703 Color aProgressColor
= rStyleSettings
.GetHighlightColor();
704 if (aProgressColor
== rStyleSettings
.GetFaceColor())
705 aProgressColor
= rStyleSettings
.GetDarkShadowColor();
706 rRenderContext
.SetLineColor();
707 rRenderContext
.SetFillColor(aProgressColor
);
709 ImplDrawProgress(rRenderContext
, mnPercent
);
711 rRenderContext
.Pop();
716 if (GetStyle() & WB_RIGHT
)
717 ImplDrawText(rRenderContext
);
721 // Do offscreen only when we are not recording layout...
722 bool bOffscreen
= !rRenderContext
.ImplIsRecordLayout();
724 // tdf#94213 - un-necessary virtual-device in GL mode
725 // causes context switch & hence flicker during sizing.
726 #if HAVE_FEATURE_OPENGL
727 if( OpenGLWrapper::isVCLOpenGLEnabled() )
732 rRenderContext
.Erase(rRect
);
734 for (sal_uInt16 i
= 0; i
< nItemCount
; i
++)
735 ImplDrawItem(rRenderContext
, bOffscreen
, i
);
738 // draw line at the top of the status bar (to visually distinguish it from
739 // shell / docking area)
740 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
741 rRenderContext
.SetLineColor(rStyleSettings
.GetShadowColor());
742 rRenderContext
.DrawLine(Point(0, 0), Point(mnDX
-1, 0));
745 void StatusBar::Resize()
747 // save width and height
748 Size aSize
= GetOutputSizePixel();
749 mnDX
= aSize
.Width() - ImplGetSVData()->maNWFData
.mnStatusBarLowerRightOffset
;
750 mnDY
= aSize
.Height();
753 mnTextY
= (mnCalcHeight
-GetTextHeight())/2;
755 // provoke re-formatting
758 if ( mbProgressMode
)
759 ImplCalcProgressRect();
764 void StatusBar::RequestHelp( const HelpEvent
& rHEvt
)
766 // no keyboard help in status bar
767 if( rHEvt
.KeyboardActivated() )
770 sal_uInt16 nItemId
= GetItemId( ScreenToOutputPixel( rHEvt
.GetMousePosPixel() ) );
774 tools::Rectangle aItemRect
= GetItemRect( nItemId
);
775 Point aPt
= OutputToScreenPixel( aItemRect
.TopLeft() );
776 aItemRect
.SetLeft( aPt
.X() );
777 aItemRect
.SetTop( aPt
.Y() );
778 aPt
= OutputToScreenPixel( aItemRect
.BottomRight() );
779 aItemRect
.SetRight( aPt
.X() );
780 aItemRect
.SetBottom( aPt
.Y() );
782 if ( rHEvt
.GetMode() & HelpEventMode::BALLOON
)
784 OUString aStr
= GetHelpText( nItemId
);
785 Help::ShowBalloon( this, aItemRect
.Center(), aItemRect
, aStr
);
788 else if ( rHEvt
.GetMode() & HelpEventMode::QUICK
)
790 OUString
aStr(GetQuickHelpText(nItemId
));
791 // show quickhelp if available
794 Help::ShowQuickHelp( this, aItemRect
, aStr
);
797 aStr
= GetItemText( nItemId
);
798 // show a quick help if item text doesn't fit
799 if ( GetTextWidth( aStr
) > aItemRect
.GetWidth() )
801 Help::ShowQuickHelp( this, aItemRect
, aStr
);
807 Window::RequestHelp( rHEvt
);
810 void StatusBar::StateChanged( StateChangedType nType
)
812 Window::StateChanged( nType
);
814 if ( nType
== StateChangedType::InitShow
)
816 else if ( nType
== StateChangedType::UpdateMode
)
818 else if ( (nType
== StateChangedType::Zoom
) ||
819 (nType
== StateChangedType::ControlFont
) )
825 else if ( nType
== StateChangedType::ControlForeground
)
830 else if ( nType
== StateChangedType::ControlBackground
)
836 //invalidate layout cache
837 for (auto & pItem
: mvItemList
)
839 pItem
->mxLayoutCache
.reset();
844 void StatusBar::DataChanged( const DataChangedEvent
& rDCEvt
)
846 Window::DataChanged( rDCEvt
);
848 if ( (rDCEvt
.GetType() == DataChangedEventType::DISPLAY
)
849 || (rDCEvt
.GetType() == DataChangedEventType::FONTS
)
850 || (rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
)
851 || ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
)
852 && (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
)
858 long nFudge
= GetTextHeight() / 4;
859 for (auto & pItem
: mvItemList
)
861 long nWidth
= GetTextWidth( pItem
->maText
) + nFudge
;
862 if( nWidth
> pItem
->mnWidth
+ STATUSBAR_OFFSET
)
863 pItem
->mnWidth
= nWidth
+ STATUSBAR_OFFSET
;
865 pItem
->mxLayoutCache
.reset();
867 Size aSize
= GetSizePixel();
868 // do not disturb current width, since
869 // CalcWindowSizePixel calculates a minimum width
870 aSize
.setHeight( CalcWindowSizePixel().Height() );
871 SetSizePixel( aSize
);
876 void StatusBar::Click()
878 maClickHdl
.Call( this );
881 void StatusBar::DoubleClick()
883 maDoubleClickHdl
.Call( this );
886 void StatusBar::UserDraw( const UserDrawEvent
& )
890 void StatusBar::InsertItem( sal_uInt16 nItemId
, sal_uLong nWidth
,
891 StatusBarItemBits nBits
,
892 long nOffset
, sal_uInt16 nPos
)
894 SAL_WARN_IF( !nItemId
, "vcl", "StatusBar::InsertItem(): ItemId == 0" );
895 SAL_WARN_IF( GetItemPos( nItemId
) != STATUSBAR_ITEM_NOTFOUND
, "vcl",
896 "StatusBar::InsertItem(): ItemId already exists" );
898 // default: IN and CENTER
899 if ( !(nBits
& (StatusBarItemBits::In
| StatusBarItemBits::Out
| StatusBarItemBits::Flat
)) )
900 nBits
|= StatusBarItemBits::In
;
901 if ( !(nBits
& (StatusBarItemBits::Left
| StatusBarItemBits::Right
| StatusBarItemBits::Center
)) )
902 nBits
|= StatusBarItemBits::Center
;
907 nWidth
*= GetDPIScaleFactor();
909 long nFudge
= GetTextHeight()/4;
910 std::unique_ptr
<ImplStatusItem
> pItem(new ImplStatusItem
);
911 pItem
->mnId
= nItemId
;
912 pItem
->mnBits
= nBits
;
913 pItem
->mnWidth
= static_cast<long>(nWidth
)+nFudge
+STATUSBAR_OFFSET
;
914 pItem
->mnOffset
= nOffset
;
915 pItem
->mpUserData
= nullptr;
916 pItem
->mbVisible
= true;
919 if ( nPos
< mvItemList
.size() ) {
920 mvItemList
.insert( mvItemList
.begin() + nPos
, std::move(pItem
) );
922 mvItemList
.push_back( std::move(pItem
) );
926 if ( ImplIsItemUpdate() )
929 CallEventListeners( VclEventId::StatusbarItemAdded
, reinterpret_cast<void*>(nItemId
) );
932 void StatusBar::RemoveItem( sal_uInt16 nItemId
)
934 sal_uInt16 nPos
= GetItemPos( nItemId
);
935 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
937 mvItemList
.erase( mvItemList
.begin() + nPos
);
940 if ( ImplIsItemUpdate() )
943 CallEventListeners( VclEventId::StatusbarItemRemoved
, reinterpret_cast<void*>(nItemId
) );
947 void StatusBar::ShowItem( sal_uInt16 nItemId
)
949 sal_uInt16 nPos
= GetItemPos( nItemId
);
951 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
953 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
954 if ( !pItem
->mbVisible
)
956 pItem
->mbVisible
= true;
959 if ( ImplIsItemUpdate() )
962 CallEventListeners( VclEventId::StatusbarShowItem
, reinterpret_cast<void*>(nItemId
) );
967 void StatusBar::HideItem( sal_uInt16 nItemId
)
969 sal_uInt16 nPos
= GetItemPos( nItemId
);
971 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
973 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
974 if ( pItem
->mbVisible
)
976 pItem
->mbVisible
= false;
979 if ( ImplIsItemUpdate() )
982 CallEventListeners( VclEventId::StatusbarHideItem
, reinterpret_cast<void*>(nItemId
) );
987 bool StatusBar::IsItemVisible( sal_uInt16 nItemId
) const
989 sal_uInt16 nPos
= GetItemPos( nItemId
);
991 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
992 return mvItemList
[ nPos
]->mbVisible
;
997 void StatusBar::Clear()
1003 if ( ImplIsItemUpdate() )
1006 CallEventListeners( VclEventId::StatusbarAllItemsRemoved
);
1009 sal_uInt16
StatusBar::GetItemCount() const
1011 return static_cast<sal_uInt16
>(mvItemList
.size());
1014 sal_uInt16
StatusBar::GetItemId( sal_uInt16 nPos
) const
1016 if ( nPos
< mvItemList
.size() )
1017 return mvItemList
[ nPos
]->mnId
;
1021 sal_uInt16
StatusBar::GetItemPos( sal_uInt16 nItemId
) const
1023 for ( size_t i
= 0, n
= mvItemList
.size(); i
< n
; ++i
) {
1024 if ( mvItemList
[ i
]->mnId
== nItemId
) {
1025 return sal_uInt16( i
);
1029 return STATUSBAR_ITEM_NOTFOUND
;
1032 sal_uInt16
StatusBar::GetItemId( const Point
& rPos
) const
1036 sal_uInt16 nItemCount
= GetItemCount();
1038 for ( nPos
= 0; nPos
< nItemCount
; nPos
++ )
1041 tools::Rectangle aRect
= ImplGetItemRectPos( nPos
);
1042 if ( aRect
.IsInside( rPos
) )
1043 return mvItemList
[ nPos
]->mnId
;
1050 tools::Rectangle
StatusBar::GetItemRect( sal_uInt16 nItemId
) const
1052 tools::Rectangle aRect
;
1056 sal_uInt16 nPos
= GetItemPos( nItemId
);
1057 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1059 // get rectangle and subtract frame
1060 aRect
= ImplGetItemRectPos( nPos
);
1062 aRect
.AdjustTop(nW
-1 );
1063 aRect
.AdjustBottom( -(nW
-1) );
1064 aRect
.AdjustLeft(nW
);
1065 aRect
.AdjustRight( -nW
);
1073 Point
StatusBar::GetItemTextPos( sal_uInt16 nItemId
) const
1077 sal_uInt16 nPos
= GetItemPos( nItemId
);
1078 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1081 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1082 tools::Rectangle aRect
= ImplGetItemRectPos( nPos
);
1084 tools::Rectangle
aTextRect( aRect
.Left()+nW
, aRect
.Top()+nW
,
1085 aRect
.Right()-nW
, aRect
.Bottom()-nW
);
1086 Point aPos
= ImplGetItemTextPos( aTextRect
.GetSize(),
1087 Size( GetTextWidth( pItem
->maText
), GetTextHeight() ),
1089 if ( !mbInUserDraw
)
1091 aPos
.AdjustX(aTextRect
.Left() );
1092 aPos
.AdjustY(aTextRect
.Top() );
1101 sal_uLong
StatusBar::GetItemWidth( sal_uInt16 nItemId
) const
1103 sal_uInt16 nPos
= GetItemPos( nItemId
);
1105 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1106 return mvItemList
[ nPos
]->mnWidth
;
1111 StatusBarItemBits
StatusBar::GetItemBits( sal_uInt16 nItemId
) const
1113 sal_uInt16 nPos
= GetItemPos( nItemId
);
1115 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1116 return mvItemList
[ nPos
]->mnBits
;
1118 return StatusBarItemBits::NONE
;
1121 long StatusBar::GetItemOffset( sal_uInt16 nItemId
) const
1123 sal_uInt16 nPos
= GetItemPos( nItemId
);
1125 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1126 return mvItemList
[ nPos
]->mnOffset
;
1131 void StatusBar::SetItemText( sal_uInt16 nItemId
, const OUString
& rText
, int nCharsWidth
)
1133 sal_uInt16 nPos
= GetItemPos( nItemId
);
1135 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1137 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1139 if ( pItem
->maText
!= rText
)
1141 pItem
->maText
= rText
;
1143 // adjust item width - see also DataChanged()
1144 long nFudge
= GetTextHeight()/4;
1147 if (nCharsWidth
!= -1)
1149 std::unique_ptr
<SalLayout
> pSalLayout
= ImplLayout("0",0,-1);
1150 const SalLayoutGlyphs
* pGlyphs
= pSalLayout
? pSalLayout
->GetGlyphs() : nullptr;
1151 nWidth
= GetTextWidth("0",0,-1,nullptr,pGlyphs
);
1152 nWidth
= nWidth
* nCharsWidth
+ nFudge
;
1156 std::unique_ptr
<SalLayout
> pSalLayout
= ImplLayout(pItem
->maText
,0,-1);
1157 const SalLayoutGlyphs
* pGlyphs
= pSalLayout
? pSalLayout
->GetGlyphs() : nullptr;
1158 nWidth
= GetTextWidth( pItem
->maText
,0,-1,nullptr,pGlyphs
) + nFudge
;
1159 // Store the calculated layout.
1160 pItem
->mxLayoutCache
= std::move(pSalLayout
);
1163 if( (nWidth
> pItem
->mnWidth
+ STATUSBAR_OFFSET
) ||
1164 ((nWidth
< pItem
->mnWidth
) && (mnDX
- STATUSBAR_OFFSET
) < mnItemsWidth
))
1166 pItem
->mnWidth
= nWidth
+ STATUSBAR_OFFSET
;
1171 // re-draw item if StatusBar is visible and UpdateMode active
1172 if ( pItem
->mbVisible
&& !mbFormat
&& ImplIsItemUpdate() )
1174 tools::Rectangle aRect
= ImplGetItemRectPos(nPos
);
1182 const OUString
& StatusBar::GetItemText( sal_uInt16 nItemId
) const
1184 sal_uInt16 nPos
= GetItemPos( nItemId
);
1186 assert( nPos
!= STATUSBAR_ITEM_NOTFOUND
);
1188 return mvItemList
[ nPos
]->maText
;
1191 void StatusBar::SetItemCommand( sal_uInt16 nItemId
, const OUString
& rCommand
)
1193 sal_uInt16 nPos
= GetItemPos( nItemId
);
1195 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1197 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1199 if ( pItem
->maCommand
!= rCommand
)
1200 pItem
->maCommand
= rCommand
;
1204 OUString
StatusBar::GetItemCommand( sal_uInt16 nItemId
)
1206 sal_uInt16 nPos
= GetItemPos( nItemId
);
1208 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1209 return mvItemList
[ nPos
]->maCommand
;
1214 void StatusBar::SetItemData( sal_uInt16 nItemId
, void* pNewData
)
1216 sal_uInt16 nPos
= GetItemPos( nItemId
);
1218 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1220 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1222 pItem
->mxLayoutCache
.reset();
1223 pItem
->mpUserData
= pNewData
;
1225 // call Draw-Item if it's a User-Item
1226 if ( (pItem
->mnBits
& StatusBarItemBits::UserDraw
) && pItem
->mbVisible
&&
1227 !mbFormat
&& ImplIsItemUpdate() )
1229 tools::Rectangle aRect
= ImplGetItemRectPos(nPos
);
1230 Invalidate(aRect
, InvalidateFlags::NoErase
);
1236 void* StatusBar::GetItemData( sal_uInt16 nItemId
) const
1238 sal_uInt16 nPos
= GetItemPos( nItemId
);
1240 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1241 return mvItemList
[ nPos
]->mpUserData
;
1246 void StatusBar::RedrawItem(sal_uInt16 nItemId
)
1251 sal_uInt16 nPos
= GetItemPos(nItemId
);
1252 if ( nPos
== STATUSBAR_ITEM_NOTFOUND
)
1255 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1256 if ((pItem
->mnBits
& StatusBarItemBits::UserDraw
) &&
1257 pItem
->mbVisible
&& ImplIsItemUpdate())
1259 tools::Rectangle aRect
= ImplGetItemRectPos(nPos
);
1265 void StatusBar::SetHelpText( sal_uInt16 nItemId
, const OUString
& rText
)
1267 sal_uInt16 nPos
= GetItemPos( nItemId
);
1269 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1270 mvItemList
[ nPos
]->maHelpText
= rText
;
1273 const OUString
& StatusBar::GetHelpText( sal_uInt16 nItemId
) const
1275 sal_uInt16 nPos
= GetItemPos( nItemId
);
1277 assert ( nPos
!= STATUSBAR_ITEM_NOTFOUND
);
1279 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1280 if ( pItem
->maHelpText
.isEmpty() && ( !pItem
->maHelpId
.isEmpty() || !pItem
->maCommand
.isEmpty() ))
1282 Help
* pHelp
= Application::GetHelp();
1285 if ( !pItem
->maCommand
.isEmpty() )
1286 pItem
->maHelpText
= pHelp
->GetHelpText( pItem
->maCommand
, this );
1287 if ( pItem
->maHelpText
.isEmpty() && !pItem
->maHelpId
.isEmpty() )
1288 pItem
->maHelpText
= pHelp
->GetHelpText( OStringToOUString( pItem
->maHelpId
, RTL_TEXTENCODING_UTF8
), this );
1292 return pItem
->maHelpText
;
1295 void StatusBar::SetQuickHelpText( sal_uInt16 nItemId
, const OUString
& rText
)
1297 sal_uInt16 nPos
= GetItemPos( nItemId
);
1299 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1300 mvItemList
[ nPos
]->maQuickHelpText
= rText
;
1303 const OUString
& StatusBar::GetQuickHelpText( sal_uInt16 nItemId
) const
1305 sal_uInt16 nPos
= GetItemPos( nItemId
);
1307 assert ( nPos
!= STATUSBAR_ITEM_NOTFOUND
);
1309 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1310 return pItem
->maQuickHelpText
;
1313 void StatusBar::SetHelpId( sal_uInt16 nItemId
, const OString
& rHelpId
)
1315 sal_uInt16 nPos
= GetItemPos( nItemId
);
1317 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1318 mvItemList
[ nPos
]->maHelpId
= rHelpId
;
1321 void StatusBar::StartProgressMode( const OUString
& rText
)
1323 SAL_WARN_IF( mbProgressMode
, "vcl", "StatusBar::StartProgressMode(): progress mode is active" );
1325 mbProgressMode
= true;
1330 ImplCalcProgressRect();
1332 // trigger Paint, which draws text and frame
1333 if ( IsReallyVisible() )
1340 void StatusBar::SetProgressValue( sal_uInt16 nNewPercent
)
1342 SAL_WARN_IF( !mbProgressMode
, "vcl", "StatusBar::SetProgressValue(): no progress mode" );
1343 SAL_WARN_IF( nNewPercent
> 100, "vcl", "StatusBar::SetProgressValue(): nPercent > 100" );
1345 bool bInvalidate
= mbProgressMode
&& IsReallyVisible() && (!mnPercent
|| (mnPercent
!= nNewPercent
));
1347 mnPercent
= nNewPercent
;
1351 // Rate limit how often we paint, otherwise in some loading scenerios we can spend significant
1352 // time just painting progress bars.
1353 sal_uInt32 nTime_ms
= osl_getGlobalTimer();
1354 if ((nTime_ms
- mnLastProgressPaint_ms
) > 100)
1356 Invalidate(maPrgsFrameRect
);
1358 mnLastProgressPaint_ms
= nTime_ms
;
1363 void StatusBar::EndProgressMode()
1365 SAL_WARN_IF( !mbProgressMode
, "vcl", "StatusBar::EndProgressMode(): no progress mode" );
1367 mbProgressMode
= false;
1370 if ( IsReallyVisible() )
1377 void StatusBar::SetText(const OUString
& rText
)
1379 if ((GetStyle() & WB_RIGHT
) && !mbProgressMode
&& IsReallyVisible() && IsUpdateMode())
1384 Window::SetText(rText
);
1389 Window::SetText(rText
);
1393 else if (mbProgressMode
)
1396 if (IsReallyVisible())
1404 Window::SetText(rText
);
1408 Size
StatusBar::CalcWindowSizePixel() const
1411 size_t nCount
= mvItemList
.size();
1413 long nCalcWidth
= STATUSBAR_OFFSET_X
*2;
1416 while ( i
< nCount
)
1418 ImplStatusItem
* pItem
= mvItemList
[ i
].get();
1419 nCalcWidth
+= pItem
->mnWidth
+ nOffset
;
1420 nOffset
= pItem
->mnOffset
;
1424 long nMinHeight
= GetTextHeight();
1425 const long nBarTextOffset
= STATUSBAR_OFFSET_TEXTY
*2;
1426 long nProgressHeight
= nMinHeight
+ nBarTextOffset
;
1428 if( IsNativeControlSupported( ControlType::Progress
, ControlPart::Entire
) )
1430 ImplControlValue aValue
;
1431 tools::Rectangle
aControlRegion( Point(), Size( nCalcWidth
, nMinHeight
) );
1432 tools::Rectangle aNativeControlRegion
, aNativeContentRegion
;
1433 if( GetNativeControlRegion( ControlType::Progress
, ControlPart::Entire
,
1434 aControlRegion
, ControlState::ENABLED
, aValue
,
1435 aNativeControlRegion
, aNativeContentRegion
) )
1437 nProgressHeight
= aNativeControlRegion
.GetHeight();
1441 nCalcHeight
= nMinHeight
+nBarTextOffset
;
1442 if( nCalcHeight
< nProgressHeight
+2 )
1443 nCalcHeight
= nProgressHeight
+2;
1445 return Size( nCalcWidth
, nCalcHeight
);
1448 void StatusBar::SetAccessibleName( sal_uInt16 nItemId
, const OUString
& rName
)
1450 sal_uInt16 nPos
= GetItemPos( nItemId
);
1452 if ( nPos
!= STATUSBAR_ITEM_NOTFOUND
)
1454 ImplStatusItem
* pItem
= mvItemList
[ nPos
].get();
1456 if ( pItem
->maAccessibleName
!= rName
)
1458 pItem
->maAccessibleName
= rName
;
1459 CallEventListeners( VclEventId::StatusbarNameChanged
, reinterpret_cast<void*>(pItem
->mnId
) );
1464 const OUString
& StatusBar::GetAccessibleName( sal_uInt16 nItemId
) const
1466 sal_uInt16 nPos
= GetItemPos( nItemId
);
1468 assert ( nPos
!= STATUSBAR_ITEM_NOTFOUND
);
1470 return mvItemList
[ nPos
]->maAccessibleName
;
1473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */