1 /* 2 * Copyright (c) 2001-2006, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stephan Aßmus <superstippi@gmx.de> 8 * DarkWyrm <bpmagic@columbus.rr.com> 9 * Axel Dörfler, axeld@pinc-software.de 10 */ 11 12 13 #include <Box.h> 14 #include <Message.h> 15 #include <Region.h> 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 21 22 BBox::BBox(BRect frame, const char *name, uint32 resizingMode, uint32 flags, 23 border_style border) 24 : BView(frame, name, resizingMode, flags | B_FRAME_EVENTS), 25 fStyle(border) 26 { 27 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 28 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 29 30 _InitObject(); 31 } 32 33 34 BBox::BBox(BMessage *archive) 35 : BView(archive) 36 { 37 _InitObject(archive); 38 } 39 40 41 BBox::~BBox() 42 { 43 _ClearLabel(); 44 } 45 46 47 BArchivable * 48 BBox::Instantiate(BMessage *archive) 49 { 50 if (validate_instantiation(archive, "BBox")) 51 return new BBox(archive); 52 53 return NULL; 54 } 55 56 57 status_t 58 BBox::Archive(BMessage *archive, bool deep) const 59 { 60 status_t ret = BView::Archive(archive, deep); 61 62 if (fLabel && ret == B_OK) 63 ret = archive->AddString("_label", fLabel); 64 65 if (fLabelView && ret == B_OK) 66 ret = archive->AddBool("_lblview", true); 67 68 if (fStyle != B_FANCY_BORDER && ret == B_OK) 69 ret = archive->AddInt32("_style", fStyle); 70 71 return ret; 72 } 73 74 75 void 76 BBox::SetBorder(border_style border) 77 { 78 fStyle = border; 79 80 if (Window() != NULL && LockLooper()) { 81 Invalidate(); 82 UnlockLooper(); 83 } 84 } 85 86 87 border_style 88 BBox::Border() const 89 { 90 return fStyle; 91 } 92 93 94 //! This function is not part of the R5 API and is not yet finalized yet 95 float 96 BBox::TopBorderOffset() 97 { 98 float labelHeight = 0; 99 100 if (fLabel != NULL) 101 labelHeight = fLabelBox->Height(); 102 else if (fLabelView != NULL) 103 labelHeight = fLabelView->Bounds().Height(); 104 105 return ceilf(labelHeight / 2.0f); 106 } 107 108 109 //! This function is not part of the R5 API and is not yet finalized yet 110 BRect 111 BBox::InnerFrame() 112 { 113 float borderSize = Border() == B_FANCY_BORDER ? 2.0f 114 : Border() == B_PLAIN_BORDER ? 1.0f : 0.0f; 115 float labelHeight = 0.0f; 116 117 if (fLabel != NULL) { 118 // fLabelBox doesn't contain the font's descent, but we want it here 119 font_height fontHeight; 120 GetFontHeight(&fontHeight); 121 122 labelHeight = ceilf(fontHeight.ascent + fontHeight.descent); 123 } else if (fLabelView != NULL) 124 labelHeight = fLabelView->Bounds().Height(); 125 126 BRect rect = Bounds().InsetByCopy(borderSize, borderSize); 127 if (labelHeight) 128 rect.top = Bounds().top + labelHeight; 129 130 return rect; 131 } 132 133 134 void 135 BBox::SetLabel(const char *string) 136 { 137 _ClearLabel(); 138 139 if (string) { 140 font_height fontHeight; 141 GetFontHeight(&fontHeight); 142 143 fLabel = strdup(string); 144 145 // leave 6 pixels of the frame, and have a gap of 4 pixels between 146 // the frame and the text on both sides 147 fLabelBox = new BRect(6.0f, 0, StringWidth(string) + 14.0f, 148 ceilf(fontHeight.ascent)); 149 } 150 151 if (Window()) 152 Invalidate(); 153 } 154 155 156 status_t 157 BBox::SetLabel(BView *viewLabel) 158 { 159 _ClearLabel(); 160 161 if (viewLabel) { 162 fLabelView = viewLabel; 163 fLabelView->MoveTo(10.0f, 0.0f); 164 AddChild(fLabelView, ChildAt(0)); 165 } 166 167 if (Window()) 168 Invalidate(); 169 170 return B_OK; 171 } 172 173 174 const char * 175 BBox::Label() const 176 { 177 return fLabel; 178 } 179 180 181 BView * 182 BBox::LabelView() const 183 { 184 return fLabelView; 185 } 186 187 188 void 189 BBox::Draw(BRect updateRect) 190 { 191 PushState(); 192 193 BRect labelBox = BRect(0, 0, 0, 0); 194 if (fLabel != NULL) { 195 labelBox = *fLabelBox; 196 BRegion update(updateRect); 197 update.Exclude(labelBox); 198 199 ConstrainClippingRegion(&update); 200 } else if (fLabelView != NULL) 201 labelBox = fLabelView->Bounds(); 202 203 switch (fStyle) { 204 case B_FANCY_BORDER: 205 _DrawFancy(labelBox); 206 break; 207 208 case B_PLAIN_BORDER: 209 _DrawPlain(labelBox); 210 break; 211 212 default: 213 break; 214 } 215 216 if (fLabel) { 217 ConstrainClippingRegion(NULL); 218 219 font_height fontHeight; 220 GetFontHeight(&fontHeight); 221 222 SetHighColor(0, 0, 0); 223 DrawString(fLabel, BPoint(10.0f, ceilf(fontHeight.ascent))); 224 } 225 226 PopState(); 227 } 228 229 230 void 231 BBox::AttachedToWindow() 232 { 233 if (Parent()) { 234 SetViewColor(Parent()->ViewColor()); 235 SetLowColor(Parent()->ViewColor()); 236 } 237 238 // The box could have been resized in the mean time 239 fBounds = Bounds(); 240 } 241 242 243 void 244 BBox::DetachedFromWindow() 245 { 246 BView::DetachedFromWindow(); 247 } 248 249 250 void 251 BBox::AllAttached() 252 { 253 BView::AllAttached(); 254 } 255 256 257 void 258 BBox::AllDetached() 259 { 260 BView::AllDetached(); 261 } 262 263 264 void 265 BBox::FrameResized(float width, float height) 266 { 267 BRect bounds(Bounds()); 268 269 // invalidate the regions that the app_server did not 270 // (for removing the previous or drawing the new border) 271 if (fStyle != B_NO_BORDER) { 272 273 int32 borderSize = fStyle == B_PLAIN_BORDER ? 0 : 1; 274 275 BRect invalid(bounds); 276 if (fBounds.right < bounds.right) { 277 // enlarging 278 invalid.left = fBounds.right - borderSize; 279 invalid.right = fBounds.right; 280 281 Invalidate(invalid); 282 } else if (fBounds.right > bounds.right) { 283 // shrinking 284 invalid.left = bounds.right - borderSize; 285 286 Invalidate(invalid); 287 } 288 289 invalid = bounds; 290 if (fBounds.bottom < bounds.bottom) { 291 // enlarging 292 invalid.top = fBounds.bottom - borderSize; 293 invalid.bottom = fBounds.bottom; 294 295 Invalidate(invalid); 296 } else if (fBounds.bottom > bounds.bottom) { 297 // shrinking 298 invalid.top = bounds.bottom - borderSize; 299 300 Invalidate(invalid); 301 } 302 } 303 304 fBounds.right = bounds.right; 305 fBounds.bottom = bounds.bottom; 306 } 307 308 309 void 310 BBox::MessageReceived(BMessage *message) 311 { 312 BView::MessageReceived(message); 313 } 314 315 316 void 317 BBox::MouseDown(BPoint point) 318 { 319 BView::MouseDown(point); 320 } 321 322 323 void 324 BBox::MouseUp(BPoint point) 325 { 326 BView::MouseUp(point); 327 } 328 329 330 void 331 BBox::WindowActivated(bool active) 332 { 333 BView::WindowActivated(active); 334 } 335 336 337 void 338 BBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 339 { 340 BView::MouseMoved(point, transit, message); 341 } 342 343 344 void 345 BBox::FrameMoved(BPoint newLocation) 346 { 347 BView::FrameMoved(newLocation); 348 } 349 350 351 BHandler * 352 BBox::ResolveSpecifier(BMessage *message, int32 index, 353 BMessage *specifier, int32 what, 354 const char *property) 355 { 356 return BView::ResolveSpecifier(message, index, specifier, what, property); 357 } 358 359 360 void 361 BBox::ResizeToPreferred() 362 { 363 BView::ResizeToPreferred(); 364 } 365 366 367 void 368 BBox::GetPreferredSize(float *_width, float *_height) 369 { 370 float width, height; 371 bool label = true; 372 373 // acount for label 374 if (fLabelView) { 375 fLabelView->GetPreferredSize(&width, &height); 376 width += 10.0; 377 // the label view is placed 10 pixels from the left 378 } else if (fLabel) { 379 font_height fh; 380 GetFontHeight(&fh); 381 width += ceilf(StringWidth(fLabel)); 382 height += ceilf(fh.ascent + fh.descent); 383 } else { 384 label = false; 385 width = 0; 386 height = 0; 387 } 388 389 // acount for border 390 switch (fStyle) { 391 case B_NO_BORDER: 392 break; 393 case B_PLAIN_BORDER: 394 // label: (1 pixel for border + 1 pixel for padding) * 2 395 // no label: (1 pixel for border) * 2 + 1 pixel for padding 396 width += label ? 4 : 3; 397 // label: 1 pixel for bottom border + 1 pixel for padding 398 // no label: (1 pixel for border) * 2 + 1 pixel for padding 399 height += label ? 2 : 3; 400 break; 401 case B_FANCY_BORDER: 402 // label: (2 pixel for border + 1 pixel for padding) * 2 403 // no label: (2 pixel for border) * 2 + 1 pixel for padding 404 width += label ? 6 : 5; 405 // label: 2 pixel for bottom border + 1 pixel for padding 406 // no label: (2 pixel for border) * 2 + 1 pixel for padding 407 height += label ? 3 : 5; 408 break; 409 } 410 // NOTE: children are ignored, you can use BBox::GetPreferredSize() 411 // to get the minimum size of this object, then add the size 412 // of your child(ren) plus inner padding for the final size 413 414 if (_width) 415 *_width = width; 416 if (_height) 417 *_height = height; 418 } 419 420 421 void 422 BBox::MakeFocus(bool focused) 423 { 424 BView::MakeFocus(focused); 425 } 426 427 428 status_t 429 BBox::GetSupportedSuites(BMessage *message) 430 { 431 return BView::GetSupportedSuites(message); 432 } 433 434 435 status_t 436 BBox::Perform(perform_code d, void *arg) 437 { 438 return BView::Perform(d, arg); 439 } 440 441 442 void BBox::_ReservedBox1() {} 443 void BBox::_ReservedBox2() {} 444 445 446 BBox & 447 BBox::operator=(const BBox &) 448 { 449 return *this; 450 } 451 452 453 void 454 BBox::_InitObject(BMessage* archive) 455 { 456 fBounds = Bounds(); 457 458 fLabel = NULL; 459 fLabelView = NULL; 460 fLabelBox = NULL; 461 462 int32 flags = 0; 463 464 BFont font(be_bold_font); 465 466 if (!archive || !archive->HasString("_fname")) 467 flags = B_FONT_FAMILY_AND_STYLE; 468 469 if (!archive || !archive->HasFloat("_fflt")) 470 flags |= B_FONT_SIZE; 471 472 if (flags != 0) 473 SetFont(&font, flags); 474 475 if (archive != NULL) { 476 const char *string; 477 if (archive->FindString("_label", &string) == B_OK) 478 SetLabel(string); 479 480 bool fancy; 481 int32 style; 482 483 if (archive->FindBool("_style", &fancy) == B_OK) 484 fStyle = fancy ? B_FANCY_BORDER : B_PLAIN_BORDER; 485 else if (archive->FindInt32("_style", &style) == B_OK) 486 fStyle = (border_style)style; 487 488 bool hasLabelView; 489 if (archive->FindBool("_lblview", &hasLabelView) == B_OK) 490 fLabelView = ChildAt(0); 491 } 492 } 493 494 495 void 496 BBox::_DrawPlain(BRect labelBox) 497 { 498 BRect rect = Bounds(); 499 rect.top += TopBorderOffset(); 500 501 rgb_color shadow = tint_color(ViewColor(), B_DARKEN_3_TINT); 502 503 if (rect.Height() == 0.0 || rect.Width() == 0.0) { 504 // used as separator 505 SetHighColor(shadow); 506 StrokeLine(rect.LeftTop(),rect.RightBottom()); 507 } else { 508 // used as box 509 rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT); 510 BeginLineArray(4); 511 AddLine(BPoint(rect.left, rect.bottom), 512 BPoint(rect.left, rect.top), light); 513 AddLine(BPoint(rect.left + 1.0f, rect.top), 514 BPoint(rect.right, rect.top), light); 515 AddLine(BPoint(rect.left + 1.0f, rect.bottom), 516 BPoint(rect.right, rect.bottom), shadow); 517 AddLine(BPoint(rect.right, rect.bottom - 1.0f), 518 BPoint(rect.right, rect.top + 1.0f), shadow); 519 EndLineArray(); 520 } 521 } 522 523 524 void 525 BBox::_DrawFancy(BRect labelBox) 526 { 527 BRect rect = Bounds(); 528 rect.top += TopBorderOffset(); 529 530 rgb_color light = tint_color(ViewColor(), B_LIGHTEN_MAX_TINT); 531 rgb_color shadow = tint_color(ViewColor(), B_DARKEN_3_TINT); 532 533 if (rect.Height() == 1.0) { 534 // used as horizontal separator 535 BeginLineArray(2); 536 AddLine(BPoint(rect.left, rect.top), 537 BPoint(rect.right, rect.top), shadow); 538 AddLine(BPoint(rect.left, rect.bottom), 539 BPoint(rect.right, rect.bottom), light); 540 EndLineArray(); 541 } else if (rect.Width() == 1.0) { 542 // used as vertical separator 543 BeginLineArray(2); 544 AddLine(BPoint(rect.left, rect.top), 545 BPoint(rect.left, rect.bottom), shadow); 546 AddLine(BPoint(rect.right, rect.top), 547 BPoint(rect.right, rect.bottom), light); 548 EndLineArray(); 549 } else { 550 // used as box 551 BeginLineArray(8); 552 AddLine(BPoint(rect.left, rect.bottom - 1.0), 553 BPoint(rect.left, rect.top), shadow); 554 AddLine(BPoint(rect.left + 1.0, rect.top), 555 BPoint(rect.right - 1.0, rect.top), shadow); 556 AddLine(BPoint(rect.left, rect.bottom), 557 BPoint(rect.right, rect.bottom), light); 558 AddLine(BPoint(rect.right, rect.bottom - 1.0), 559 BPoint(rect.right, rect.top), light); 560 561 rect.InsetBy(1.0, 1.0); 562 563 AddLine(BPoint(rect.left, rect.bottom - 1.0), 564 BPoint(rect.left, rect.top), light); 565 AddLine(BPoint(rect.left + 1.0, rect.top), 566 BPoint(rect.right - 1.0, rect.top), light); 567 AddLine(BPoint(rect.left, rect.bottom), 568 BPoint(rect.right, rect.bottom), shadow); 569 AddLine(BPoint(rect.right, rect.bottom - 1.0), 570 BPoint(rect.right, rect.top), shadow); 571 EndLineArray(); 572 } 573 } 574 575 576 void 577 BBox::_ClearLabel() 578 { 579 fBounds.top = 0; 580 581 if (fLabel) { 582 delete fLabelBox; 583 free(fLabel); 584 fLabel = NULL; 585 fLabelBox = NULL; 586 } else if (fLabelView) { 587 fLabelView->RemoveSelf(); 588 delete fLabelView; 589 fLabelView = NULL; 590 } 591 } 592