1 /* 2 * Copyright 2001-2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 /*! BStatusBar displays a "percentage-of-completion" gauge. */ 11 12 #include <Message.h> 13 #include <Region.h> 14 #include <StatusBar.h> 15 16 #include <Layout.h> 17 #include <LayoutUtils.h> 18 19 #include <stdlib.h> 20 #include <string.h> 21 22 23 static const rgb_color kDefaultBarColor = {50, 150, 255, 255}; 24 25 26 BStatusBar::BStatusBar(BRect frame, const char *name, const char *label, 27 const char *trailingLabel) 28 : BView(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW), 29 fLabel(label), 30 fTrailingLabel(trailingLabel) 31 { 32 _InitObject(); 33 } 34 35 36 BStatusBar::BStatusBar(const char *name, const char *label, 37 const char *trailingLabel) 38 : BView(BRect(0, 0, -1, -1), name, B_FOLLOW_LEFT | B_FOLLOW_TOP, 39 B_WILL_DRAW | B_SUPPORTS_LAYOUT), 40 fLabel(label), 41 fTrailingLabel(trailingLabel) 42 { 43 _InitObject(); 44 } 45 46 47 BStatusBar::BStatusBar(BMessage *archive) 48 : BView(archive) 49 { 50 _InitObject(); 51 52 archive->FindString("_label", &fLabel); 53 archive->FindString("_tlabel", &fTrailingLabel); 54 55 archive->FindString("_text", &fText); 56 archive->FindString("_ttext", &fTrailingText); 57 58 float floatValue; 59 if (archive->FindFloat("_high", &floatValue) == B_OK) { 60 fBarHeight = floatValue; 61 fCustomBarHeight = true; 62 } 63 64 int32 color; 65 if (archive->FindInt32("_bcolor", (int32 *)&color) == B_OK) 66 fBarColor = *(rgb_color *)&color; 67 68 if (archive->FindFloat("_val", &floatValue) == B_OK) 69 fCurrent = floatValue; 70 if (archive->FindFloat("_max", &floatValue) == B_OK) 71 fMax = floatValue; 72 } 73 74 75 BStatusBar::~BStatusBar() 76 { 77 } 78 79 80 void 81 BStatusBar::_InitObject() 82 { 83 fMax = 100.0f; 84 fCurrent = 0.0f; 85 86 fBarHeight = -1.0f; 87 fTextWidth = 0.0f; 88 fLabelWidth = 0.0f; 89 fTrailingTextWidth = 0.0f; 90 fTrailingLabelWidth = 0.0f; 91 92 fBarColor = kDefaultBarColor; 93 fCustomBarHeight = false; 94 95 SetFlags(Flags() | B_FRAME_EVENTS); 96 } 97 98 99 BArchivable * 100 BStatusBar::Instantiate(BMessage *archive) 101 { 102 if (validate_instantiation(archive, "BStatusBar")) 103 return new BStatusBar(archive); 104 105 return NULL; 106 } 107 108 109 status_t 110 BStatusBar::Archive(BMessage *archive, bool deep) const 111 { 112 status_t err = BView::Archive(archive, deep); 113 if (err < B_OK) 114 return err; 115 116 if (fCustomBarHeight) 117 err = archive->AddFloat("_high", fBarHeight); 118 119 if (err == B_OK && fBarColor != kDefaultBarColor) 120 err = archive->AddInt32("_bcolor", (const uint32 &)fBarColor); 121 122 if (err == B_OK && fCurrent != 0.0f) 123 err = archive->AddFloat("_val", fCurrent); 124 if (err == B_OK && fMax != 100.0f ) 125 err = archive->AddFloat("_max", fMax); 126 127 if (err == B_OK && fText.Length()) 128 err = archive->AddString("_text", fText); 129 if (err == B_OK && fTrailingText.Length()) 130 err = archive->AddString("_ttext", fTrailingText); 131 132 if (err == B_OK && fLabel.Length()) 133 err = archive->AddString("_label", fLabel); 134 if (err == B_OK && fTrailingLabel.Length()) 135 err = archive->AddString ("_tlabel", fTrailingLabel); 136 137 return err; 138 } 139 140 141 void 142 BStatusBar::AttachedToWindow() 143 { 144 // resize so that the height fits 145 float width, height; 146 GetPreferredSize(&width, &height); 147 ResizeTo(Bounds().Width(), height); 148 149 SetViewColor(B_TRANSPARENT_COLOR); 150 151 if (Parent()) 152 SetLowColor(Parent()->ViewColor()); 153 154 fLabelWidth = ceilf(StringWidth(fLabel.String())); 155 fTrailingLabelWidth = ceilf(StringWidth(fTrailingLabel.String())); 156 fTextWidth = ceilf(StringWidth(fText.String())); 157 fTrailingTextWidth = ceilf(StringWidth(fTrailingText.String())); 158 } 159 160 161 void 162 BStatusBar::MessageReceived(BMessage *message) 163 { 164 switch(message->what) { 165 case B_UPDATE_STATUS_BAR: 166 { 167 float delta; 168 const char *text = NULL, *trailing_text = NULL; 169 170 message->FindFloat("delta", &delta); 171 message->FindString("text", &text); 172 message->FindString("trailing_text", &trailing_text); 173 174 Update(delta, text, trailing_text); 175 176 break; 177 } 178 179 case B_RESET_STATUS_BAR: 180 { 181 const char *label = NULL, *trailing_label = NULL; 182 183 message->FindString("label", &label); 184 message->FindString("trailing_label", &trailing_label); 185 186 Reset(label, trailing_label); 187 188 break; 189 } 190 191 default: 192 BView::MessageReceived(message); 193 break; 194 } 195 } 196 197 198 void 199 BStatusBar::Draw(BRect updateRect) 200 { 201 rgb_color backgroundColor; 202 if (Parent()) 203 backgroundColor = Parent()->ViewColor(); 204 else 205 backgroundColor = ui_color(B_PANEL_BACKGROUND_COLOR); 206 207 font_height fontHeight; 208 GetFontHeight(&fontHeight); 209 BRect barFrame = _BarFrame(&fontHeight); 210 BRect outerFrame = barFrame.InsetByCopy(-2.0f, -2.0f); 211 212 BRegion background(updateRect); 213 background.Exclude(outerFrame); 214 FillRegion(&background, B_SOLID_LOW); 215 216 // Draw labels/texts 217 218 BRect rect = outerFrame; 219 rect.top = 0.0f; 220 rect.bottom = outerFrame.top - 1.0f; 221 222 if (updateRect.Intersects(rect)) { 223 // update labels 224 SetHighColor(ui_color(B_CONTROL_TEXT_COLOR)); 225 MovePenTo(0.0f, ceilf(fontHeight.ascent) + 1.0f); 226 227 if (fLabel.Length()) 228 DrawString(fLabel.String()); 229 if (fText.Length()) 230 DrawString(fText.String()); 231 232 if (fTrailingText.Length() || fTrailingLabel.Length()) { 233 MovePenTo(rect.right - fTrailingTextWidth - fTrailingLabelWidth, 234 ceilf(fontHeight.ascent) + 1.0f); 235 236 if (fTrailingText.Length()) 237 DrawString(fTrailingText.String()); 238 239 if (fTrailingLabel.Length()) 240 DrawString(fTrailingLabel.String()); 241 } 242 } 243 244 // Draw bar 245 246 if (!updateRect.Intersects(outerFrame)) 247 return; 248 249 rect = outerFrame; 250 251 // First bevel 252 SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_1_TINT)); 253 StrokeLine(rect.LeftBottom(), rect.LeftTop()); 254 StrokeLine(rect.RightTop()); 255 256 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT)); 257 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 258 StrokeLine(BPoint(rect.right, rect.top + 1.0f)); 259 260 rect.InsetBy(1.0f, 1.0f); 261 262 // Second bevel 263 SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_4_TINT)); 264 StrokeLine(rect.LeftBottom(), rect.LeftTop()); 265 StrokeLine(rect.RightTop()); 266 267 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 268 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 269 StrokeLine(BPoint(rect.right, rect.top + 1.0f)); 270 271 rect = barFrame; 272 rect.right = _BarPosition(barFrame); 273 274 // draw bar itself 275 276 if (rect.right >= rect.left) { 277 // Bevel 278 SetHighColor(tint_color(fBarColor, B_LIGHTEN_2_TINT)); 279 StrokeLine(rect.LeftBottom(), rect.LeftTop()); 280 StrokeLine(rect.RightTop()); 281 282 SetHighColor(tint_color(fBarColor, B_DARKEN_2_TINT)); 283 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom()); 284 StrokeLine(BPoint(rect.right, rect.top + 1.0f)); 285 286 // filling 287 SetHighColor(fBarColor); 288 FillRect(rect.InsetByCopy(1.0f, 1.0f)); 289 } 290 291 if (rect.right < barFrame.right) { 292 // empty space 293 rect.left = rect.right + 1.0f; 294 rect.right = barFrame.right; 295 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_MAX_TINT)); 296 FillRect(rect); 297 } 298 } 299 300 301 void 302 BStatusBar::SetBarColor(rgb_color color) 303 { 304 fBarColor = color; 305 306 Invalidate(); 307 } 308 309 310 void 311 BStatusBar::SetBarHeight(float barHeight) 312 { 313 float oldHeight = BarHeight(); 314 315 fCustomBarHeight = true; 316 fBarHeight = barHeight; 317 318 if (barHeight == oldHeight) 319 return; 320 321 // resize so that the height fits 322 float width, height; 323 GetPreferredSize(&width, &height); 324 ResizeTo(Bounds().Width(), height); 325 } 326 327 328 void 329 BStatusBar::SetText(const char* string) 330 { 331 _SetTextData(fText, fTextWidth, string, fLabelWidth, false); 332 } 333 334 335 void 336 BStatusBar::SetTrailingText(const char* string) 337 { 338 _SetTextData(fTrailingText, fTrailingTextWidth, string, 339 fTrailingLabelWidth, true); 340 } 341 342 343 void 344 BStatusBar::SetMaxValue(float max) 345 { 346 // R5 and/or Zeta's SetMaxValue does not trigger an invalidate here. 347 // this is probably not ideal behavior, but it does break apps in some cases 348 // as observed with SpaceMonitor. 349 // TODO: revisit this when we break binary compatibility 350 fMax = max; 351 } 352 353 354 void 355 BStatusBar::Update(float delta, const char* text, const char* trailingText) 356 { 357 BStatusBar::SetTo(fCurrent + delta, text, trailingText); 358 } 359 360 361 void 362 BStatusBar::SetTo(float value, const char* text, const char* trailingText) 363 { 364 SetText(text); 365 SetTrailingText(trailingText); 366 367 if (value > fMax) 368 value = fMax; 369 else if (value < 0.0f) 370 value = 0.0f; 371 if (value == fCurrent) 372 return; 373 374 BRect barFrame = _BarFrame(); 375 float oldPosition = _BarPosition(barFrame); 376 377 fCurrent = value; 378 float newPosition = _BarPosition(barFrame); 379 380 // update only the part of the status bar with actual changes 381 382 if (oldPosition == newPosition) 383 return; 384 385 BRect update = barFrame; 386 update.InsetBy(-1, -1); 387 // TODO: this shouldn't be necessary, investigate - app_server bug?! 388 389 if (oldPosition < newPosition) { 390 update.left = max_c(oldPosition - 1, update.left); 391 update.right = newPosition; 392 } else { 393 update.left = max_c(newPosition - 1, update.left); 394 update.right = oldPosition; 395 } 396 397 Invalidate(update); 398 } 399 400 401 void 402 BStatusBar::Reset(const char *label, const char *trailingLabel) 403 { 404 // Reset replaces the label and trailing label with copies of the 405 // strings passed as arguments. If either argument is NULL, the 406 // label or trailing label will be deleted and erased. 407 _SetTextData(fLabel, fLabelWidth, label, 0.0f, false); 408 _SetTextData(fTrailingLabel, fTrailingLabelWidth, trailingLabel, 0.0f, true); 409 410 // Reset deletes and erases any text or trailing text 411 _SetTextData(fText, fTextWidth, NULL, fLabelWidth, false); 412 _SetTextData(fTrailingText, fTrailingTextWidth, NULL, fTrailingLabelWidth, true); 413 414 fCurrent = 0.0f; 415 fMax = 100.0f; 416 417 Invalidate(); 418 } 419 420 421 float 422 BStatusBar::CurrentValue() const 423 { 424 return fCurrent; 425 } 426 427 428 float 429 BStatusBar::MaxValue() const 430 { 431 return fMax; 432 } 433 434 435 rgb_color 436 BStatusBar::BarColor() const 437 { 438 return fBarColor; 439 } 440 441 442 float 443 BStatusBar::BarHeight() const 444 { 445 if (!fCustomBarHeight && fBarHeight == -1.0f) { 446 // the default bar height is as height as the label 447 font_height fontHeight; 448 GetFontHeight(&fontHeight); 449 const_cast<BStatusBar *>(this)->fBarHeight = fontHeight.ascent 450 + fontHeight.descent + 5.0f; 451 } 452 453 return fBarHeight; 454 } 455 456 457 const char * 458 BStatusBar::Text() const 459 { 460 return fText.String(); 461 } 462 463 464 const char * 465 BStatusBar::TrailingText() const 466 { 467 return fTrailingText.String(); 468 } 469 470 471 const char * 472 BStatusBar::Label() const 473 { 474 return fLabel.String(); 475 } 476 477 478 const char * 479 BStatusBar::TrailingLabel() const 480 { 481 return fTrailingLabel.String(); 482 } 483 484 485 void 486 BStatusBar::MouseDown(BPoint point) 487 { 488 BView::MouseDown(point); 489 } 490 491 492 void 493 BStatusBar::MouseUp(BPoint point) 494 { 495 BView::MouseUp(point); 496 } 497 498 499 void 500 BStatusBar::WindowActivated(bool state) 501 { 502 BView::WindowActivated(state); 503 } 504 505 506 void 507 BStatusBar::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 508 { 509 BView::MouseMoved(point, transit, message); 510 } 511 512 513 void 514 BStatusBar::DetachedFromWindow() 515 { 516 BView::DetachedFromWindow(); 517 } 518 519 520 void 521 BStatusBar::FrameMoved(BPoint newPosition) 522 { 523 BView::FrameMoved(newPosition); 524 } 525 526 527 void 528 BStatusBar::FrameResized(float newWidth, float newHeight) 529 { 530 BView::FrameResized(newWidth, newHeight); 531 Invalidate(); 532 } 533 534 535 BHandler * 536 BStatusBar::ResolveSpecifier(BMessage* message, int32 index, 537 BMessage* specifier, int32 what, const char *property) 538 { 539 return BView::ResolveSpecifier(message, index, specifier, what, property); 540 } 541 542 543 void 544 BStatusBar::ResizeToPreferred() 545 { 546 BView::ResizeToPreferred(); 547 } 548 549 550 void 551 BStatusBar::GetPreferredSize(float* _width, float* _height) 552 { 553 if (_width) { 554 // AttachedToWindow() might not have been called yet 555 *_width = ceilf(StringWidth(fLabel.String())) 556 + ceilf(StringWidth(fTrailingLabel.String())) 557 + ceilf(StringWidth(fText.String())) 558 + ceilf(StringWidth(fTrailingText.String())) 559 + 5.0f; 560 } 561 562 if (_height) { 563 font_height fontHeight; 564 GetFontHeight(&fontHeight); 565 566 *_height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f + BarHeight(); 567 } 568 } 569 570 571 void 572 BStatusBar::MakeFocus(bool state) 573 { 574 BView::MakeFocus(state); 575 } 576 577 578 void 579 BStatusBar::AllAttached() 580 { 581 BView::AllAttached(); 582 } 583 584 585 void 586 BStatusBar::AllDetached() 587 { 588 BView::AllDetached(); 589 } 590 591 592 status_t 593 BStatusBar::GetSupportedSuites(BMessage* data) 594 { 595 return BView::GetSupportedSuites(data); 596 } 597 598 599 BSize 600 BStatusBar::MinSize() 601 { 602 float width, height; 603 GetPreferredSize(&width, &height); 604 605 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(width, height)); 606 } 607 608 609 BSize 610 BStatusBar::MaxSize() 611 { 612 float width, height; 613 GetPreferredSize(&width, &height); 614 615 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 616 BSize(B_SIZE_UNLIMITED, height)); 617 } 618 619 620 BSize 621 BStatusBar::PreferredSize() 622 { 623 float width, height; 624 GetPreferredSize(&width, &height); 625 626 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(width, height)); 627 } 628 629 630 status_t 631 BStatusBar::Perform(perform_code d, void* arg) 632 { 633 return BView::Perform(d, arg); 634 } 635 636 637 extern "C" void 638 _ReservedStatusBar1__10BStatusBar(BStatusBar* self, float value, 639 const char* text, const char* trailingText) 640 { 641 self->BStatusBar::SetTo(value, text, trailingText); 642 } 643 644 645 void BStatusBar::_ReservedStatusBar2() {} 646 void BStatusBar::_ReservedStatusBar3() {} 647 void BStatusBar::_ReservedStatusBar4() {} 648 649 650 BStatusBar & 651 BStatusBar::operator=(const BStatusBar& other) 652 { 653 return *this; 654 } 655 656 657 void 658 BStatusBar::_SetTextData(BString& text, float& width, const char* source, 659 float position, bool rightAligned) 660 { 661 if (source == NULL) 662 source = ""; 663 664 // If there were no changes, we don't have to do anything 665 if (text == source) 666 return; 667 668 float oldWidth = width; 669 text = source; 670 width = ceilf(StringWidth(text.String())); 671 672 // determine which part of the view needs an update 673 674 // if a label changed, we also need to update the texts 675 float invalidateWidth = max_c(width, oldWidth); 676 if (&text == &fLabel) 677 invalidateWidth += fTextWidth; 678 if (&text == &fTrailingLabel) 679 invalidateWidth += fTrailingTextWidth; 680 681 if (rightAligned) 682 position = Bounds().Width() - position - invalidateWidth; 683 684 font_height fontHeight; 685 GetFontHeight(&fontHeight); 686 687 Invalidate(BRect(position, 1.0f, position + invalidateWidth, 688 ceilf(fontHeight.ascent + fontHeight.descent))); 689 } 690 691 692 /*! 693 Returns the inner bar frame without the surrounding bevel. 694 */ 695 BRect 696 BStatusBar::_BarFrame(const font_height* fontHeight) const 697 { 698 float top; 699 if (fontHeight == NULL) { 700 font_height height; 701 GetFontHeight(&height); 702 top = ceilf(height.ascent + height.descent) + 6.0f; 703 } else 704 top = ceilf(fontHeight->ascent + fontHeight->descent) + 6.0f; 705 706 return BRect(2.0f, top, Bounds().right - 2.0f, top + BarHeight() - 4.0f); 707 } 708 709 710 float 711 BStatusBar::_BarPosition(const BRect& barFrame) const 712 { 713 if (fCurrent == 0.0f) 714 return barFrame.left - 1.0f; 715 716 return roundf(barFrame.left + ceilf(fCurrent * barFrame.Width() / fMax)); 717 } 718 719