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 rRenderContext.Push(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 rRenderContext.Pop();
728 }
729 else
730 {
731 // draw text
732 if (GetStyle() & WB_RIGHT)
733 ImplDrawText(rRenderContext);
734
735 // draw items
736
737 // Do offscreen only when we are not recording layout...
738 bool bOffscreen = !rRenderContext.ImplIsRecordLayout();
739
740 if (!bOffscreen)
741 rRenderContext.Erase(rRect);
742
743 for (sal_uInt16 i = 0; i < nItemCount; i++)
744 ImplDrawItem(rRenderContext, bOffscreen, i);
745 }
746
747 // draw line at the top of the status bar (to visually distinguish it from
748 // shell / docking area)
749 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
750 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
751 rRenderContext.DrawLine(Point(0, 0), Point(mnDX-1, 0));
752 }
753
Resize()754 void StatusBar::Resize()
755 {
756 // save width and height
757 Size aSize = GetOutputSizePixel();
758 mnDX = aSize.Width() - ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
759 mnDY = aSize.Height();
760 mnCalcHeight = mnDY;
761
762 mnTextY = (mnCalcHeight-GetTextHeight())/2;
763
764 // provoke re-formatting
765 mbFormat = true;
766
767 if ( mbProgressMode )
768 ImplCalcProgressRect();
769
770 Invalidate();
771 }
772
RequestHelp(const HelpEvent & rHEvt)773 void StatusBar::RequestHelp( const HelpEvent& rHEvt )
774 {
775 // no keyboard help in status bar
776 if( rHEvt.KeyboardActivated() )
777 return;
778
779 sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
780
781 if ( nItemId )
782 {
783 tools::Rectangle aItemRect = GetItemRect( nItemId );
784 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
785 aItemRect.SetLeft( aPt.X() );
786 aItemRect.SetTop( aPt.Y() );
787 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
788 aItemRect.SetRight( aPt.X() );
789 aItemRect.SetBottom( aPt.Y() );
790
791 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
792 {
793 OUString aStr = GetHelpText( nItemId );
794 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
795 return;
796 }
797 else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
798 {
799 OUString aStr(GetQuickHelpText(nItemId));
800 // show quickhelp if available
801 if (!aStr.isEmpty())
802 {
803 Help::ShowQuickHelp( this, aItemRect, aStr );
804 return;
805 }
806 aStr = GetItemText( nItemId );
807 // show a quick help if item text doesn't fit
808 if ( GetTextWidth( aStr ) > aItemRect.GetWidth() )
809 {
810 Help::ShowQuickHelp( this, aItemRect, aStr );
811 return;
812 }
813 }
814 }
815
816 Window::RequestHelp( rHEvt );
817 }
818
StateChanged(StateChangedType nType)819 void StatusBar::StateChanged( StateChangedType nType )
820 {
821 Window::StateChanged( nType );
822
823 if ( nType == StateChangedType::InitShow )
824 ImplFormat();
825 else if ( nType == StateChangedType::UpdateMode )
826 Invalidate();
827 else if ( (nType == StateChangedType::Zoom) ||
828 (nType == StateChangedType::ControlFont) )
829 {
830 mbFormat = true;
831 ImplInitSettings();
832 Invalidate();
833 }
834 else if ( nType == StateChangedType::ControlForeground )
835 {
836 ImplInitSettings();
837 Invalidate();
838 }
839 else if ( nType == StateChangedType::ControlBackground )
840 {
841 ImplInitSettings();
842 Invalidate();
843 }
844
845 //invalidate layout cache
846 for (auto & pItem : mvItemList)
847 {
848 pItem->mLayoutGlyphsCache.reset();
849 }
850
851 }
852
DataChanged(const DataChangedEvent & rDCEvt)853 void StatusBar::DataChanged( const DataChangedEvent& rDCEvt )
854 {
855 Window::DataChanged( rDCEvt );
856
857 if ( !((rDCEvt.GetType() == DataChangedEventType::DISPLAY )
858 || (rDCEvt.GetType() == DataChangedEventType::FONTS )
859 || (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION)
860 || ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS)
861 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
862 ))
863 )
864 return;
865
866 mbFormat = true;
867 ImplInitSettings();
868 tools::Long nFudge = GetTextHeight() / 4;
869 for (auto & pItem : mvItemList)
870 {
871 tools::Long nWidth = GetTextWidth( pItem->maText ) + nFudge;
872 if( nWidth > pItem->mnWidth + STATUSBAR_OFFSET )
873 pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
874
875 pItem->mLayoutGlyphsCache.reset();
876 }
877 Size aSize = GetSizePixel();
878 // do not disturb current width, since
879 // CalcWindowSizePixel calculates a minimum width
880 aSize.setHeight( CalcWindowSizePixel().Height() );
881 SetSizePixel( aSize );
882 Invalidate();
883 }
884
Click()885 void StatusBar::Click()
886 {
887 maClickHdl.Call( this );
888 }
889
DoubleClick()890 void StatusBar::DoubleClick()
891 {
892 maDoubleClickHdl.Call( this );
893 }
894
UserDraw(const UserDrawEvent &)895 void StatusBar::UserDraw( const UserDrawEvent& )
896 {
897 }
898
InsertItem(sal_uInt16 nItemId,sal_uLong nWidth,StatusBarItemBits nBits,tools::Long nOffset,sal_uInt16 nPos)899 void StatusBar::InsertItem( sal_uInt16 nItemId, sal_uLong nWidth,
900 StatusBarItemBits nBits,
901 tools::Long nOffset, sal_uInt16 nPos )
902 {
903 SAL_WARN_IF( !nItemId, "vcl", "StatusBar::InsertItem(): ItemId == 0" );
904 SAL_WARN_IF( GetItemPos( nItemId ) != STATUSBAR_ITEM_NOTFOUND, "vcl",
905 "StatusBar::InsertItem(): ItemId already exists" );
906
907 // default: IN and CENTER
908 if ( !(nBits & (StatusBarItemBits::In | StatusBarItemBits::Out | StatusBarItemBits::Flat)) )
909 nBits |= StatusBarItemBits::In;
910 if ( !(nBits & (StatusBarItemBits::Left | StatusBarItemBits::Right | StatusBarItemBits::Center)) )
911 nBits |= StatusBarItemBits::Center;
912
913 // create item
914 if (mbAdjustHiDPI)
915 {
916 nWidth *= GetDPIScaleFactor();
917 }
918 tools::Long nFudge = GetTextHeight()/4;
919 std::unique_ptr<ImplStatusItem> pItem(new ImplStatusItem);
920 pItem->mnId = nItemId;
921 pItem->mnBits = nBits;
922 pItem->mnWidth = static_cast<tools::Long>(nWidth)+nFudge+STATUSBAR_OFFSET;
923 pItem->mnOffset = nOffset;
924 pItem->mpUserData = nullptr;
925 pItem->mbVisible = true;
926
927 // add item to list
928 if ( nPos < mvItemList.size() ) {
929 mvItemList.insert( mvItemList.begin() + nPos, std::move(pItem) );
930 } else {
931 mvItemList.push_back( std::move(pItem) );
932 }
933
934 mbFormat = true;
935 if ( ImplIsItemUpdate() )
936 Invalidate();
937
938 CallEventListeners( VclEventId::StatusbarItemAdded, reinterpret_cast<void*>(nItemId) );
939 }
940
RemoveItem(sal_uInt16 nItemId)941 void StatusBar::RemoveItem( sal_uInt16 nItemId )
942 {
943 sal_uInt16 nPos = GetItemPos( nItemId );
944 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
945 {
946 mvItemList.erase( mvItemList.begin() + nPos );
947
948 mbFormat = true;
949 if ( ImplIsItemUpdate() )
950 Invalidate();
951
952 CallEventListeners( VclEventId::StatusbarItemRemoved, reinterpret_cast<void*>(nItemId) );
953 }
954 }
955
ShowItem(sal_uInt16 nItemId)956 void StatusBar::ShowItem( sal_uInt16 nItemId )
957 {
958 sal_uInt16 nPos = GetItemPos( nItemId );
959
960 if ( nPos == STATUSBAR_ITEM_NOTFOUND )
961 return;
962
963 ImplStatusItem* pItem = mvItemList[ nPos ].get();
964 if ( !pItem->mbVisible )
965 {
966 pItem->mbVisible = true;
967
968 mbFormat = true;
969 if ( ImplIsItemUpdate() )
970 Invalidate();
971
972 CallEventListeners( VclEventId::StatusbarShowItem, reinterpret_cast<void*>(nItemId) );
973 }
974 }
975
HideItem(sal_uInt16 nItemId)976 void StatusBar::HideItem( sal_uInt16 nItemId )
977 {
978 sal_uInt16 nPos = GetItemPos( nItemId );
979
980 if ( nPos == STATUSBAR_ITEM_NOTFOUND )
981 return;
982
983 ImplStatusItem* pItem = mvItemList[ nPos ].get();
984 if ( pItem->mbVisible )
985 {
986 pItem->mbVisible = false;
987
988 mbFormat = true;
989 if ( ImplIsItemUpdate() )
990 Invalidate();
991
992 CallEventListeners( VclEventId::StatusbarHideItem, reinterpret_cast<void*>(nItemId) );
993 }
994 }
995
IsItemVisible(sal_uInt16 nItemId) const996 bool StatusBar::IsItemVisible( sal_uInt16 nItemId ) const
997 {
998 sal_uInt16 nPos = GetItemPos( nItemId );
999
1000 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1001 return mvItemList[ nPos ]->mbVisible;
1002 else
1003 return false;
1004 }
1005
Clear()1006 void StatusBar::Clear()
1007 {
1008 // delete all items
1009 mvItemList.clear();
1010
1011 mbFormat = true;
1012 if ( ImplIsItemUpdate() )
1013 Invalidate();
1014
1015 CallEventListeners( VclEventId::StatusbarAllItemsRemoved );
1016 }
1017
GetItemCount() const1018 sal_uInt16 StatusBar::GetItemCount() const
1019 {
1020 return static_cast<sal_uInt16>(mvItemList.size());
1021 }
1022
GetItemId(sal_uInt16 nPos) const1023 sal_uInt16 StatusBar::GetItemId( sal_uInt16 nPos ) const
1024 {
1025 if ( nPos < mvItemList.size() )
1026 return mvItemList[ nPos ]->mnId;
1027 return 0;
1028 }
1029
GetItemPos(sal_uInt16 nItemId) const1030 sal_uInt16 StatusBar::GetItemPos( sal_uInt16 nItemId ) const
1031 {
1032 for ( size_t i = 0, n = mvItemList.size(); i < n; ++i ) {
1033 if ( mvItemList[ i ]->mnId == nItemId ) {
1034 return sal_uInt16( i );
1035 }
1036 }
1037
1038 return STATUSBAR_ITEM_NOTFOUND;
1039 }
1040
GetItemId(const Point & rPos) const1041 sal_uInt16 StatusBar::GetItemId( const Point& rPos ) const
1042 {
1043 if ( !mbFormat )
1044 {
1045 sal_uInt16 nItemCount = GetItemCount();
1046 sal_uInt16 nPos;
1047 for ( nPos = 0; nPos < nItemCount; nPos++ )
1048 {
1049 // get rectangle
1050 tools::Rectangle aRect = ImplGetItemRectPos( nPos );
1051 if ( aRect.Contains( rPos ) )
1052 return mvItemList[ nPos ]->mnId;
1053 }
1054 }
1055
1056 return 0;
1057 }
1058
GetItemRect(sal_uInt16 nItemId) const1059 tools::Rectangle StatusBar::GetItemRect( sal_uInt16 nItemId ) const
1060 {
1061 tools::Rectangle aRect;
1062
1063 if ( !mbFormat )
1064 {
1065 sal_uInt16 nPos = GetItemPos( nItemId );
1066 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1067 {
1068 // get rectangle and subtract frame
1069 aRect = ImplGetItemRectPos( nPos );
1070 tools::Long nW = 1;
1071 aRect.AdjustTop(nW-1 );
1072 aRect.AdjustBottom( -(nW-1) );
1073 aRect.AdjustLeft(nW );
1074 aRect.AdjustRight( -nW );
1075 return aRect;
1076 }
1077 }
1078
1079 return aRect;
1080 }
1081
GetItemTextPos(sal_uInt16 nItemId) const1082 Point StatusBar::GetItemTextPos( sal_uInt16 nItemId ) const
1083 {
1084 if ( !mbFormat )
1085 {
1086 sal_uInt16 nPos = GetItemPos( nItemId );
1087 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1088 {
1089 // get rectangle
1090 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1091 tools::Rectangle aRect = ImplGetItemRectPos( nPos );
1092 tools::Long nW = 1;
1093 tools::Rectangle aTextRect( aRect.Left()+nW, aRect.Top()+nW,
1094 aRect.Right()-nW, aRect.Bottom()-nW );
1095 Point aPos = ImplGetItemTextPos( aTextRect.GetSize(),
1096 Size( GetTextWidth( pItem->maText ), GetTextHeight() ),
1097 pItem->mnBits );
1098 if ( !mbInUserDraw )
1099 {
1100 aPos.AdjustX(aTextRect.Left() );
1101 aPos.AdjustY(aTextRect.Top() );
1102 }
1103 return aPos;
1104 }
1105 }
1106
1107 return Point();
1108 }
1109
GetItemWidth(sal_uInt16 nItemId) const1110 sal_uLong StatusBar::GetItemWidth( sal_uInt16 nItemId ) const
1111 {
1112 sal_uInt16 nPos = GetItemPos( nItemId );
1113
1114 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1115 return mvItemList[ nPos ]->mnWidth;
1116
1117 return 0;
1118 }
1119
GetItemBits(sal_uInt16 nItemId) const1120 StatusBarItemBits StatusBar::GetItemBits( sal_uInt16 nItemId ) const
1121 {
1122 sal_uInt16 nPos = GetItemPos( nItemId );
1123
1124 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1125 return mvItemList[ nPos ]->mnBits;
1126
1127 return StatusBarItemBits::NONE;
1128 }
1129
GetItemOffset(sal_uInt16 nItemId) const1130 tools::Long StatusBar::GetItemOffset( sal_uInt16 nItemId ) const
1131 {
1132 sal_uInt16 nPos = GetItemPos( nItemId );
1133
1134 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1135 return mvItemList[ nPos ]->mnOffset;
1136
1137 return 0;
1138 }
1139
PaintSelfAndChildrenImmediately()1140 void StatusBar::PaintSelfAndChildrenImmediately()
1141 {
1142 WindowImpl* pWindowImpl = ImplGetWindowImpl();
1143 const bool bOrigOverlapWin = pWindowImpl->mbOverlapWin;
1144 // Temporarily set mbOverlapWin so that any parent windows of StatusBar
1145 // that happen to have accumulated Invalidates are not taken as the root
1146 // paint candidate from which to paint the paint hierarchy. So we limit the
1147 // paint here to this statusbar and its children, disabling the
1148 // optimization to bundle pending paints together and suppressing any
1149 // unexpected side effects of entering parent window paint handlers if this
1150 // call is not from the primordial thread.
1151 pWindowImpl->mbOverlapWin = true;
1152 PaintImmediately();
1153 pWindowImpl->mbOverlapWin = bOrigOverlapWin;
1154 }
1155
SetItemText(sal_uInt16 nItemId,const OUString & rText,int nCharsWidth)1156 void StatusBar::SetItemText( sal_uInt16 nItemId, const OUString& rText, int nCharsWidth )
1157 {
1158 sal_uInt16 nPos = GetItemPos( nItemId );
1159
1160 if ( nPos == STATUSBAR_ITEM_NOTFOUND )
1161 return;
1162
1163 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1164
1165 if ( pItem->maText == rText )
1166 return;
1167
1168 pItem->maText = rText;
1169
1170 // adjust item width - see also DataChanged()
1171 tools::Long nFudge = GetTextHeight()/4;
1172
1173 tools::Long nWidth;
1174 if (nCharsWidth != -1)
1175 {
1176 nWidth = GetTextWidth(u"0"_ustr,0,-1,nullptr,
1177 SalLayoutGlyphsCache::self()->GetLayoutGlyphs(GetOutDev(),u"0"_ustr));
1178 nWidth = nWidth * nCharsWidth + nFudge;
1179 }
1180 else
1181 {
1182 pItem->mLayoutGlyphsCache.reset();
1183 nWidth = GetTextWidth( pItem->maText,0,-1,nullptr, pItem->GetTextGlyphs(GetOutDev())) + nFudge;
1184 }
1185
1186 if( (nWidth > pItem->mnWidth + STATUSBAR_OFFSET) ||
1187 ((nWidth < pItem->mnWidth) && (mnDX - STATUSBAR_OFFSET) < mnItemsWidth ))
1188 {
1189 pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
1190 ImplFormat();
1191 Invalidate();
1192 }
1193
1194 // re-draw item if StatusBar is visible and UpdateMode active
1195 if ( pItem->mbVisible && !mbFormat && ImplIsItemUpdate() )
1196 {
1197 tools::Rectangle aRect = ImplGetItemRectPos(nPos);
1198 Invalidate(aRect);
1199 PaintSelfAndChildrenImmediately();
1200 }
1201 }
1202
GetItemText(sal_uInt16 nItemId) const1203 const OUString& StatusBar::GetItemText( sal_uInt16 nItemId ) const
1204 {
1205 sal_uInt16 nPos = GetItemPos( nItemId );
1206
1207 assert( nPos != STATUSBAR_ITEM_NOTFOUND );
1208
1209 return mvItemList[ nPos ]->maText;
1210 }
1211
SetItemCommand(sal_uInt16 nItemId,const OUString & rCommand)1212 void StatusBar::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
1213 {
1214 sal_uInt16 nPos = GetItemPos( nItemId );
1215
1216 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1217 {
1218 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1219
1220 if ( pItem->maCommand != rCommand )
1221 pItem->maCommand = rCommand;
1222 }
1223 }
1224
GetItemCommand(sal_uInt16 nItemId)1225 OUString StatusBar::GetItemCommand( sal_uInt16 nItemId )
1226 {
1227 sal_uInt16 nPos = GetItemPos( nItemId );
1228
1229 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1230 return mvItemList[ nPos ]->maCommand;
1231
1232 return OUString();
1233 }
1234
SetItemData(sal_uInt16 nItemId,void * pNewData)1235 void StatusBar::SetItemData( sal_uInt16 nItemId, void* pNewData )
1236 {
1237 sal_uInt16 nPos = GetItemPos( nItemId );
1238
1239 if ( nPos == STATUSBAR_ITEM_NOTFOUND )
1240 return;
1241
1242 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1243 // invalidate cache
1244 pItem->mLayoutGlyphsCache.reset();
1245 pItem->mpUserData = pNewData;
1246
1247 // call Draw-Item if it's a User-Item
1248 if ( (pItem->mnBits & StatusBarItemBits::UserDraw) && pItem->mbVisible &&
1249 !mbFormat && ImplIsItemUpdate() )
1250 {
1251 tools::Rectangle aRect = ImplGetItemRectPos(nPos);
1252 Invalidate(aRect, InvalidateFlags::NoErase);
1253 PaintSelfAndChildrenImmediately();
1254 }
1255 }
1256
GetItemData(sal_uInt16 nItemId) const1257 void* StatusBar::GetItemData( sal_uInt16 nItemId ) const
1258 {
1259 sal_uInt16 nPos = GetItemPos( nItemId );
1260
1261 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1262 return mvItemList[ nPos ]->mpUserData;
1263
1264 return nullptr;
1265 }
1266
RedrawItem(sal_uInt16 nItemId)1267 void StatusBar::RedrawItem(sal_uInt16 nItemId)
1268 {
1269 if ( mbFormat )
1270 return;
1271
1272 sal_uInt16 nPos = GetItemPos(nItemId);
1273 if ( nPos == STATUSBAR_ITEM_NOTFOUND )
1274 return;
1275
1276 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1277 if ((pItem->mnBits & StatusBarItemBits::UserDraw) &&
1278 pItem->mbVisible && ImplIsItemUpdate())
1279 {
1280 tools::Rectangle aRect = ImplGetItemRectPos(nPos);
1281 Invalidate(aRect);
1282 PaintSelfAndChildrenImmediately();
1283 }
1284 }
1285
SetHelpText(sal_uInt16 nItemId,const OUString & rText)1286 void StatusBar::SetHelpText( sal_uInt16 nItemId, const OUString& rText )
1287 {
1288 sal_uInt16 nPos = GetItemPos( nItemId );
1289
1290 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1291 mvItemList[ nPos ]->maHelpText = rText;
1292 }
1293
GetHelpText(sal_uInt16 nItemId) const1294 const OUString& StatusBar::GetHelpText( 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 if ( pItem->maHelpText.isEmpty() && ( !pItem->maHelpId.isEmpty() || !pItem->maCommand.isEmpty() ))
1302 {
1303 Help* pHelp = Application::GetHelp();
1304 if ( pHelp )
1305 {
1306 if ( !pItem->maCommand.isEmpty() )
1307 pItem->maHelpText = pHelp->GetHelpText( pItem->maCommand );
1308 if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
1309 pItem->maHelpText = pHelp->GetHelpText( pItem->maHelpId );
1310 }
1311 }
1312
1313 return pItem->maHelpText;
1314 }
1315
SetQuickHelpText(sal_uInt16 nItemId,const OUString & rText)1316 void StatusBar::SetQuickHelpText( sal_uInt16 nItemId, const OUString& rText )
1317 {
1318 sal_uInt16 nPos = GetItemPos( nItemId );
1319
1320 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1321 mvItemList[ nPos ]->maQuickHelpText = rText;
1322 }
1323
GetQuickHelpText(sal_uInt16 nItemId) const1324 const OUString& StatusBar::GetQuickHelpText( sal_uInt16 nItemId ) const
1325 {
1326 sal_uInt16 nPos = GetItemPos( nItemId );
1327
1328 assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
1329
1330 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1331 return pItem->maQuickHelpText;
1332 }
1333
SetHelpId(sal_uInt16 nItemId,const OUString & rHelpId)1334 void StatusBar::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
1335 {
1336 sal_uInt16 nPos = GetItemPos( nItemId );
1337
1338 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1339 mvItemList[ nPos ]->maHelpId = rHelpId;
1340 }
1341
StartProgressMode(const OUString & rText)1342 void StatusBar::StartProgressMode( const OUString& rText )
1343 {
1344 SAL_WARN_IF( mbProgressMode, "vcl", "StatusBar::StartProgressMode(): progress mode is active" );
1345
1346 mbProgressMode = true;
1347 mnPercent = 0;
1348 maPrgsTxt = rText;
1349
1350 // compute size
1351 ImplCalcProgressRect();
1352
1353 // trigger Paint, which draws text and frame
1354 if ( IsReallyVisible() )
1355 {
1356 Invalidate();
1357 PaintSelfAndChildrenImmediately();
1358 }
1359 }
1360
SetProgressValue(sal_uInt16 nNewPercent)1361 void StatusBar::SetProgressValue( sal_uInt16 nNewPercent )
1362 {
1363 SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::SetProgressValue(): no progress mode" );
1364 SAL_WARN_IF( nNewPercent > 100, "vcl", "StatusBar::SetProgressValue(): nPercent > 100" );
1365
1366 bool bInvalidate = mbProgressMode && IsReallyVisible() && (!mnPercent || (mnPercent != nNewPercent));
1367
1368 mnPercent = nNewPercent;
1369
1370 if (bInvalidate)
1371 {
1372 // Rate limit how often we paint, otherwise in some loading scenarios we can spend significant
1373 // time just painting progress bars.
1374 sal_uInt32 nTime_ms = osl_getGlobalTimer();
1375 if ((nTime_ms - mnLastProgressPaint_ms) > 100)
1376 {
1377 Invalidate(maPrgsFrameRect);
1378 PaintSelfAndChildrenImmediately();
1379 mnLastProgressPaint_ms = nTime_ms;
1380 }
1381 }
1382 }
1383
EndProgressMode()1384 void StatusBar::EndProgressMode()
1385 {
1386 SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::EndProgressMode(): no progress mode" );
1387
1388 mbProgressMode = false;
1389 maPrgsTxt.clear();
1390
1391 if ( IsReallyVisible() )
1392 {
1393 Invalidate();
1394 PaintSelfAndChildrenImmediately();
1395 }
1396 }
1397
SetText(const OUString & rText)1398 void StatusBar::SetText(const OUString& rText)
1399 {
1400 if ((GetStyle() & WB_RIGHT) && !mbProgressMode && IsReallyVisible() && IsUpdateMode())
1401 {
1402 if (mbFormat)
1403 {
1404 Invalidate();
1405 Window::SetText(rText);
1406 }
1407 else
1408 {
1409 Invalidate();
1410 Window::SetText(rText);
1411 PaintSelfAndChildrenImmediately();
1412 }
1413 }
1414 else if (mbProgressMode)
1415 {
1416 maPrgsTxt = rText;
1417 if (IsReallyVisible())
1418 {
1419 Invalidate();
1420 PaintSelfAndChildrenImmediately();
1421 }
1422 }
1423 else
1424 {
1425 Window::SetText(rText);
1426 }
1427 }
1428
CalcWindowSizePixel() const1429 Size StatusBar::CalcWindowSizePixel() const
1430 {
1431 size_t i = 0;
1432 size_t nCount = mvItemList.size();
1433 tools::Long nOffset = 0;
1434 tools::Long nCalcWidth = STATUSBAR_OFFSET_X*2;
1435 tools::Long nCalcHeight;
1436
1437 while ( i < nCount )
1438 {
1439 ImplStatusItem* pItem = mvItemList[ i ].get();
1440 nCalcWidth += pItem->mnWidth + nOffset;
1441 nOffset = pItem->mnOffset;
1442 i++;
1443 }
1444
1445 tools::Long nMinHeight = std::max( static_cast<int>(GetTextHeight()), STATUSBAR_MIN_HEIGHT);
1446 const tools::Long nBarTextOffset = STATUSBAR_OFFSET_TEXTY*2;
1447 tools::Long nProgressHeight = nMinHeight + nBarTextOffset;
1448
1449 if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
1450 {
1451 ImplControlValue aValue;
1452 tools::Rectangle aControlRegion( Point(), Size( nCalcWidth, nMinHeight ) );
1453 tools::Rectangle aNativeControlRegion, aNativeContentRegion;
1454 if( GetNativeControlRegion( ControlType::Progress, ControlPart::Entire,
1455 aControlRegion, ControlState::ENABLED, aValue,
1456 aNativeControlRegion, aNativeContentRegion ) )
1457 {
1458 nProgressHeight = aNativeControlRegion.GetHeight();
1459 }
1460 }
1461
1462 nCalcHeight = nMinHeight+nBarTextOffset;
1463 if( nCalcHeight < nProgressHeight+2 )
1464 nCalcHeight = nProgressHeight+2;
1465
1466 return Size( nCalcWidth, nCalcHeight );
1467 }
1468
SetAccessibleName(sal_uInt16 nItemId,const OUString & rName)1469 void StatusBar::SetAccessibleName( sal_uInt16 nItemId, const OUString& rName )
1470 {
1471 sal_uInt16 nPos = GetItemPos( nItemId );
1472
1473 if ( nPos != STATUSBAR_ITEM_NOTFOUND )
1474 {
1475 ImplStatusItem* pItem = mvItemList[ nPos ].get();
1476
1477 if ( pItem->maAccessibleName != rName )
1478 {
1479 pItem->maAccessibleName = rName;
1480 CallEventListeners( VclEventId::StatusbarNameChanged, reinterpret_cast<void*>(pItem->mnId) );
1481 }
1482 }
1483 }
1484
GetAccessibleName(sal_uInt16 nItemId) const1485 const OUString& StatusBar::GetAccessibleName( sal_uInt16 nItemId ) const
1486 {
1487 sal_uInt16 nPos = GetItemPos( nItemId );
1488
1489 assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
1490
1491 return mvItemList[ nPos ]->maAccessibleName;
1492 }
1493
1494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1495