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