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 fMax = max; 347 Invalidate(_BarFrame()); 348 } 349 350 351 void 352 BStatusBar::Update(float delta, const char* text, const char* trailingText) 353 { 354 BStatusBar::SetTo(fCurrent + delta, text, trailingText); 355 } 356 357 358 void 359 BStatusBar::SetTo(float value, const char* text, const char* trailingText) 360 { 361 SetText(text); 362 SetTrailingText(trailingText); 363 364 if (value > fMax) 365 value = fMax; 366 else if (value < 0.0f) 367 value = 0.0f; 368 if (value == fCurrent) 369 return; 370 371 BRect barFrame = _BarFrame(); 372 float oldPosition = _BarPosition(barFrame); 373 374 fCurrent = value; 375 float newPosition = _BarPosition(barFrame); 376 377 // update only the part of the status bar with actual changes 378 379 if (oldPosition == newPosition) 380 return; 381 382 BRect update = barFrame; 383 update.InsetBy(-1, -1); 384 // TODO: this shouldn't be necessary, investigate - app_server bug?! 385 386 if (oldPosition < newPosition) { 387 update.left = max_c(oldPosition - 1, update.left); 388 update.right = newPosition; 389 } else { 390 update.left = max_c(newPosition - 1, update.left); 391 update.right = oldPosition; 392 } 393 394 Invalidate(update); 395 } 396 397 398 void 399 BStatusBar::Reset(const char *label, const char *trailingLabel) 400 { 401 // Reset replaces the label and trailing label with copies of the 402 // strings passed as arguments. If either argument is NULL, the 403 // label or trailing label will be deleted and erased. 404 _SetTextData(fLabel, fLabelWidth, label, 0.0f, false); 405 _SetTextData(fTrailingLabel, fTrailingLabelWidth, trailingLabel, 0.0f, true); 406 407 // Reset deletes and erases any text or trailing text 408 _SetTextData(fText, fTextWidth, NULL, fLabelWidth, false); 409 _SetTextData(fTrailingText, fTrailingTextWidth, NULL, fTrailingLabelWidth, true); 410 411 fCurrent = 0.0f; 412 fMax = 100.0f; 413 414 Invalidate(); 415 } 416 417 418 float 419 BStatusBar::CurrentValue() const 420 { 421 return fCurrent; 422 } 423 424 425 float 426 BStatusBar::MaxValue() const 427 { 428 return fMax; 429 } 430 431 432 rgb_color 433 BStatusBar::BarColor() const 434 { 435 return fBarColor; 436 } 437 438 439 float 440 BStatusBar::BarHeight() const 441 { 442 if (!fCustomBarHeight && fBarHeight == -1.0f) { 443 // the default bar height is as height as the label 444 font_height fontHeight; 445 GetFontHeight(&fontHeight); 446 const_cast<BStatusBar *>(this)->fBarHeight = fontHeight.ascent 447 + fontHeight.descent + 5.0f; 448 } 449 450 return fBarHeight; 451 } 452 453 454 const char * 455 BStatusBar::Text() const 456 { 457 return fText.String(); 458 } 459 460 461 const char * 462 BStatusBar::TrailingText() const 463 { 464 return fTrailingText.String(); 465 } 466 467 468 const char * 469 BStatusBar::Label() const 470 { 471 return fLabel.String(); 472 } 473 474 475 const char * 476 BStatusBar::TrailingLabel() const 477 { 478 return fTrailingLabel.String(); 479 } 480 481 482 void 483 BStatusBar::MouseDown(BPoint point) 484 { 485 BView::MouseDown(point); 486 } 487 488 489 void 490 BStatusBar::MouseUp(BPoint point) 491 { 492 BView::MouseUp(point); 493 } 494 495 496 void 497 BStatusBar::WindowActivated(bool state) 498 { 499 BView::WindowActivated(state); 500 } 501 502 503 void 504 BStatusBar::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 505 { 506 BView::MouseMoved(point, transit, message); 507 } 508 509 510 void 511 BStatusBar::DetachedFromWindow() 512 { 513 BView::DetachedFromWindow(); 514 } 515 516 517 void 518 BStatusBar::FrameMoved(BPoint newPosition) 519 { 520 BView::FrameMoved(newPosition); 521 } 522 523 524 void 525 BStatusBar::FrameResized(float newWidth, float newHeight) 526 { 527 BView::FrameResized(newWidth, newHeight); 528 Invalidate(); 529 } 530 531 532 BHandler * 533 BStatusBar::ResolveSpecifier(BMessage* message, int32 index, 534 BMessage* specifier, int32 what, const char *property) 535 { 536 return BView::ResolveSpecifier(message, index, specifier, what, property); 537 } 538 539 540 void 541 BStatusBar::ResizeToPreferred() 542 { 543 BView::ResizeToPreferred(); 544 } 545 546 547 void 548 BStatusBar::GetPreferredSize(float* _width, float* _height) 549 { 550 if (_width) { 551 // AttachedToWindow() might not have been called yet 552 *_width = ceilf(StringWidth(fLabel.String())) 553 + ceilf(StringWidth(fTrailingLabel.String())) 554 + ceilf(StringWidth(fText.String())) 555 + ceilf(StringWidth(fTrailingText.String())) 556 + 5.0f; 557 } 558 559 if (_height) { 560 font_height fontHeight; 561 GetFontHeight(&fontHeight); 562 563 *_height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f + BarHeight(); 564 } 565 } 566 567 568 void 569 BStatusBar::MakeFocus(bool state) 570 { 571 BView::MakeFocus(state); 572 } 573 574 575 void 576 BStatusBar::AllAttached() 577 { 578 BView::AllAttached(); 579 } 580 581 582 void 583 BStatusBar::AllDetached() 584 { 585 BView::AllDetached(); 586 } 587 588 589 status_t 590 BStatusBar::GetSupportedSuites(BMessage* data) 591 { 592 return BView::GetSupportedSuites(data); 593 } 594 595 596 BSize 597 BStatusBar::MinSize() 598 { 599 float width, height; 600 GetPreferredSize(&width, &height); 601 602 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(width, height)); 603 } 604 605 606 BSize 607 BStatusBar::MaxSize() 608 { 609 float width, height; 610 GetPreferredSize(&width, &height); 611 612 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 613 BSize(B_SIZE_UNLIMITED, height)); 614 } 615 616 617 BSize 618 BStatusBar::PreferredSize() 619 { 620 float width, height; 621 GetPreferredSize(&width, &height); 622 623 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), BSize(width, height)); 624 } 625 626 627 status_t 628 BStatusBar::Perform(perform_code d, void* arg) 629 { 630 return BView::Perform(d, arg); 631 } 632 633 634 extern "C" void 635 _ReservedStatusBar1__10BStatusBar(BStatusBar* self, float value, 636 const char* text, const char* trailingText) 637 { 638 self->BStatusBar::SetTo(value, text, trailingText); 639 } 640 641 642 void BStatusBar::_ReservedStatusBar2() {} 643 void BStatusBar::_ReservedStatusBar3() {} 644 void BStatusBar::_ReservedStatusBar4() {} 645 646 647 BStatusBar & 648 BStatusBar::operator=(const BStatusBar& other) 649 { 650 return *this; 651 } 652 653 654 void 655 BStatusBar::_SetTextData(BString& text, float& width, const char* source, 656 float position, bool rightAligned) 657 { 658 if (source == NULL) 659 source = ""; 660 661 // If there were no changes, we don't have to do anything 662 if (text == source) 663 return; 664 665 float oldWidth = width; 666 text = source; 667 width = ceilf(StringWidth(text.String())); 668 669 // determine which part of the view needs an update 670 671 // if a label changed, we also need to update the texts 672 float invalidateWidth = max_c(width, oldWidth); 673 if (&text == &fLabel) 674 invalidateWidth += fTextWidth; 675 if (&text == &fTrailingLabel) 676 invalidateWidth += fTrailingTextWidth; 677 678 if (rightAligned) 679 position = Bounds().Width() - position - invalidateWidth; 680 681 font_height fontHeight; 682 GetFontHeight(&fontHeight); 683 684 Invalidate(BRect(position, 1.0f, position + invalidateWidth, 685 ceilf(fontHeight.ascent + fontHeight.descent))); 686 } 687 688 689 /*! 690 Returns the inner bar frame without the surrounding bevel. 691 */ 692 BRect 693 BStatusBar::_BarFrame(const font_height* fontHeight) const 694 { 695 float top; 696 if (fontHeight == NULL) { 697 font_height height; 698 GetFontHeight(&height); 699 top = ceilf(height.ascent + height.descent) + 6.0f; 700 } else 701 top = ceilf(fontHeight->ascent + fontHeight->descent) + 6.0f; 702 703 return BRect(2.0f, top, Bounds().right - 2.0f, top + BarHeight() - 4.0f); 704 } 705 706 707 float 708 BStatusBar::_BarPosition(const BRect& barFrame) const 709 { 710 if (fCurrent == 0.0f) 711 return barFrame.left - 1.0f; 712 713 return roundf(barFrame.left + ceilf(fCurrent * barFrame.Width() / fMax)); 714 } 715 716