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