1 /* 2 * Copyright 2001-2009, Stephan Aßmus <superstippi@gmx.de> 3 * Copyright 2001-2009, Ingo Weinhold <ingo_weinhold@gmx.de> 4 * All rights reserved. Distributed under the terms of the MIT license. 5 */ 6 7 8 #include "SeparatorView.h" 9 10 #include <new> 11 12 #include <math.h> 13 #include <stdio.h> 14 15 #include <ControlLook.h> 16 #include <LayoutUtils.h> 17 #include <Region.h> 18 19 20 static const float kMinBorderLength = 5.0f; 21 22 23 // TODO: Actually implement alignment support! 24 // TODO: More testing, especially archiving. 25 26 27 BSeparatorView::BSeparatorView(orientation orientation, border_style border) 28 : 29 BView(NULL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 30 { 31 _Init(NULL, NULL, orientation, BAlignment(B_ALIGN_HORIZONTAL_CENTER, 32 B_ALIGN_VERTICAL_CENTER), border); 33 } 34 35 36 BSeparatorView::BSeparatorView(const char* name, const char* label, 37 orientation orientation, border_style border, const BAlignment& alignment) 38 : 39 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 40 { 41 _Init(label, NULL, orientation, alignment, border); 42 } 43 44 45 BSeparatorView::BSeparatorView(const char* name, BView* labelView, 46 orientation orientation, border_style border, const BAlignment& alignment) 47 : 48 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 49 { 50 _Init(NULL, labelView, orientation, alignment, border); 51 } 52 53 54 BSeparatorView::BSeparatorView(const char* label, 55 orientation orientation, border_style border, const BAlignment& alignment) 56 : 57 BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 58 { 59 _Init(label, NULL, orientation, alignment, border); 60 } 61 62 63 BSeparatorView::BSeparatorView(BView* labelView, 64 orientation orientation, border_style border, const BAlignment& alignment) 65 : 66 BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE) 67 { 68 _Init(NULL, labelView, orientation, alignment, border); 69 } 70 71 72 BSeparatorView::BSeparatorView(BMessage* archive) 73 : 74 BView(archive), 75 fLabel(), 76 fLabelView(NULL), 77 fOrientation(B_HORIZONTAL), 78 fAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, 79 B_ALIGN_VERTICAL_CENTER)), 80 fBorder(B_FANCY_BORDER) 81 { 82 // NULL archives will be caught by BView. 83 84 const char* label; 85 if (archive->FindString("_labelview", &label) == B_OK) { 86 fLabelView = FindView(label); 87 } else if (archive->FindString("_label", &label) == B_OK) { 88 fLabel.SetTo(label); 89 } 90 91 int32 orientation; 92 if (archive->FindInt32("_orientation", &orientation) == B_OK) 93 fOrientation = (enum orientation)orientation; 94 95 int32 hAlignment; 96 int32 vAlignment; 97 if (archive->FindInt32("_halignment", &hAlignment) == B_OK 98 && archive->FindInt32("_valignment", &vAlignment) == B_OK) { 99 fAlignment.horizontal = (alignment)hAlignment; 100 fAlignment.vertical = (vertical_alignment)vAlignment; 101 } 102 103 int32 borderStyle; 104 if (archive->FindInt32("_border", &borderStyle) != B_OK) 105 fBorder = (border_style)borderStyle; 106 } 107 108 109 BSeparatorView::~BSeparatorView() 110 { 111 } 112 113 114 // #pragma mark - Archiving 115 116 117 BArchivable* 118 BSeparatorView::Instantiate(BMessage* archive) 119 { 120 if (validate_instantiation(archive, "BSeparatorView")) 121 return new (std::nothrow) BSeparatorView(archive); 122 123 return NULL; 124 } 125 126 127 status_t 128 BSeparatorView::Archive(BMessage* into, bool deep) const 129 { 130 // TODO: Test this. 131 status_t result = BView::Archive(into, deep); 132 if (result != B_OK) 133 return result; 134 135 if (fLabelView != NULL) 136 result = into->AddString("_labelview", fLabelView->Name()); 137 else 138 result = into->AddString("_label", fLabel.String()); 139 140 if (result == B_OK) 141 result = into->AddInt32("_orientation", fOrientation); 142 143 if (result == B_OK) 144 result = into->AddInt32("_halignment", fAlignment.horizontal); 145 146 if (result == B_OK) 147 result = into->AddInt32("_valignment", fAlignment.vertical); 148 149 if (result == B_OK) 150 result = into->AddInt32("_border", fBorder); 151 152 return result; 153 } 154 155 156 // #pragma mark - 157 158 159 void 160 BSeparatorView::Draw(BRect updateRect) 161 { 162 rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); 163 rgb_color highColor = ui_color(B_PANEL_TEXT_COLOR); 164 165 if (BView* parent = Parent()) { 166 if (parent->ViewColor() != B_TRANSPARENT_COLOR) { 167 bgColor = parent->ViewColor(); 168 highColor = parent->HighColor(); 169 } 170 } 171 172 BRect labelBounds; 173 if (fLabelView != NULL) { 174 labelBounds = fLabelView->Frame(); 175 } else if (fLabel.CountChars() > 0) { 176 labelBounds = _MaxLabelBounds(); 177 float labelWidth = StringWidth(fLabel.String()); 178 if (fOrientation == B_HORIZONTAL) { 179 switch (fAlignment.horizontal) { 180 case B_ALIGN_LEFT: 181 default: 182 labelBounds.right = labelBounds.left + labelWidth; 183 break; 184 185 case B_ALIGN_RIGHT: 186 labelBounds.left = labelBounds.right - labelWidth; 187 break; 188 189 case B_ALIGN_CENTER: 190 labelBounds.left = (labelBounds.left + labelBounds.right 191 - labelWidth) / 2; 192 labelBounds.right = labelBounds.left + labelWidth; 193 break; 194 } 195 } else { 196 switch (fAlignment.vertical) { 197 case B_ALIGN_TOP: 198 default: 199 labelBounds.bottom = labelBounds.top + labelWidth; 200 break; 201 202 case B_ALIGN_BOTTOM: 203 labelBounds.top = labelBounds.bottom - labelWidth; 204 break; 205 206 case B_ALIGN_MIDDLE: 207 labelBounds.top = (labelBounds.top + labelBounds.bottom 208 - labelWidth) / 2; 209 labelBounds.bottom = labelBounds.top + labelWidth; 210 break; 211 } 212 } 213 } 214 215 BRect bounds = Bounds(); 216 BRegion region(bounds); 217 if (labelBounds.IsValid()) { 218 region.Exclude(labelBounds); 219 ConstrainClippingRegion(®ion); 220 } 221 222 float borderSize = _BorderSize(); 223 if (borderSize > 0.0f) { 224 if (fOrientation == B_HORIZONTAL) { 225 bounds.top = floorf((bounds.top + bounds.bottom + 1 - borderSize) 226 / 2); 227 bounds.bottom = bounds.top + borderSize - 1; 228 region.Exclude(bounds); 229 be_control_look->DrawBorder(this, bounds, updateRect, bgColor, 230 fBorder, 0, BControlLook::B_TOP_BORDER); 231 } else { 232 bounds.left = floorf((bounds.left + bounds.right + 1 - borderSize) 233 / 2); 234 bounds.right = bounds.left + borderSize - 1; 235 region.Exclude(bounds); 236 be_control_look->DrawBorder(this, bounds, updateRect, bgColor, 237 fBorder, 0, BControlLook::B_LEFT_BORDER); 238 } 239 if (labelBounds.IsValid()) 240 region.Include(labelBounds); 241 242 ConstrainClippingRegion(®ion); 243 } 244 245 SetLowColor(bgColor); 246 FillRect(updateRect, B_SOLID_LOW); 247 248 if (fLabel.CountChars() > 0) { 249 font_height fontHeight; 250 GetFontHeight(&fontHeight); 251 252 SetHighColor(highColor); 253 254 if (fOrientation == B_HORIZONTAL) { 255 DrawString(fLabel.String(), BPoint(labelBounds.left, 256 labelBounds.top + ceilf(fontHeight.ascent))); 257 } else { 258 DrawString(fLabel.String(), BPoint(labelBounds.left 259 + ceilf(fontHeight.ascent), labelBounds.bottom)); 260 } 261 } 262 } 263 264 265 void 266 BSeparatorView::GetPreferredSize(float* _width, float* _height) 267 { 268 float width = 0.0f; 269 float height = 0.0f; 270 271 if (fLabelView != NULL) { 272 fLabelView->GetPreferredSize(&width, &height); 273 } else if (fLabel.CountChars() > 0) { 274 width = StringWidth(fLabel.String()); 275 font_height fontHeight; 276 GetFontHeight(&fontHeight); 277 height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent); 278 if (fOrientation == B_VERTICAL) { 279 // swap width and height 280 float temp = height; 281 height = width; 282 width = temp; 283 } 284 } 285 286 float borderSize = _BorderSize(); 287 288 // Add some room for the border 289 if (fOrientation == B_HORIZONTAL) { 290 width += kMinBorderLength * 2.0f; 291 height = max_c(height, borderSize - 1.0f); 292 } else { 293 height += kMinBorderLength * 2.0f; 294 width = max_c(width, borderSize - 1.0f); 295 } 296 297 if (_width != NULL) 298 *_width = width; 299 300 if (_height != NULL) 301 *_height = height; 302 } 303 304 305 BSize 306 BSeparatorView::MinSize() 307 { 308 BSize size; 309 GetPreferredSize(&size.width, &size.height); 310 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 311 } 312 313 314 BSize 315 BSeparatorView::MaxSize() 316 { 317 BSize size(MinSize()); 318 if (fOrientation == B_HORIZONTAL) 319 size.width = B_SIZE_UNLIMITED; 320 else 321 size.height = B_SIZE_UNLIMITED; 322 323 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); 324 } 325 326 327 BSize 328 BSeparatorView::PreferredSize() 329 { 330 BSize size; 331 GetPreferredSize(&size.width, &size.height); 332 333 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); 334 } 335 336 337 // #pragma mark - 338 339 340 void 341 BSeparatorView::SetOrientation(orientation orientation) 342 { 343 if (orientation == fOrientation) 344 return; 345 346 fOrientation = orientation; 347 348 BFont font; 349 GetFont(&font); 350 if (fOrientation == B_VERTICAL) 351 font.SetRotation(90.0f); 352 353 SetFont(&font); 354 355 Invalidate(); 356 InvalidateLayout(); 357 } 358 359 360 void 361 BSeparatorView::SetAlignment(const BAlignment& aligment) 362 { 363 if (aligment == fAlignment) 364 return; 365 366 fAlignment = aligment; 367 368 Invalidate(); 369 InvalidateLayout(); 370 } 371 372 373 void 374 BSeparatorView::SetBorderStyle(border_style border) 375 { 376 if (border == fBorder) 377 return; 378 379 fBorder = border; 380 381 Invalidate(); 382 } 383 384 385 void 386 BSeparatorView::SetLabel(const char* label) 387 { 388 if (label == NULL) 389 label = ""; 390 391 if (fLabel == label) 392 return; 393 394 fLabel.SetTo(label); 395 396 Invalidate(); 397 InvalidateLayout(); 398 } 399 400 401 void 402 BSeparatorView::SetLabel(BView* view, bool deletePrevious) 403 { 404 if (fLabelView == view) 405 return; 406 407 if (fLabelView != NULL) { 408 fLabelView->RemoveSelf(); 409 if (deletePrevious) 410 delete fLabelView; 411 } 412 413 fLabelView = view; 414 415 if (fLabelView != NULL) 416 AddChild(view); 417 } 418 419 420 status_t 421 BSeparatorView::Perform(perform_code code, void* data) 422 { 423 return BView::Perform(code, data); 424 } 425 426 427 // #pragma mark - protected/private 428 429 430 void 431 BSeparatorView::DoLayout() 432 { 433 BView::DoLayout(); 434 435 if (fLabelView == NULL) 436 return; 437 438 BRect bounds = _MaxLabelBounds(); 439 BRect frame = BLayoutUtils::AlignInFrame(bounds, fLabelView->MaxSize(), 440 fAlignment); 441 442 fLabelView->MoveTo(frame.LeftTop()); 443 fLabelView->ResizeTo(frame.Width(), frame.Height()); 444 } 445 446 447 void 448 BSeparatorView::_Init(const char* label, BView* labelView, 449 orientation orientation, BAlignment alignment, border_style border) 450 { 451 fLabel = ""; 452 fLabelView = NULL; 453 454 fOrientation = B_HORIZONTAL; 455 fAlignment = alignment; 456 fBorder = border; 457 458 SetFont(be_bold_font); 459 SetViewColor(B_TRANSPARENT_32_BIT); 460 461 SetLabel(label); 462 SetLabel(labelView, true); 463 SetOrientation(orientation); 464 } 465 466 467 float 468 BSeparatorView::_BorderSize() const 469 { 470 // TODO: Get these values from the BControlLook class. 471 switch (fBorder) { 472 case B_PLAIN_BORDER: 473 return 1.0f; 474 475 case B_FANCY_BORDER: 476 return 2.0f; 477 478 case B_NO_BORDER: 479 default: 480 return 0.0f; 481 } 482 } 483 484 485 BRect 486 BSeparatorView::_MaxLabelBounds() const 487 { 488 BRect bounds = Bounds(); 489 if (fOrientation == B_HORIZONTAL) 490 bounds.InsetBy(kMinBorderLength, 0.0f); 491 else 492 bounds.InsetBy(0.0f, kMinBorderLength); 493 494 return bounds; 495 } 496 497 498 // #pragma mark - FBC padding 499 500 501 void BSeparatorView::_ReservedSeparatorView1() {} 502 void BSeparatorView::_ReservedSeparatorView2() {} 503 void BSeparatorView::_ReservedSeparatorView3() {} 504 void BSeparatorView::_ReservedSeparatorView4() {} 505 void BSeparatorView::_ReservedSeparatorView5() {} 506 void BSeparatorView::_ReservedSeparatorView6() {} 507 void BSeparatorView::_ReservedSeparatorView7() {} 508 void BSeparatorView::_ReservedSeparatorView8() {} 509 void BSeparatorView::_ReservedSeparatorView9() {} 510 void BSeparatorView::_ReservedSeparatorView10() {} 511