1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2009, Stephan Aßmus, superstippi@gmx.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "HeaderView.h" 9 10 #include <stdio.h> 11 12 #include <algorithm> 13 #include <new> 14 15 #include <ControlLook.h> 16 #include <LayoutUtils.h> 17 18 19 // #pragma mark - HeaderRenderer 20 21 22 HeaderRenderer::~HeaderRenderer() 23 { 24 } 25 26 27 void 28 HeaderRenderer::DrawHeaderBackground(BView* view, BRect frame, BRect updateRect, 29 uint32 flags) 30 { 31 BRect bgRect = frame; 32 33 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 34 view->SetHighColor(tint_color(base, B_DARKEN_2_TINT)); 35 view->StrokeLine(bgRect.LeftBottom(), bgRect.RightBottom()); 36 37 bgRect.bottom--; 38 bgRect.right--; 39 40 // if ((flags & CLICKED) != 0) 41 // base = tint_color(base, B_DARKEN_1_TINT); 42 43 be_control_look->DrawButtonBackground(view, bgRect, updateRect, base, 0, 44 BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER); 45 46 view->StrokeLine(frame.RightTop(), frame.RightBottom()); 47 } 48 49 50 // #pragma mark - DefaultHeaderRenderer 51 52 53 DefaultHeaderRenderer::DefaultHeaderRenderer() 54 { 55 } 56 57 58 DefaultHeaderRenderer::~DefaultHeaderRenderer() 59 { 60 } 61 62 63 float 64 DefaultHeaderRenderer::HeaderHeight(BView* view, const Header* header) 65 { 66 BVariant value; 67 if (!header->GetValue(value)) 68 return 0; 69 70 if (value.Type() == B_STRING_TYPE) { 71 font_height fontHeight; 72 view->GetFontHeight(&fontHeight); 73 return ceilf((fontHeight.ascent + fontHeight.descent) * 1.2f) + 2.0f; 74 } else { 75 // TODO: Support more value types. 76 return 0; 77 } 78 } 79 80 81 float 82 DefaultHeaderRenderer::PreferredHeaderWidth(BView* view, const Header* header) 83 { 84 BVariant value; 85 if (!header->GetValue(value)) 86 return 0; 87 88 if (value.Type() == B_STRING_TYPE) { 89 float width = view->StringWidth(value.ToString()); 90 return width + be_control_look->DefaultLabelSpacing() * 2.0f; 91 } else { 92 // TODO: Support more value types. 93 return 0; 94 } 95 } 96 97 98 void 99 DefaultHeaderRenderer::DrawHeader(BView* view, BRect frame, BRect updateRect, 100 const Header* header, uint32 flags) 101 { 102 DrawHeaderBackground(view, frame, updateRect, flags); 103 104 BVariant value; 105 if (!header->GetValue(value)) 106 return; 107 108 frame.InsetBy(be_control_look->DefaultLabelSpacing(), 0); 109 110 if (value.Type() == B_STRING_TYPE) { 111 be_control_look->DrawLabel(view, value.ToString(), frame, updateRect, 112 view->LowColor(), 0); 113 } 114 } 115 116 117 // #pragma mark - HeaderListener 118 119 120 HeaderListener::~HeaderListener() 121 { 122 } 123 124 125 void 126 HeaderListener::HeaderWidthChanged(Header* header) 127 { 128 } 129 130 131 void 132 HeaderListener::HeaderWidthRestrictionsChanged(Header* header) 133 { 134 } 135 136 137 void 138 HeaderListener::HeaderValueChanged(Header* header) 139 { 140 } 141 142 143 void 144 HeaderListener::HeaderRendererChanged(Header* header) 145 { 146 } 147 148 149 // #pragma mark - Header 150 151 152 Header::Header(int32 modelIndex) 153 : 154 fWidth(100), 155 fMinWidth(0), 156 fMaxWidth(10000), 157 fPreferredWidth(100), 158 fValue(), 159 fRenderer(NULL), 160 fModelIndex(modelIndex), 161 fResizable(true) 162 { 163 } 164 165 166 Header::Header(float width, float minWidth, float maxWidth, 167 float preferredWidth, int32 modelIndex) 168 : 169 fWidth(width), 170 fMinWidth(minWidth), 171 fMaxWidth(maxWidth), 172 fPreferredWidth(preferredWidth), 173 fValue(), 174 fRenderer(NULL), 175 fModelIndex(modelIndex), 176 fResizable(true) 177 { 178 } 179 180 181 float 182 Header::Width() const 183 { 184 return fWidth; 185 } 186 187 188 float 189 Header::MinWidth() const 190 { 191 return fMinWidth; 192 } 193 194 195 float 196 Header::MaxWidth() const 197 { 198 return fMaxWidth; 199 } 200 201 202 float 203 Header::PreferredWidth() const 204 { 205 return fPreferredWidth; 206 } 207 208 209 void 210 Header::SetWidth(float width) 211 { 212 if (width != fWidth) { 213 fWidth = width; 214 NotifyWidthChanged(); 215 } 216 } 217 218 219 void 220 Header::SetMinWidth(float width) 221 { 222 if (width != fMinWidth) { 223 fMinWidth = width; 224 NotifyWidthRestrictionsChanged(); 225 } 226 } 227 228 229 void 230 Header::SetMaxWidth(float width) 231 { 232 if (width != fMaxWidth) { 233 fMaxWidth = width; 234 NotifyWidthRestrictionsChanged(); 235 } 236 } 237 238 239 void 240 Header::SetPreferredWidth(float width) 241 { 242 if (width != fPreferredWidth) { 243 fPreferredWidth = width; 244 NotifyWidthRestrictionsChanged(); 245 } 246 } 247 248 249 bool 250 Header::IsResizable() const 251 { 252 return fResizable; 253 } 254 255 256 void 257 Header::SetResizable(bool resizable) 258 { 259 if (resizable != fResizable) { 260 fResizable = resizable; 261 NotifyWidthRestrictionsChanged(); 262 } 263 } 264 265 266 bool 267 Header::GetValue(BVariant& _value) const 268 { 269 _value = fValue; 270 return true; 271 } 272 273 274 void 275 Header::SetValue(const BVariant& value) 276 { 277 fValue = value; 278 NotifyValueChanged(); 279 } 280 281 282 int32 283 Header::ModelIndex() const 284 { 285 return fModelIndex; 286 } 287 288 289 void 290 Header::SetModelIndex(int32 index) 291 { 292 fModelIndex = index; 293 } 294 295 296 HeaderRenderer* 297 Header::GetHeaderRenderer() const 298 { 299 return fRenderer; 300 } 301 302 303 void 304 Header::SetHeaderRenderer(HeaderRenderer* renderer) 305 { 306 if (renderer != fRenderer) { 307 fRenderer = renderer; 308 NotifyRendererChanged(); 309 } 310 } 311 312 313 void 314 Header::AddListener(HeaderListener* listener) 315 { 316 fListeners.AddItem(listener); 317 } 318 319 320 void 321 Header::RemoveListener(HeaderListener* listener) 322 { 323 fListeners.RemoveItem(listener); 324 } 325 326 327 void 328 Header::NotifyWidthChanged() 329 { 330 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 331 HeaderListener* listener = fListeners.ItemAt(i); 332 listener->HeaderWidthChanged(this); 333 } 334 } 335 336 337 void 338 Header::NotifyWidthRestrictionsChanged() 339 { 340 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 341 HeaderListener* listener = fListeners.ItemAt(i); 342 listener->HeaderWidthRestrictionsChanged(this); 343 } 344 } 345 346 347 void 348 Header::NotifyValueChanged() 349 { 350 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 351 HeaderListener* listener = fListeners.ItemAt(i); 352 listener->HeaderValueChanged(this); 353 } 354 } 355 356 357 void 358 Header::NotifyRendererChanged() 359 { 360 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 361 HeaderListener* listener = fListeners.ItemAt(i); 362 listener->HeaderRendererChanged(this); 363 } 364 } 365 366 367 // #pragma mark - HeaderModelListener 368 369 370 HeaderModelListener::~HeaderModelListener() 371 { 372 } 373 374 375 void 376 HeaderModelListener::HeaderAdded(HeaderModel* model, int32 index) 377 { 378 } 379 380 381 void 382 HeaderModelListener::HeaderRemoved(HeaderModel* model, int32 index) 383 { 384 } 385 386 387 void 388 HeaderModelListener::HeaderMoved(HeaderModel* model, int32 fromIndex, 389 int32 toIndex) 390 { 391 } 392 393 394 // #pragma mark - HeaderModel 395 396 397 HeaderModel::HeaderModel() 398 { 399 } 400 401 402 HeaderModel::~HeaderModel() 403 { 404 } 405 406 407 int32 408 HeaderModel::CountHeaders() const 409 { 410 return fHeaders.CountItems(); 411 } 412 413 414 Header* 415 HeaderModel::HeaderAt(int32 index) const 416 { 417 return fHeaders.ItemAt(index); 418 } 419 420 421 int32 422 HeaderModel::IndexOfHeader(Header* header) const 423 { 424 return fHeaders.IndexOf(header); 425 } 426 427 428 bool 429 HeaderModel::AddHeader(Header* header) 430 { 431 if (!fHeaders.AddItem(header)) 432 return false; 433 434 NotifyHeaderAdded(fHeaders.CountItems() - 1); 435 436 return true; 437 } 438 439 440 Header* 441 HeaderModel::RemoveHeader(int32 index) 442 { 443 Header* header = fHeaders.RemoveItemAt(index); 444 if (header != NULL) 445 return NULL; 446 447 NotifyHeaderRemoved(index); 448 449 return header; 450 } 451 452 453 void 454 HeaderModel::RemoveHeader(Header* header) 455 { 456 RemoveHeader(fHeaders.IndexOf(header)); 457 } 458 459 460 bool 461 HeaderModel::MoveHeader(int32 fromIndex, int32 toIndex) 462 { 463 int32 headerCount = fHeaders.CountItems(); 464 if (fromIndex < 0 || fromIndex >= headerCount 465 || toIndex < 0 || toIndex >= headerCount) { 466 return false; 467 } 468 469 if (fromIndex == toIndex) 470 return true; 471 472 Header* header = fHeaders.RemoveItemAt(fromIndex); 473 fHeaders.AddItem(header, toIndex); 474 // TODO: Might fail. 475 476 NotifyHeaderMoved(fromIndex, toIndex); 477 478 return true; 479 } 480 481 482 void 483 HeaderModel::AddListener(HeaderModelListener* listener) 484 { 485 fListeners.AddItem(listener); 486 } 487 488 489 void 490 HeaderModel::RemoveListener(HeaderModelListener* listener) 491 { 492 fListeners.RemoveItem(listener); 493 } 494 495 496 void 497 HeaderModel::NotifyHeaderAdded(int32 index) 498 { 499 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 500 HeaderModelListener* listener = fListeners.ItemAt(i); 501 listener->HeaderAdded(this, index); 502 } 503 } 504 505 506 void 507 HeaderModel::NotifyHeaderRemoved(int32 index) 508 { 509 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 510 HeaderModelListener* listener = fListeners.ItemAt(i); 511 listener->HeaderRemoved(this, index); 512 } 513 } 514 515 516 void 517 HeaderModel::NotifyHeaderMoved(int32 fromIndex, int32 toIndex) 518 { 519 for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { 520 HeaderModelListener* listener = fListeners.ItemAt(i); 521 listener->HeaderMoved(this, fromIndex, toIndex); 522 } 523 } 524 525 526 // #pragma mark - HeaderEntry 527 528 529 struct HeaderView::HeaderEntry { 530 Header* header; 531 float position; 532 float width; 533 534 HeaderEntry(Header* header) 535 : 536 header(header) 537 { 538 } 539 }; 540 541 542 // #pragma mark - HeaderEntry 543 544 545 class HeaderView::DragState { 546 public: 547 virtual ~DragState() 548 { 549 } 550 551 virtual void MouseDown(BPoint where) = 0; 552 virtual void DragTo(BPoint where) = 0; 553 virtual void MouseUp(); 554 555 }; 556 557 558 // #pragma mark - HeaderView 559 560 561 HeaderView::HeaderView() 562 : 563 BView("header view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 564 fModel(NULL), 565 fHeaderEntries(10, true), 566 fLayoutValid(false) 567 { 568 HeaderModel* model = new(std::nothrow) HeaderModel; 569 Reference<HeaderModel> modelReference(model, true); 570 SetModel(model); 571 } 572 573 574 HeaderView::~HeaderView() 575 { 576 SetModel(NULL); 577 } 578 579 580 void 581 HeaderView::Draw(BRect updateRect) 582 { 583 if (fModel == NULL) 584 return; 585 586 _ValidateHeadersLayout(); 587 588 DefaultHeaderRenderer defaultRenderer; 589 float bottom = Bounds().Height(); 590 float left = 0.0f; 591 592 for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++) { 593 if (Header* header = fModel->HeaderAt(i)) { 594 HeaderRenderer* renderer = header->GetHeaderRenderer(); 595 if (renderer == NULL) 596 renderer = &defaultRenderer; 597 598 BRect frame(entry->position, 0, entry->position + entry->width - 1, 599 bottom); 600 renderer->DrawHeader(this, frame, updateRect, header, 0); 601 // TODO: flags! 602 603 left = entry->position + entry->width; 604 } 605 } 606 607 // TODO: We are not using any custom renderer here. 608 defaultRenderer.DrawHeaderBackground(this, 609 BRect(left, 0, Bounds().right + 1, bottom), updateRect, 0); 610 } 611 612 613 BSize 614 HeaderView::MinSize() 615 { 616 _ValidateHeadersLayout(); 617 618 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 619 BSize(100, fPreferredHeight - 1)); 620 } 621 622 623 BSize 624 HeaderView::MaxSize() 625 { 626 _ValidateHeadersLayout(); 627 628 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 629 BSize(B_SIZE_UNLIMITED, fPreferredHeight - 1)); 630 } 631 632 633 BSize 634 HeaderView::PreferredSize() 635 { 636 _ValidateHeadersLayout(); 637 638 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 639 BSize(fPreferredWidth - 1, fPreferredHeight - 1)); 640 } 641 642 643 HeaderModel* 644 HeaderView::Model() const 645 { 646 return fModel; 647 } 648 649 650 void 651 HeaderView::MouseDown(BPoint where) 652 { 653 } 654 655 656 void 657 HeaderView::MouseUp(BPoint where) 658 { 659 } 660 661 662 void 663 HeaderView::MouseMoved(BPoint where, uint32 transit, 664 const BMessage* dragMessage) 665 { 666 } 667 668 669 status_t 670 HeaderView::SetModel(HeaderModel* model) 671 { 672 if (model == fModel) 673 return B_OK; 674 675 if (fModel != NULL) { 676 // remove all headers 677 for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++) 678 entry->header->RemoveListener(this); 679 fHeaderEntries.MakeEmpty(); 680 681 fModel->RemoveListener(this); 682 fModel->ReleaseReference(); 683 } 684 685 fModel = model; 686 687 if (fModel != NULL) { 688 fModel->AcquireReference(); 689 fModel->AddListener(this); 690 691 // create header entries 692 int32 headerCount = fModel->CountHeaders(); 693 for (int32 i = 0; i < headerCount; i++) { 694 HeaderEntry* entry = new(std::nothrow) HeaderEntry( 695 fModel->HeaderAt(i)); 696 if (entry == NULL || !fHeaderEntries.AddItem(entry)) { 697 delete entry; 698 return B_NO_MEMORY; 699 } 700 701 entry->header->AddListener(this); 702 } 703 } 704 705 _InvalidateHeadersLayout(0); 706 Invalidate(); 707 708 return B_OK; 709 } 710 711 712 void 713 HeaderView::AddListener(HeaderViewListener* listener) 714 { 715 fListeners.AddItem(listener); 716 } 717 718 719 void 720 HeaderView::RemoveListener(HeaderViewListener* listener) 721 { 722 fListeners.RemoveItem(listener); 723 } 724 725 726 void 727 HeaderView::HeaderAdded(HeaderModel* model, int32 index) 728 { 729 if (Header* header = fModel->HeaderAt(index)) { 730 HeaderEntry* entry = new(std::nothrow) HeaderEntry( 731 fModel->HeaderAt(index)); 732 printf("HeaderView::HeaderAdded(%p, %ld): header: %p, entry: %p\n", model, index, entry->header, entry); 733 if (entry == NULL || !fHeaderEntries.AddItem(entry)) { 734 delete entry; 735 return; 736 } 737 738 header->AddListener(this); 739 _InvalidateHeadersLayout(index); 740 } 741 } 742 743 744 void 745 HeaderView::HeaderRemoved(HeaderModel* model, int32 index) 746 { 747 if (HeaderEntry* entry = fHeaderEntries.RemoveItemAt(index)) { 748 entry->header->RemoveListener(this); 749 _InvalidateHeadersLayout(index); 750 } 751 } 752 753 754 void 755 HeaderView::HeaderMoved(HeaderModel* model, int32 fromIndex, int32 toIndex) 756 { 757 _InvalidateHeadersLayout(std::min(fromIndex, toIndex)); 758 } 759 760 761 void 762 HeaderView::HeaderWidthChanged(Header* header) 763 { 764 _HeaderPropertiesChanged(header, true, true); 765 } 766 767 768 void 769 HeaderView::HeaderWidthRestrictionsChanged(Header* header) 770 { 771 // TODO:... 772 } 773 774 775 void 776 HeaderView::HeaderValueChanged(Header* header) 777 { 778 _HeaderPropertiesChanged(header, true, false); 779 } 780 781 782 void 783 HeaderView::HeaderRendererChanged(Header* header) 784 { 785 _HeaderPropertiesChanged(header, true, true); 786 } 787 788 789 void 790 HeaderView::_HeaderPropertiesChanged(Header* header, bool redrawNeeded, 791 bool relayoutNeeded) 792 { 793 if (!redrawNeeded && !relayoutNeeded) 794 return; 795 796 int32 index = fModel->IndexOfHeader(header); 797 798 if (relayoutNeeded) 799 _InvalidateHeadersLayout(index); 800 else if (redrawNeeded) 801 _InvalidateHeaders(index, index + 1); 802 } 803 804 805 void 806 HeaderView::_InvalidateHeadersLayout(int32 firstIndex) 807 { 808 if (!fLayoutValid) 809 return; 810 811 fLayoutValid = false; 812 InvalidateLayout(); 813 Invalidate(); 814 } 815 816 817 void 818 HeaderView::_InvalidateHeaders(int32 firstIndex, int32 endIndex) 819 { 820 Invalidate(); 821 // TODO: Be less lazy! 822 } 823 824 825 void 826 HeaderView::_ValidateHeadersLayout() 827 { 828 if (fLayoutValid) 829 return; 830 831 DefaultHeaderRenderer defaultRenderer; 832 833 int32 headerCount = fHeaderEntries.CountItems(); 834 float position = 0; 835 fPreferredWidth = 0; 836 fPreferredHeight = 0; 837 838 for (int32 i = 0; i < headerCount; i++) { 839 HeaderEntry* entry = fHeaderEntries.ItemAt(i); 840 entry->position = position; 841 if (Header* header = fModel->HeaderAt(i)) { 842 entry->width = header->Width(); 843 fPreferredWidth += header->PreferredWidth(); 844 } else 845 entry->width = 0; 846 847 position = entry->position + entry->width; 848 849 if (Header* header = fModel->HeaderAt(i)) { 850 HeaderRenderer* renderer = header->GetHeaderRenderer(); 851 if (renderer == NULL) 852 renderer = &defaultRenderer; 853 854 float height = renderer->HeaderHeight(this, header); 855 if (height > fPreferredHeight) 856 fPreferredHeight = height; 857 } 858 } 859 860 fLayoutValid = true; 861 } 862 863 864 // #pragma mark - HeaderViewListener 865 866 867 HeaderViewListener::~HeaderViewListener() 868 { 869 } 870