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