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