xref: /core/vcl/source/window/status.cxx (revision 7183b3ba)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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 #include <svdata.hxx>
33 #include <window.h>
34 
35 #define STATUSBAR_OFFSET_X      STATUSBAR_OFFSET
36 #define STATUSBAR_OFFSET_Y      2
37 #define STATUSBAR_OFFSET_TEXTY  3
38 
39 #define STATUSBAR_PRGS_OFFSET   3
40 #define STATUSBAR_PRGS_COUNT    100
41 #define STATUSBAR_PRGS_MIN      5
42 
43 class StatusBar::ImplData
44 {
45 public:
46     ImplData();
47 
48     VclPtr<VirtualDevice> mpVirDev;
49 };
50 
51 StatusBar::ImplData::ImplData()
52 {
53     mpVirDev = nullptr;
54 }
55 
56 struct ImplStatusItem
57 {
58     sal_uInt16                          mnId;
59     StatusBarItemBits                   mnBits;
60     tools::Long                                mnWidth;
61     tools::Long                                mnOffset;
62     tools::Long                                mnExtraWidth;
63     tools::Long                                mnX;
64     OUString                            maText;
65     OUString                            maHelpText;
66     OUString                            maQuickHelpText;
67     OString                             maHelpId;
68     void*                               mpUserData;
69     bool                                mbVisible;
70     OUString                            maAccessibleName;
71     OUString                            maCommand;
72     std::unique_ptr<SalLayout>          mxLayoutCache;
73 };
74 
75 static tools::Long ImplCalcProgressWidth( sal_uInt16 nMax, tools::Long nSize )
76 {
77     return ((nMax*(nSize+(nSize/2)))-(nSize/2)+(STATUSBAR_PRGS_OFFSET*2));
78 }
79 
80 static Point ImplGetItemTextPos( const Size& rRectSize, const Size& rTextSize,
81                                  StatusBarItemBits nStyle )
82 {
83     tools::Long nX;
84     tools::Long nY;
85     tools::Long delta = (rTextSize.Height()/4) + 1;
86     if( delta + rTextSize.Width() > rRectSize.Width() )
87         delta = 0;
88 
89     if ( nStyle & StatusBarItemBits::Left )
90         nX = delta;
91     else if ( nStyle & StatusBarItemBits::Right )
92         nX = rRectSize.Width()-rTextSize.Width()-delta;
93     else // StatusBarItemBits::Center
94         nX = (rRectSize.Width()-rTextSize.Width())/2;
95     nY = (rRectSize.Height()-rTextSize.Height())/2 + 1;
96     return Point( nX, nY );
97 }
98 
99 bool StatusBar::ImplIsItemUpdate() const
100 {
101     return !mbProgressMode && IsReallyVisible() && IsUpdateMode();
102 }
103 
104 void StatusBar::ImplInit( vcl::Window* pParent, WinBits nStyle )
105 {
106     mpImplData.reset(new ImplData);
107 
108     // default: RightAlign
109     if ( !(nStyle & (WB_LEFT | WB_RIGHT)) )
110         nStyle |= WB_RIGHT;
111 
112     Window::ImplInit( pParent, nStyle & ~WB_BORDER, nullptr );
113 
114     // remember WinBits
115     mpImplData->mpVirDev = VclPtr<VirtualDevice>::Create( *GetOutDev() );
116     mnCurItemId     = 0;
117     mbFormat        = true;
118     mbProgressMode  = false;
119     mbInUserDraw    = false;
120     mbAdjustHiDPI   = false;
121     mnItemsWidth    = STATUSBAR_OFFSET_X;
122     mnDX            = 0;
123     mnDY            = 0;
124     mnCalcHeight    = 0;
125     mnTextY         = STATUSBAR_OFFSET_TEXTY;
126 
127     ImplInitSettings();
128 
129     SetOutputSizePixel( CalcWindowSizePixel() );
130 }
131 
132 StatusBar::StatusBar( vcl::Window* pParent, WinBits nStyle ) :
133     Window( WindowType::STATUSBAR ),
134     mnLastProgressPaint_ms(osl_getGlobalTimer())
135 {
136     ImplInit( pParent, nStyle );
137 }
138 
139 StatusBar::~StatusBar()
140 {
141     disposeOnce();
142 }
143 
144 void StatusBar::dispose()
145 {
146     // delete all items
147     mvItemList.clear();
148 
149     // delete VirtualDevice
150     mpImplData->mpVirDev.disposeAndClear();
151     mpImplData.reset();
152     Window::dispose();
153 }
154 
155 void StatusBar::AdjustItemWidthsForHiDPI()
156 {
157     mbAdjustHiDPI = true;
158 }
159 
160 void StatusBar::ApplySettings(vcl::RenderContext& rRenderContext)
161 {
162     rRenderContext.SetLineColor();
163 
164     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
165     ApplyControlFont(rRenderContext, rStyleSettings.GetToolFont());
166 
167     Color aColor;
168     if (IsControlForeground())
169         aColor = GetControlForeground();
170     else if (GetStyle() & WB_3DLOOK)
171         aColor = rStyleSettings.GetButtonTextColor();
172     else
173         aColor = rStyleSettings.GetWindowTextColor();
174     rRenderContext.SetTextColor(aColor);
175 
176     rRenderContext.SetTextFillColor();
177 
178     if (IsControlBackground())
179         aColor = GetControlBackground();
180     else if (GetStyle() & WB_3DLOOK)
181         aColor = rStyleSettings.GetFaceColor();
182     else
183         aColor = rStyleSettings.GetWindowColor();
184     rRenderContext.SetBackground(aColor);
185 
186     // NWF background
187     if (!IsControlBackground() &&
188           rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundWindow))
189     {
190         ImplGetWindowImpl()->mnNativeBackground = ControlPart::BackgroundWindow;
191         EnableChildTransparentMode();
192     }
193 }
194 
195 void StatusBar::ImplInitSettings()
196 {
197     ApplySettings(*GetOutDev());
198 
199     mpImplData->mpVirDev->SetFont(GetFont());
200     mpImplData->mpVirDev->SetTextColor(GetTextColor());
201     mpImplData->mpVirDev->SetTextAlign(GetTextAlign());
202     mpImplData->mpVirDev->SetTextFillColor();
203     mpImplData->mpVirDev->SetBackground(GetBackground());
204 }
205 
206 void StatusBar::ImplFormat()
207 {
208     tools::Long            nExtraWidth;
209     tools::Long            nExtraWidth2;
210     tools::Long            nX;
211     sal_uInt16      nAutoSizeItems;
212     bool            bChanged;
213 
214     do {
215         // sum up widths
216         nAutoSizeItems = 0;
217         mnItemsWidth = STATUSBAR_OFFSET_X;
218         bChanged = false;
219         tools::Long nOffset = 0;
220         for ( const auto & pItem : mvItemList ) {
221             if ( pItem->mbVisible )
222             {
223                 if ( pItem->mnBits & StatusBarItemBits::AutoSize ) {
224                     nAutoSizeItems++;
225                 }
226 
227                 mnItemsWidth += pItem->mnWidth + nOffset;
228                 nOffset = pItem->mnOffset;
229             }
230         }
231 
232         if ( mnDX > 0 && mnDX < mnItemsWidth )
233         {
234             // Total width of items is more than available width
235             // Try to hide secondary elements, if any
236             for ( auto & pItem : mvItemList )
237             {
238                 if ( pItem->mbVisible && !(pItem->mnBits & StatusBarItemBits::Mandatory) )
239                 {
240                     pItem->mbVisible = false;
241                     bChanged = true;
242                     break;
243                 }
244             }
245         }
246         else if ( mnDX > mnItemsWidth )
247         {
248             // Width of statusbar is sufficient.
249             // Try to restore hidden items, if any
250             for ( auto & pItem : mvItemList )
251             {
252                 if ( !pItem->mbVisible &&
253                     !(pItem->mnBits & StatusBarItemBits::Mandatory) &&
254                     pItem->mnWidth + nOffset + mnItemsWidth < mnDX )
255                 {
256                     pItem->mbVisible = true;
257                     bChanged = true;
258                     break;
259                 }
260             }
261         }
262     } while ( bChanged );
263 
264     if ( GetStyle() & WB_RIGHT )
265     {
266         // AutoSize isn't computed for right-alignment,
267         // because we show the text that is declared by SetText on the left side
268         nX              = mnDX - mnItemsWidth;
269         nExtraWidth     = 0;
270         nExtraWidth2    = 0;
271     }
272     else
273     {
274         mnItemsWidth += STATUSBAR_OFFSET_X;
275 
276         // calling AutoSize is potentially necessary for left-aligned text,
277         if ( nAutoSizeItems && (mnDX > (mnItemsWidth - STATUSBAR_OFFSET)) )
278         {
279             nExtraWidth  = (mnDX - mnItemsWidth - 1) / nAutoSizeItems;
280             nExtraWidth2 = (mnDX - mnItemsWidth - 1) % nAutoSizeItems;
281         }
282         else
283         {
284             nExtraWidth  = 0;
285             nExtraWidth2 = 0;
286         }
287         nX = STATUSBAR_OFFSET_X;
288 
289         if( GetOutDev()->HasMirroredGraphics() && IsRTLEnabled() )
290             nX += ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
291     }
292 
293     for (auto & pItem : mvItemList) {
294         if ( pItem->mbVisible ) {
295             if ( pItem->mnBits & StatusBarItemBits::AutoSize ) {
296                 pItem->mnExtraWidth = nExtraWidth;
297                 if ( nExtraWidth2 ) {
298                     pItem->mnExtraWidth++;
299                     nExtraWidth2--;
300                 }
301             } else {
302                 pItem->mnExtraWidth = 0;
303             }
304 
305             pItem->mnX = nX;
306             nX += pItem->mnWidth + pItem->mnExtraWidth + pItem->mnOffset;
307         }
308     }
309 
310     mbFormat = false;
311 }
312 
313 tools::Rectangle StatusBar::ImplGetItemRectPos( sal_uInt16 nPos ) const
314 {
315     tools::Rectangle       aRect;
316     ImplStatusItem* pItem = ( nPos < mvItemList.size() ) ? mvItemList[ nPos ].get() : nullptr;
317     if ( pItem && pItem->mbVisible )
318     {
319         aRect.SetLeft( pItem->mnX );
320         aRect.SetRight( aRect.Left() + pItem->mnWidth + pItem->mnExtraWidth );
321         aRect.SetTop( STATUSBAR_OFFSET_Y );
322         aRect.SetBottom( mnCalcHeight - STATUSBAR_OFFSET_Y );
323     }
324 
325     return aRect;
326 }
327 
328 sal_uInt16 StatusBar::ImplGetFirstVisiblePos() const
329 {
330     for( size_t nPos = 0; nPos < mvItemList.size(); nPos++ )
331     {
332         ImplStatusItem* pItem = mvItemList[ nPos ].get();
333         if ( pItem->mbVisible )
334             return sal_uInt16(nPos);
335     }
336 
337     return SAL_MAX_UINT16;
338 }
339 
340 void StatusBar::ImplDrawText(vcl::RenderContext& rRenderContext)
341 {
342     // prevent item box from being overwritten
343     tools::Rectangle aTextRect;
344     aTextRect.SetLeft( STATUSBAR_OFFSET_X + 1 );
345     aTextRect.SetTop( mnTextY );
346     if (GetStyle() & WB_RIGHT)
347         aTextRect.SetRight( mnDX - mnItemsWidth - 1 );
348     else
349         aTextRect.SetRight( mnDX - 1 );
350     if (aTextRect.Right() > aTextRect.Left())
351     {
352         // compute position
353         OUString aStr = GetText();
354         sal_Int32 nPos = aStr.indexOf('\n');
355         if (nPos != -1)
356             aStr = aStr.copy(0, nPos);
357 
358         aTextRect.SetBottom( aTextRect.Top()+GetTextHeight()+1 );
359 
360         rRenderContext.DrawText(aTextRect, aStr, DrawTextFlags::Left | DrawTextFlags::Top | DrawTextFlags::Clip | DrawTextFlags::EndEllipsis);
361     }
362 }
363 
364 void StatusBar::ImplDrawItem(vcl::RenderContext& rRenderContext, bool bOffScreen, sal_uInt16 nPos)
365 {
366     tools::Rectangle aRect = ImplGetItemRectPos(nPos);
367 
368     if (aRect.IsEmpty())
369         return;
370 
371     // compute output region
372     ImplStatusItem* pItem = mvItemList[nPos].get();
373     tools::Long nW = 1;
374     tools::Rectangle aTextRect(aRect.Left() + nW, aRect.Top() + nW,
375                         aRect.Right() - nW, aRect.Bottom() - nW);
376 
377     Size aTextRectSize(aTextRect.GetSize());
378 
379     if (bOffScreen)
380     {
381         mpImplData->mpVirDev->SetOutputSizePixel(aTextRectSize);
382     }
383     else
384     {
385         vcl::Region aRegion(aTextRect);
386         rRenderContext.SetClipRegion(aRegion);
387     }
388 
389     // if the framework code is drawing status, let it do all the work
390     if (!(pItem->mnBits & StatusBarItemBits::UserDraw))
391     {
392         SalLayout* pLayoutCache = pItem->mxLayoutCache.get();
393 
394         if(!pLayoutCache)
395         {
396             // update cache
397             pItem->mxLayoutCache = rRenderContext.ImplLayout(pItem->maText, 0, -1);
398             pLayoutCache = pItem->mxLayoutCache.get();
399         }
400 
401         const SalLayoutGlyphs glyphs = pLayoutCache ? pLayoutCache->GetGlyphs() : SalLayoutGlyphs();
402         const SalLayoutGlyphs* pGlyphs = pLayoutCache ? &glyphs : nullptr;
403         Size aTextSize(rRenderContext.GetTextWidth(pItem->maText,0,-1,nullptr,pGlyphs), rRenderContext.GetTextHeight());
404         Point aTextPos = ImplGetItemTextPos(aTextRectSize, aTextSize, pItem->mnBits);
405 
406         if (bOffScreen)
407         {
408             mpImplData->mpVirDev->DrawText(
409                         aTextPos,
410                         pItem->maText,
411                         0, -1, nullptr, nullptr,
412                         pGlyphs );
413         }
414         else
415         {
416             aTextPos.AdjustX(aTextRect.Left() );
417             aTextPos.AdjustY(aTextRect.Top() );
418             rRenderContext.DrawText(
419                         aTextPos,
420                         pItem->maText,
421                         0, -1, nullptr, nullptr,
422                         pGlyphs );
423         }
424     }
425 
426     // call DrawItem if necessary
427     if (pItem->mnBits & StatusBarItemBits::UserDraw)
428     {
429         if (bOffScreen)
430         {
431             mbInUserDraw = true;
432             mpImplData->mpVirDev->EnableRTL( IsRTLEnabled() );
433             UserDrawEvent aODEvt(mpImplData->mpVirDev, tools::Rectangle(Point(), aTextRectSize), pItem->mnId);
434             UserDraw(aODEvt);
435             mpImplData->mpVirDev->EnableRTL(false);
436             mbInUserDraw = false;
437         }
438         else
439         {
440             UserDrawEvent aODEvt(&rRenderContext, aTextRect, pItem->mnId);
441             UserDraw(aODEvt);
442         }
443     }
444 
445     if (bOffScreen)
446         rRenderContext.DrawOutDev(aTextRect.TopLeft(), aTextRectSize, Point(), aTextRectSize, *mpImplData->mpVirDev);
447     else
448         rRenderContext.SetClipRegion();
449 
450     if (nPos != ImplGetFirstVisiblePos())
451     {
452         // draw separator
453         Point aFrom(aRect.TopLeft());
454         aFrom.AdjustX( -4 );
455         aFrom.AdjustY( 1 );
456         Point aTo(aRect.BottomLeft());
457         aTo.AdjustX( -4 );
458         aTo.AdjustY( -1 );
459 
460         DecorationView aDecoView(&rRenderContext);
461         aDecoView.DrawSeparator(aFrom, aTo);
462     }
463 
464     if (!rRenderContext.ImplIsRecordLayout())
465         CallEventListeners(VclEventId::StatusbarDrawItem, reinterpret_cast<void*>(pItem->mnId));
466 }
467 
468 void DrawProgress(vcl::Window* pWindow, vcl::RenderContext& rRenderContext, const Point& rPos,
469                   tools::Long nOffset, tools::Long nPrgsWidth, tools::Long nPrgsHeight,
470                   sal_uInt16 nPercent1, sal_uInt16 nPercent2, sal_uInt16 nPercentCount,
471                   const tools::Rectangle& rFramePosSize)
472 {
473     if (rRenderContext.IsNativeControlSupported(ControlType::Progress, ControlPart::Entire))
474     {
475         bool bNeedErase = ImplGetSVData()->maNWFData.mbProgressNeedsErase;
476 
477         tools::Long nFullWidth = (nPrgsWidth + nOffset) * (10000 / nPercentCount);
478         tools::Long nPerc = std::min<sal_uInt16>(nPercent2, 10000);
479         ImplControlValue aValue(nFullWidth * nPerc / 10000);
480         tools::Rectangle aDrawRect(rPos, Size(nFullWidth, nPrgsHeight));
481         tools::Rectangle aControlRegion(aDrawRect);
482 
483         if(bNeedErase)
484         {
485             vcl::Window* pEraseWindow = pWindow;
486             while (pEraseWindow->IsPaintTransparent() && !pEraseWindow->ImplGetWindowImpl()->mbFrame)
487             {
488                 pEraseWindow = pEraseWindow->ImplGetWindowImpl()->mpParent;
489             }
490 
491             if (pEraseWindow == pWindow)
492             {
493                 // restore background of pWindow
494                 rRenderContext.Erase(rFramePosSize);
495             }
496             else
497             {
498                 // restore transparent background
499                 Point aTL(pWindow->OutputToAbsoluteScreenPixel(rFramePosSize.TopLeft()));
500                 aTL = pEraseWindow->AbsoluteScreenToOutputPixel(aTL);
501                 tools::Rectangle aRect(aTL, rFramePosSize.GetSize());
502                 pEraseWindow->Invalidate(aRect, InvalidateFlags::NoChildren     |
503                                                 InvalidateFlags::NoClipChildren |
504                                                 InvalidateFlags::Transparent);
505                 pEraseWindow->PaintImmediately();
506             }
507             rRenderContext.Push(vcl::PushFlags::CLIPREGION);
508             rRenderContext.IntersectClipRegion(rFramePosSize);
509         }
510 
511         bool bNativeOK = rRenderContext.DrawNativeControl(ControlType::Progress, ControlPart::Entire, aControlRegion,
512                                                           ControlState::ENABLED, aValue, OUString());
513         if (bNeedErase)
514             rRenderContext.Pop();
515         if (bNativeOK)
516             return;
517     }
518 
519     // precompute values
520     sal_uInt16 nPerc1 = nPercent1 / nPercentCount;
521     sal_uInt16 nPerc2 = nPercent2 / nPercentCount;
522 
523     if (nPerc1 > nPerc2)
524     {
525         // support progress that can also decrease
526 
527         // compute rectangle
528         tools::Long nDX = nPrgsWidth + nOffset;
529         tools::Long nLeft = rPos.X() + ((nPerc1 - 1) * nDX);
530         tools::Rectangle aRect(nLeft, rPos.Y(), nLeft + nPrgsWidth, rPos.Y() + nPrgsHeight);
531 
532         do
533         {
534             rRenderContext.Erase(aRect);
535             aRect.AdjustLeft( -nDX );
536             aRect.AdjustRight( -nDX );
537             nPerc1--;
538         }
539         while (nPerc1 > nPerc2);
540     }
541     else if (nPerc1 < nPerc2)
542     {
543         // draw Percent rectangle
544         // if Percent2 greater than 100%, adapt values
545         if (nPercent2 > 10000)
546         {
547             nPerc2 = 10000 / nPercentCount;
548             if (nPerc1 >= nPerc2)
549                 nPerc1 = nPerc2 - 1;
550         }
551 
552         // compute rectangle
553         tools::Long nDX = nPrgsWidth + nOffset;
554         tools::Long nLeft = rPos.X() + (nPerc1 * nDX);
555         tools::Rectangle aRect(nLeft, rPos.Y(), nLeft + nPrgsWidth, rPos.Y() + nPrgsHeight);
556 
557         do
558         {
559             rRenderContext.DrawRect(aRect);
560             aRect.AdjustLeft(nDX );
561             aRect.AdjustRight(nDX );
562             nPerc1++;
563         }
564         while (nPerc1 < nPerc2);
565 
566         // if greater than 100%, set rectangle to blink
567         if (nPercent2 > 10000)
568         {
569             // define on/off status
570             if (((nPercent2 / nPercentCount) & 0x01) == (nPercentCount & 0x01))
571             {
572                 aRect.AdjustLeft( -nDX );
573                 aRect.AdjustRight( -nDX );
574                 rRenderContext.Erase(aRect);
575             }
576         }
577     }
578 }
579 
580 void StatusBar::ImplDrawProgress(vcl::RenderContext& rRenderContext, sal_uInt16 nPercent2)
581 {
582     bool bNative = rRenderContext.IsNativeControlSupported(ControlType::Progress, ControlPart::Entire);
583     // bPaint: draw text also, else only update progress
584     rRenderContext.DrawText(maPrgsTxtPos, maPrgsTxt);
585     if (!bNative)
586     {
587         DecorationView aDecoView(&rRenderContext);
588         aDecoView.DrawFrame(maPrgsFrameRect, DrawFrameStyle::In);
589     }
590 
591     Point aPos(maPrgsFrameRect.Left() + STATUSBAR_PRGS_OFFSET,
592                maPrgsFrameRect.Top()  + STATUSBAR_PRGS_OFFSET);
593     tools::Long nPrgsHeight = mnPrgsSize;
594     if (bNative)
595     {
596         aPos = maPrgsFrameRect.TopLeft();
597         nPrgsHeight = maPrgsFrameRect.GetHeight();
598     }
599     DrawProgress(this, rRenderContext, aPos, mnPrgsSize / 2, mnPrgsSize, nPrgsHeight,
600                  0, nPercent2 * 100, mnPercentCount, maPrgsFrameRect);
601 }
602 
603 void StatusBar::ImplCalcProgressRect()
604 {
605     // calculate text size
606     Size aPrgsTxtSize( GetTextWidth( maPrgsTxt ), GetTextHeight() );
607     maPrgsTxtPos.setX( STATUSBAR_OFFSET_X+1 );
608 
609     // calculate progress frame
610     maPrgsFrameRect.SetLeft( maPrgsTxtPos.X()+aPrgsTxtSize.Width()+STATUSBAR_OFFSET );
611     maPrgsFrameRect.SetTop( STATUSBAR_OFFSET_Y );
612     maPrgsFrameRect.SetBottom( mnCalcHeight - STATUSBAR_OFFSET_Y );
613 
614     // calculate size of progress rects
615     mnPrgsSize = maPrgsFrameRect.Bottom()-maPrgsFrameRect.Top()-(STATUSBAR_PRGS_OFFSET*2);
616     sal_uInt16 nMaxPercent = STATUSBAR_PRGS_COUNT;
617 
618     tools::Long nMaxWidth = mnDX-STATUSBAR_OFFSET-1;
619 
620     // make smaller if there are too many rects
621     while ( maPrgsFrameRect.Left()+ImplCalcProgressWidth( nMaxPercent, mnPrgsSize ) > nMaxWidth )
622     {
623         nMaxPercent--;
624         if ( nMaxPercent <= STATUSBAR_PRGS_MIN )
625             break;
626     }
627     maPrgsFrameRect.SetRight( maPrgsFrameRect.Left() + ImplCalcProgressWidth( nMaxPercent, mnPrgsSize ) );
628 
629     // save the divisor for later
630     mnPercentCount = 10000 / nMaxPercent;
631     bool bNativeOK = false;
632     if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
633     {
634         ImplControlValue aValue;
635         tools::Rectangle aControlRegion( tools::Rectangle( Point(), maPrgsFrameRect.GetSize() ) );
636         tools::Rectangle aNativeControlRegion, aNativeContentRegion;
637         if( (bNativeOK = GetNativeControlRegion( ControlType::Progress, ControlPart::Entire, aControlRegion,
638                                                  ControlState::ENABLED, aValue,
639                                                  aNativeControlRegion, aNativeContentRegion ) ) )
640         {
641             tools::Long nProgressHeight = aNativeControlRegion.GetHeight();
642             if( nProgressHeight > maPrgsFrameRect.GetHeight() )
643             {
644                 tools::Long nDelta = nProgressHeight - maPrgsFrameRect.GetHeight();
645                 maPrgsFrameRect.AdjustTop( -(nDelta - nDelta/2) );
646                 maPrgsFrameRect.AdjustBottom(nDelta/2 );
647             }
648             maPrgsTxtPos.setY( maPrgsFrameRect.Top() + (nProgressHeight - GetTextHeight())/2 );
649         }
650     }
651     if( ! bNativeOK )
652         maPrgsTxtPos.setY( mnTextY );
653 }
654 
655 void StatusBar::MouseButtonDown( const MouseEvent& rMEvt )
656 {
657     // trigger toolbox only for left mouse button
658     if ( !rMEvt.IsLeft() )
659         return;
660 
661     Point  aMousePos = rMEvt.GetPosPixel();
662 
663     // search for clicked item
664     for ( size_t i = 0; i < mvItemList.size(); ++i )
665     {
666         ImplStatusItem* pItem = mvItemList[ i ].get();
667         // check item for being clicked
668         if ( ImplGetItemRectPos( sal_uInt16(i) ).Contains( aMousePos ) )
669         {
670             mnCurItemId = pItem->mnId;
671             if ( rMEvt.GetClicks() == 2 )
672                 DoubleClick();
673             else
674                 Click();
675             mnCurItemId = 0;
676 
677             // Item found
678             return;
679         }
680     }
681 
682     // if there's no item, trigger Click or DoubleClick
683     if ( rMEvt.GetClicks() == 2 )
684         DoubleClick();
685     else
686         Click();
687 }
688 
689 void StatusBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
690 {
691     if (mbFormat)
692         ImplFormat();
693 
694     sal_uInt16 nItemCount = sal_uInt16( mvItemList.size() );
695 
696     if (mbProgressMode)
697     {
698         rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
699 
700         const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
701         Color aProgressColor = rStyleSettings.GetHighlightColor();
702         if (aProgressColor == rStyleSettings.GetFaceColor())
703             aProgressColor = rStyleSettings.GetDarkShadowColor();
704         rRenderContext.SetLineColor();
705         rRenderContext.SetFillColor(aProgressColor);
706 
707         ImplDrawProgress(rRenderContext, mnPercent);
708 
709         rRenderContext.Pop();
710     }
711     else
712     {
713         // draw text
714         if (GetStyle() & WB_RIGHT)
715             ImplDrawText(rRenderContext);
716 
717         // draw items
718 
719         // Do offscreen only when we are not recording layout...
720         bool bOffscreen = !rRenderContext.ImplIsRecordLayout();
721 
722         if (!bOffscreen)
723             rRenderContext.Erase(rRect);
724 
725         for (sal_uInt16 i = 0; i < nItemCount; i++)
726             ImplDrawItem(rRenderContext, bOffscreen, i);
727     }
728 
729     // draw line at the top of the status bar (to visually distinguish it from
730     // shell / docking area)
731     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
732     rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
733     rRenderContext.DrawLine(Point(0, 0), Point(mnDX-1, 0));
734 }
735 
736 void StatusBar::Resize()
737 {
738     // save width and height
739     Size aSize = GetOutputSizePixel();
740     mnDX = aSize.Width() - ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
741     mnDY = aSize.Height();
742     mnCalcHeight = mnDY;
743 
744     mnTextY = (mnCalcHeight-GetTextHeight())/2;
745 
746     // provoke re-formatting
747     mbFormat = true;
748 
749     if ( mbProgressMode )
750         ImplCalcProgressRect();
751 
752     Invalidate();
753 }
754 
755 void StatusBar::RequestHelp( const HelpEvent& rHEvt )
756 {
757     // no keyboard help in status bar
758     if( rHEvt.KeyboardActivated() )
759         return;
760 
761     sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
762 
763     if ( nItemId )
764     {
765         tools::Rectangle aItemRect = GetItemRect( nItemId );
766         Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
767         aItemRect.SetLeft( aPt.X() );
768         aItemRect.SetTop( aPt.Y() );
769         aPt = OutputToScreenPixel( aItemRect.BottomRight() );
770         aItemRect.SetRight( aPt.X() );
771         aItemRect.SetBottom( aPt.Y() );
772 
773         if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
774         {
775             OUString aStr = GetHelpText( nItemId );
776             Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
777             return;
778         }
779         else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
780         {
781             OUString aStr(GetQuickHelpText(nItemId));
782             // show quickhelp if available
783             if (!aStr.isEmpty())
784             {
785                 Help::ShowQuickHelp( this, aItemRect, aStr );
786                 return;
787             }
788             aStr = GetItemText( nItemId );
789             // show a quick help if item text doesn't fit
790             if ( GetTextWidth( aStr ) > aItemRect.GetWidth() )
791             {
792                 Help::ShowQuickHelp( this, aItemRect, aStr );
793                 return;
794             }
795         }
796     }
797 
798     Window::RequestHelp( rHEvt );
799 }
800 
801 void StatusBar::StateChanged( StateChangedType nType )
802 {
803     Window::StateChanged( nType );
804 
805     if ( nType == StateChangedType::InitShow )
806         ImplFormat();
807     else if ( nType == StateChangedType::UpdateMode )
808         Invalidate();
809     else if ( (nType == StateChangedType::Zoom) ||
810               (nType == StateChangedType::ControlFont) )
811     {
812         mbFormat = true;
813         ImplInitSettings();
814         Invalidate();
815     }
816     else if ( nType == StateChangedType::ControlForeground )
817     {
818         ImplInitSettings();
819         Invalidate();
820     }
821     else if ( nType == StateChangedType::ControlBackground )
822     {
823         ImplInitSettings();
824         Invalidate();
825     }
826 
827     //invalidate layout cache
828     for (auto & pItem : mvItemList)
829     {
830         pItem->mxLayoutCache.reset();
831     }
832 
833 }
834 
835 void StatusBar::DataChanged( const DataChangedEvent& rDCEvt )
836 {
837     Window::DataChanged( rDCEvt );
838 
839     if (  !((rDCEvt.GetType() == DataChangedEventType::DISPLAY         )
840        || (rDCEvt.GetType() == DataChangedEventType::FONTS           )
841        || (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION)
842        || (  (rDCEvt.GetType() == DataChangedEventType::SETTINGS)
843           && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
844           ))
845        )
846         return;
847 
848     mbFormat = true;
849     ImplInitSettings();
850     tools::Long nFudge = GetTextHeight() / 4;
851     for (auto & pItem : mvItemList)
852     {
853         tools::Long nWidth = GetTextWidth( pItem->maText ) + nFudge;
854         if( nWidth > pItem->mnWidth + STATUSBAR_OFFSET )
855             pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
856 
857         pItem->mxLayoutCache.reset();
858     }
859     Size aSize = GetSizePixel();
860     // do not disturb current width, since
861     // CalcWindowSizePixel calculates a minimum width
862     aSize.setHeight( CalcWindowSizePixel().Height() );
863     SetSizePixel( aSize );
864     Invalidate();
865 }
866 
867 void StatusBar::Click()
868 {
869     maClickHdl.Call( this );
870 }
871 
872 void StatusBar::DoubleClick()
873 {
874     maDoubleClickHdl.Call( this );
875 }
876 
877 void StatusBar::UserDraw( const UserDrawEvent& )
878 {
879 }
880 
881 void StatusBar::InsertItem( sal_uInt16 nItemId, sal_uLong nWidth,
882                             StatusBarItemBits nBits,
883                             tools::Long nOffset, sal_uInt16 nPos )
884 {
885     SAL_WARN_IF( !nItemId, "vcl", "StatusBar::InsertItem(): ItemId == 0" );
886     SAL_WARN_IF( GetItemPos( nItemId ) != STATUSBAR_ITEM_NOTFOUND, "vcl",
887                 "StatusBar::InsertItem(): ItemId already exists" );
888 
889     // default: IN and CENTER
890     if ( !(nBits & (StatusBarItemBits::In | StatusBarItemBits::Out | StatusBarItemBits::Flat)) )
891         nBits |= StatusBarItemBits::In;
892     if ( !(nBits & (StatusBarItemBits::Left | StatusBarItemBits::Right | StatusBarItemBits::Center)) )
893         nBits |= StatusBarItemBits::Center;
894 
895     // create item
896     if (mbAdjustHiDPI)
897     {
898         nWidth *= GetDPIScaleFactor();
899     }
900     tools::Long nFudge = GetTextHeight()/4;
901     std::unique_ptr<ImplStatusItem> pItem(new ImplStatusItem);
902     pItem->mnId             = nItemId;
903     pItem->mnBits           = nBits;
904     pItem->mnWidth          = static_cast<tools::Long>(nWidth)+nFudge+STATUSBAR_OFFSET;
905     pItem->mnOffset         = nOffset;
906     pItem->mpUserData       = nullptr;
907     pItem->mbVisible        = true;
908 
909     // add item to list
910     if ( nPos < mvItemList.size() ) {
911         mvItemList.insert( mvItemList.begin() + nPos, std::move(pItem) );
912     } else {
913         mvItemList.push_back( std::move(pItem) );
914     }
915 
916     mbFormat = true;
917     if ( ImplIsItemUpdate() )
918         Invalidate();
919 
920     CallEventListeners( VclEventId::StatusbarItemAdded, reinterpret_cast<void*>(nItemId) );
921 }
922 
923 void StatusBar::RemoveItem( sal_uInt16 nItemId )
924 {
925     sal_uInt16 nPos = GetItemPos( nItemId );
926     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
927     {
928         mvItemList.erase( mvItemList.begin() + nPos );
929 
930         mbFormat = true;
931         if ( ImplIsItemUpdate() )
932             Invalidate();
933 
934         CallEventListeners( VclEventId::StatusbarItemRemoved, reinterpret_cast<void*>(nItemId) );
935     }
936 }
937 
938 void StatusBar::ShowItem( sal_uInt16 nItemId )
939 {
940     sal_uInt16 nPos = GetItemPos( nItemId );
941 
942     if ( nPos == STATUSBAR_ITEM_NOTFOUND )
943         return;
944 
945     ImplStatusItem* pItem = mvItemList[ nPos ].get();
946     if ( !pItem->mbVisible )
947     {
948         pItem->mbVisible = true;
949 
950         mbFormat = true;
951         if ( ImplIsItemUpdate() )
952             Invalidate();
953 
954         CallEventListeners( VclEventId::StatusbarShowItem, reinterpret_cast<void*>(nItemId) );
955     }
956 }
957 
958 void StatusBar::HideItem( sal_uInt16 nItemId )
959 {
960     sal_uInt16 nPos = GetItemPos( nItemId );
961 
962     if ( nPos == STATUSBAR_ITEM_NOTFOUND )
963         return;
964 
965     ImplStatusItem* pItem = mvItemList[ nPos ].get();
966     if ( pItem->mbVisible )
967     {
968         pItem->mbVisible = false;
969 
970         mbFormat = true;
971         if ( ImplIsItemUpdate() )
972             Invalidate();
973 
974         CallEventListeners( VclEventId::StatusbarHideItem, reinterpret_cast<void*>(nItemId) );
975     }
976 }
977 
978 bool StatusBar::IsItemVisible( sal_uInt16 nItemId ) const
979 {
980     sal_uInt16 nPos = GetItemPos( nItemId );
981 
982     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
983         return mvItemList[ nPos ]->mbVisible;
984     else
985         return false;
986 }
987 
988 void StatusBar::Clear()
989 {
990     // delete all items
991     mvItemList.clear();
992 
993     mbFormat = true;
994     if ( ImplIsItemUpdate() )
995         Invalidate();
996 
997     CallEventListeners( VclEventId::StatusbarAllItemsRemoved );
998 }
999 
1000 sal_uInt16 StatusBar::GetItemCount() const
1001 {
1002     return static_cast<sal_uInt16>(mvItemList.size());
1003 }
1004 
1005 sal_uInt16 StatusBar::GetItemId( sal_uInt16 nPos ) const
1006 {
1007     if ( nPos < mvItemList.size() )
1008         return mvItemList[ nPos ]->mnId;
1009     return 0;
1010 }
1011 
1012 sal_uInt16 StatusBar::GetItemPos( sal_uInt16 nItemId ) const
1013 {
1014     for ( size_t i = 0, n = mvItemList.size(); i < n; ++i ) {
1015         if ( mvItemList[ i ]->mnId == nItemId ) {
1016             return sal_uInt16( i );
1017         }
1018     }
1019 
1020     return STATUSBAR_ITEM_NOTFOUND;
1021 }
1022 
1023 sal_uInt16 StatusBar::GetItemId( const Point& rPos ) const
1024 {
1025     if ( !mbFormat )
1026     {
1027         sal_uInt16 nItemCount = GetItemCount();
1028         sal_uInt16 nPos;
1029         for ( nPos = 0; nPos < nItemCount; nPos++ )
1030         {
1031             // get rectangle
1032             tools::Rectangle aRect = ImplGetItemRectPos( nPos );
1033             if ( aRect.Contains( rPos ) )
1034                 return mvItemList[ nPos ]->mnId;
1035         }
1036     }
1037 
1038     return 0;
1039 }
1040 
1041 tools::Rectangle StatusBar::GetItemRect( sal_uInt16 nItemId ) const
1042 {
1043     tools::Rectangle aRect;
1044 
1045     if ( !mbFormat )
1046     {
1047         sal_uInt16 nPos = GetItemPos( nItemId );
1048         if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1049         {
1050             // get rectangle and subtract frame
1051             aRect = ImplGetItemRectPos( nPos );
1052             tools::Long nW = 1;
1053             aRect.AdjustTop(nW-1 );
1054             aRect.AdjustBottom( -(nW-1) );
1055             aRect.AdjustLeft(nW );
1056             aRect.AdjustRight( -nW );
1057             return aRect;
1058         }
1059     }
1060 
1061     return aRect;
1062 }
1063 
1064 Point StatusBar::GetItemTextPos( sal_uInt16 nItemId ) const
1065 {
1066     if ( !mbFormat )
1067     {
1068         sal_uInt16 nPos = GetItemPos( nItemId );
1069         if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1070         {
1071             // get rectangle
1072             ImplStatusItem* pItem = mvItemList[ nPos ].get();
1073             tools::Rectangle aRect = ImplGetItemRectPos( nPos );
1074             tools::Long nW = 1;
1075             tools::Rectangle           aTextRect( aRect.Left()+nW, aRect.Top()+nW,
1076                                            aRect.Right()-nW, aRect.Bottom()-nW );
1077             Point aPos = ImplGetItemTextPos( aTextRect.GetSize(),
1078                                              Size( GetTextWidth( pItem->maText ), GetTextHeight() ),
1079                                              pItem->mnBits );
1080             if ( !mbInUserDraw )
1081             {
1082                 aPos.AdjustX(aTextRect.Left() );
1083                 aPos.AdjustY(aTextRect.Top() );
1084             }
1085             return aPos;
1086         }
1087     }
1088 
1089     return Point();
1090 }
1091 
1092 sal_uLong StatusBar::GetItemWidth( sal_uInt16 nItemId ) const
1093 {
1094     sal_uInt16 nPos = GetItemPos( nItemId );
1095 
1096     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1097         return mvItemList[ nPos ]->mnWidth;
1098 
1099     return 0;
1100 }
1101 
1102 StatusBarItemBits StatusBar::GetItemBits( sal_uInt16 nItemId ) const
1103 {
1104     sal_uInt16 nPos = GetItemPos( nItemId );
1105 
1106     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1107         return mvItemList[ nPos ]->mnBits;
1108 
1109     return StatusBarItemBits::NONE;
1110 }
1111 
1112 tools::Long StatusBar::GetItemOffset( sal_uInt16 nItemId ) const
1113 {
1114     sal_uInt16 nPos = GetItemPos( nItemId );
1115 
1116     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1117         return mvItemList[ nPos ]->mnOffset;
1118 
1119     return 0;
1120 }
1121 
1122 void StatusBar::SetItemText( sal_uInt16 nItemId, const OUString& rText, int nCharsWidth )
1123 {
1124     sal_uInt16 nPos = GetItemPos( nItemId );
1125 
1126     if ( nPos == STATUSBAR_ITEM_NOTFOUND )
1127         return;
1128 
1129     ImplStatusItem* pItem = mvItemList[ nPos ].get();
1130 
1131     if ( pItem->maText == rText )
1132         return;
1133 
1134     pItem->maText = rText;
1135 
1136     // adjust item width - see also DataChanged()
1137     tools::Long nFudge = GetTextHeight()/4;
1138 
1139     tools::Long nWidth;
1140     if (nCharsWidth != -1)
1141     {
1142         std::unique_ptr<SalLayout> pSalLayout = GetOutDev()->ImplLayout("0",0,-1);
1143         const SalLayoutGlyphs glyphs = pSalLayout ? pSalLayout->GetGlyphs() : SalLayoutGlyphs();
1144         nWidth = GetTextWidth("0",0,-1,nullptr,pSalLayout ? &glyphs : nullptr);
1145         nWidth = nWidth * nCharsWidth + nFudge;
1146     }
1147     else
1148     {
1149         std::unique_ptr<SalLayout> pSalLayout = GetOutDev()->ImplLayout(pItem->maText,0,-1);
1150         const SalLayoutGlyphs glyphs = pSalLayout ? pSalLayout->GetGlyphs() : SalLayoutGlyphs();
1151         nWidth = GetTextWidth( pItem->maText,0,-1,nullptr,pSalLayout ? &glyphs : nullptr) + nFudge;
1152         // Store the calculated layout.
1153         pItem->mxLayoutCache = std::move(pSalLayout);
1154     }
1155 
1156     if( (nWidth > pItem->mnWidth + STATUSBAR_OFFSET) ||
1157         ((nWidth < pItem->mnWidth) && (mnDX - STATUSBAR_OFFSET) < mnItemsWidth  ))
1158     {
1159         pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
1160         ImplFormat();
1161         Invalidate();
1162     }
1163 
1164     // re-draw item if StatusBar is visible and UpdateMode active
1165     if ( pItem->mbVisible && !mbFormat && ImplIsItemUpdate() )
1166     {
1167         tools::Rectangle aRect = ImplGetItemRectPos(nPos);
1168         Invalidate(aRect);
1169         PaintImmediately();
1170     }
1171 }
1172 
1173 const OUString& StatusBar::GetItemText( sal_uInt16 nItemId ) const
1174 {
1175     sal_uInt16 nPos = GetItemPos( nItemId );
1176 
1177     assert( nPos != STATUSBAR_ITEM_NOTFOUND );
1178 
1179     return mvItemList[ nPos ]->maText;
1180 }
1181 
1182 void StatusBar::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1183 {
1184     sal_uInt16 nPos = GetItemPos( nItemId );
1185 
1186     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1187     {
1188         ImplStatusItem* pItem = mvItemList[ nPos ].get();
1189 
1190         if ( pItem->maCommand != rCommand )
1191             pItem->maCommand = rCommand;
1192     }
1193 }
1194 
1195 OUString StatusBar::GetItemCommand( sal_uInt16 nItemId )
1196 {
1197     sal_uInt16 nPos = GetItemPos( nItemId );
1198 
1199     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1200         return mvItemList[ nPos ]->maCommand;
1201 
1202     return OUString();
1203 }
1204 
1205 void StatusBar::SetItemData( sal_uInt16 nItemId, void* pNewData )
1206 {
1207     sal_uInt16 nPos = GetItemPos( nItemId );
1208 
1209     if ( nPos == STATUSBAR_ITEM_NOTFOUND )
1210         return;
1211 
1212     ImplStatusItem* pItem = mvItemList[ nPos ].get();
1213     // invalidate cache
1214     pItem->mxLayoutCache.reset();
1215     pItem->mpUserData = pNewData;
1216 
1217     // call Draw-Item if it's a User-Item
1218     if ( (pItem->mnBits & StatusBarItemBits::UserDraw) && pItem->mbVisible &&
1219          !mbFormat && ImplIsItemUpdate() )
1220     {
1221         tools::Rectangle aRect = ImplGetItemRectPos(nPos);
1222         Invalidate(aRect, InvalidateFlags::NoErase);
1223         PaintImmediately();
1224     }
1225 }
1226 
1227 void* StatusBar::GetItemData( sal_uInt16 nItemId ) const
1228 {
1229     sal_uInt16 nPos = GetItemPos( nItemId );
1230 
1231     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1232         return mvItemList[ nPos ]->mpUserData;
1233 
1234     return nullptr;
1235 }
1236 
1237 void StatusBar::RedrawItem(sal_uInt16 nItemId)
1238 {
1239     if ( mbFormat )
1240         return;
1241 
1242     sal_uInt16 nPos = GetItemPos(nItemId);
1243     if ( nPos == STATUSBAR_ITEM_NOTFOUND )
1244         return;
1245 
1246     ImplStatusItem* pItem = mvItemList[ nPos ].get();
1247     if ((pItem->mnBits & StatusBarItemBits::UserDraw) &&
1248         pItem->mbVisible && ImplIsItemUpdate())
1249     {
1250         tools::Rectangle aRect = ImplGetItemRectPos(nPos);
1251         Invalidate(aRect);
1252         PaintImmediately();
1253     }
1254 }
1255 
1256 void StatusBar::SetHelpText( sal_uInt16 nItemId, const OUString& rText )
1257 {
1258     sal_uInt16 nPos = GetItemPos( nItemId );
1259 
1260     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1261         mvItemList[ nPos ]->maHelpText = rText;
1262 }
1263 
1264 const OUString& StatusBar::GetHelpText( sal_uInt16 nItemId ) const
1265 {
1266     sal_uInt16 nPos = GetItemPos( nItemId );
1267 
1268     assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
1269 
1270     ImplStatusItem* pItem = mvItemList[ nPos ].get();
1271     if ( pItem->maHelpText.isEmpty() && ( !pItem->maHelpId.isEmpty() || !pItem->maCommand.isEmpty() ))
1272     {
1273         Help* pHelp = Application::GetHelp();
1274         if ( pHelp )
1275         {
1276             if ( !pItem->maCommand.isEmpty() )
1277                 pItem->maHelpText = pHelp->GetHelpText( pItem->maCommand, this );
1278             if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
1279                 pItem->maHelpText = pHelp->GetHelpText( OStringToOUString( pItem->maHelpId, RTL_TEXTENCODING_UTF8 ), this );
1280         }
1281     }
1282 
1283     return pItem->maHelpText;
1284 }
1285 
1286 void StatusBar::SetQuickHelpText( sal_uInt16 nItemId, const OUString& rText )
1287 {
1288     sal_uInt16 nPos = GetItemPos( nItemId );
1289 
1290     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1291         mvItemList[ nPos ]->maQuickHelpText = rText;
1292 }
1293 
1294 const OUString& StatusBar::GetQuickHelpText( sal_uInt16 nItemId ) const
1295 {
1296     sal_uInt16 nPos = GetItemPos( nItemId );
1297 
1298     assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
1299 
1300     ImplStatusItem* pItem = mvItemList[ nPos ].get();
1301     return pItem->maQuickHelpText;
1302 }
1303 
1304 void StatusBar::SetHelpId( sal_uInt16 nItemId, const OString& rHelpId )
1305 {
1306     sal_uInt16 nPos = GetItemPos( nItemId );
1307 
1308     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1309         mvItemList[ nPos ]->maHelpId = rHelpId;
1310 }
1311 
1312 void StatusBar::StartProgressMode( const OUString& rText )
1313 {
1314     SAL_WARN_IF( mbProgressMode, "vcl", "StatusBar::StartProgressMode(): progress mode is active" );
1315 
1316     mbProgressMode  = true;
1317     mnPercent       = 0;
1318     maPrgsTxt       = rText;
1319 
1320     // compute size
1321     ImplCalcProgressRect();
1322 
1323     // trigger Paint, which draws text and frame
1324     if ( IsReallyVisible() )
1325     {
1326         Invalidate();
1327         PaintImmediately();
1328     }
1329 }
1330 
1331 void StatusBar::SetProgressValue( sal_uInt16 nNewPercent )
1332 {
1333     SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::SetProgressValue(): no progress mode" );
1334     SAL_WARN_IF( nNewPercent > 100, "vcl", "StatusBar::SetProgressValue(): nPercent > 100" );
1335 
1336     bool bInvalidate = mbProgressMode && IsReallyVisible() && (!mnPercent || (mnPercent != nNewPercent));
1337 
1338     mnPercent = nNewPercent;
1339 
1340     if (bInvalidate)
1341     {
1342         // Rate limit how often we paint, otherwise in some loading scenarios we can spend significant
1343         // time just painting progress bars.
1344         sal_uInt32 nTime_ms = osl_getGlobalTimer();
1345         if ((nTime_ms - mnLastProgressPaint_ms) > 100)
1346         {
1347             Invalidate(maPrgsFrameRect);
1348             PaintImmediately();
1349             mnLastProgressPaint_ms = nTime_ms;
1350         }
1351     }
1352 }
1353 
1354 void StatusBar::EndProgressMode()
1355 {
1356     SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::EndProgressMode(): no progress mode" );
1357 
1358     mbProgressMode = false;
1359     maPrgsTxt.clear();
1360 
1361     if ( IsReallyVisible() )
1362     {
1363         Invalidate();
1364         PaintImmediately();
1365     }
1366 }
1367 
1368 void StatusBar::SetText(const OUString& rText)
1369 {
1370     if ((GetStyle() & WB_RIGHT) && !mbProgressMode && IsReallyVisible() && IsUpdateMode())
1371     {
1372         if (mbFormat)
1373         {
1374             Invalidate();
1375             Window::SetText(rText);
1376         }
1377         else
1378         {
1379             Invalidate();
1380             Window::SetText(rText);
1381             PaintImmediately();
1382         }
1383     }
1384     else if (mbProgressMode)
1385     {
1386         maPrgsTxt = rText;
1387         if (IsReallyVisible())
1388         {
1389             Invalidate();
1390             PaintImmediately();
1391         }
1392     }
1393     else
1394     {
1395         Window::SetText(rText);
1396     }
1397 }
1398 
1399 Size StatusBar::CalcWindowSizePixel() const
1400 {
1401     size_t  i = 0;
1402     size_t  nCount = mvItemList.size();
1403     tools::Long    nOffset = 0;
1404     tools::Long    nCalcWidth = STATUSBAR_OFFSET_X*2;
1405     tools::Long    nCalcHeight;
1406 
1407     while ( i < nCount )
1408     {
1409         ImplStatusItem* pItem = mvItemList[ i ].get();
1410         nCalcWidth += pItem->mnWidth + nOffset;
1411         nOffset = pItem->mnOffset;
1412         i++;
1413     }
1414 
1415     tools::Long nMinHeight = GetTextHeight();
1416     const tools::Long nBarTextOffset = STATUSBAR_OFFSET_TEXTY*2;
1417     tools::Long nProgressHeight = nMinHeight + nBarTextOffset;
1418 
1419     if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
1420     {
1421         ImplControlValue aValue;
1422         tools::Rectangle aControlRegion( Point(), Size( nCalcWidth, nMinHeight ) );
1423         tools::Rectangle aNativeControlRegion, aNativeContentRegion;
1424         if( GetNativeControlRegion( ControlType::Progress, ControlPart::Entire,
1425                     aControlRegion, ControlState::ENABLED, aValue,
1426                     aNativeControlRegion, aNativeContentRegion ) )
1427         {
1428             nProgressHeight = aNativeControlRegion.GetHeight();
1429         }
1430     }
1431 
1432     nCalcHeight = nMinHeight+nBarTextOffset;
1433     if( nCalcHeight < nProgressHeight+2 )
1434         nCalcHeight = nProgressHeight+2;
1435 
1436     return Size( nCalcWidth, nCalcHeight );
1437 }
1438 
1439 void StatusBar::SetAccessibleName( sal_uInt16 nItemId, const OUString& rName )
1440 {
1441     sal_uInt16 nPos = GetItemPos( nItemId );
1442 
1443     if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1444     {
1445         ImplStatusItem* pItem = mvItemList[ nPos ].get();
1446 
1447         if ( pItem->maAccessibleName != rName )
1448         {
1449             pItem->maAccessibleName = rName;
1450             CallEventListeners( VclEventId::StatusbarNameChanged, reinterpret_cast<void*>(pItem->mnId) );
1451         }
1452     }
1453 }
1454 
1455 const OUString& StatusBar::GetAccessibleName( sal_uInt16 nItemId ) const
1456 {
1457     sal_uInt16 nPos = GetItemPos( nItemId );
1458 
1459     assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
1460 
1461     return mvItemList[ nPos ]->maAccessibleName;
1462 }
1463 
1464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1465