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 20static const float kMinBorderLength = 5.0f; 21 22 23// TODO: Actually implement alignment support! 24// TODO: More testing, especially archiving. 25 26 27BSeparatorView::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 36BSeparatorView::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 45BSeparatorView::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 54BSeparatorView::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 63BSeparatorView::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 72BSeparatorView::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 109BSeparatorView::~BSeparatorView() 110{ 111} 112 113 114// #pragma mark - Archiving 115 116 117BArchivable* 118BSeparatorView::Instantiate(BMessage* archive) 119{ 120 if (validate_instantiation(archive, "BSeparatorView")) 121 return new (std::nothrow) BSeparatorView(archive); 122 123 return NULL; 124} 125 126 127status_t 128BSeparatorView::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 159void 160BSeparatorView::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 265void 266BSeparatorView::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 305BSize 306BSeparatorView::MinSize() 307{ 308 BSize size; 309 GetPreferredSize(&size.width, &size.height); 310 return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); 311} 312 313 314BSize 315BSeparatorView::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 327BSize 328BSeparatorView::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 340void 341BSeparatorView::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 360void 361BSeparatorView::SetAlignment(const BAlignment& aligment) 362{ 363 if (aligment == fAlignment) 364 return; 365 366 fAlignment = aligment; 367 368 Invalidate(); 369 InvalidateLayout(); 370} 371 372 373void 374BSeparatorView::SetBorderStyle(border_style border) 375{ 376 if (border == fBorder) 377 return; 378 379 fBorder = border; 380 381 Invalidate(); 382} 383 384 385void 386BSeparatorView::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 401void 402BSeparatorView::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 420status_t 421BSeparatorView::Perform(perform_code code, void* data) 422{ 423 return BView::Perform(code, data); 424} 425 426 427// #pragma mark - protected/private 428 429 430void 431BSeparatorView::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 447void 448BSeparatorView::_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 467float 468BSeparatorView::_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 485BRect 486BSeparatorView::_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 501void BSeparatorView::_ReservedSeparatorView1() {} 502void BSeparatorView::_ReservedSeparatorView2() {} 503void BSeparatorView::_ReservedSeparatorView3() {} 504void BSeparatorView::_ReservedSeparatorView4() {} 505void BSeparatorView::_ReservedSeparatorView5() {} 506void BSeparatorView::_ReservedSeparatorView6() {} 507void BSeparatorView::_ReservedSeparatorView7() {} 508void BSeparatorView::_ReservedSeparatorView8() {} 509void BSeparatorView::_ReservedSeparatorView9() {} 510void BSeparatorView::_ReservedSeparatorView10() {} 511