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