1/* 2 * Copyright 2001-2011, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Stephan A��mus <superstippi@gmx.de> 8 * Clemens Zeidler <haiku@clemens-zeidler.de> 9 * Ingo Weinhold <ingo_weinhold@gmx.de> 10 */ 11 12 13/*! Base class for window decorators */ 14 15 16#include "Decorator.h" 17 18#include <stdio.h> 19 20#include <Region.h> 21 22#include "DrawingEngine.h" 23 24 25Decorator::Tab::Tab() 26 : 27 zoomRect(), 28 closeRect(), 29 minimizeRect(), 30 31 closePressed(false), 32 zoomPressed(false), 33 minimizePressed(false), 34 35 look(B_TITLED_WINDOW_LOOK), 36 flags(0), 37 isFocused(false), 38 title("") 39{ 40 41} 42 43 44/*! \brief Constructor 45 46 Does general initialization of internal data members and creates a colorset 47 object. 48 49 \param rect Size of client area 50 \param wlook style of window look. See Window.h 51 \param wfeel style of window feel. See Window.h 52 \param wflags various window flags. See Window.h 53*/ 54Decorator::Decorator(DesktopSettings& settings, BRect rect) 55 : 56 fDrawingEngine(NULL), 57 fDrawState(), 58 59 fTitleBarRect(), 60 fFrame(rect), 61 fResizeRect(), 62 fBorderRect(), 63 64 fTopTab(NULL), 65 66 fFootprintValid(false) 67{ 68 memset(&fRegionHighlights, HIGHLIGHT_NONE, sizeof(fRegionHighlights)); 69} 70 71 72/*! 73 \brief Destructor 74 75 Frees the color set and the title string 76*/ 77Decorator::~Decorator() 78{ 79} 80 81 82Decorator::Tab* 83Decorator::AddTab(DesktopSettings& settings, const char* title, 84 window_look look, uint32 flags, int32 index, BRegion* updateRegion) 85{ 86 Decorator::Tab* tab = _AllocateNewTab(); 87 if (tab == NULL) 88 return NULL; 89 tab->title = title; 90 tab->look = look; 91 tab->flags = flags; 92 93 bool ok = false; 94 if (index >= 0) { 95 if (fTabList.AddItem(tab, index) == true) 96 ok = true; 97 } else if (fTabList.AddItem(tab) == true) 98 ok = true; 99 100 if (ok == false) { 101 delete tab; 102 return NULL; 103 } 104 105 Decorator::Tab* oldTop = fTopTab; 106 fTopTab = tab; 107 if (_AddTab(settings, index, updateRegion) == false) { 108 fTabList.RemoveItem(tab); 109 delete tab; 110 fTopTab = oldTop; 111 return NULL; 112 } 113 114 _InvalidateFootprint(); 115 return tab; 116} 117 118 119bool 120Decorator::RemoveTab(int32 index, BRegion* updateRegion) 121{ 122 Decorator::Tab* tab = fTabList.RemoveItemAt(index); 123 if (tab == NULL) 124 return false; 125 126 _RemoveTab(index, updateRegion); 127 128 delete tab; 129 _InvalidateFootprint(); 130 return true; 131} 132 133 134bool 135Decorator::MoveTab(int32 from, int32 to, bool isMoving, BRegion* updateRegion) 136{ 137 if (_MoveTab(from, to, isMoving, updateRegion) == false) 138 return false; 139 if (fTabList.MoveItem(from, to) == false) { 140 // move the tab back 141 _MoveTab(from, to, isMoving, updateRegion); 142 return false; 143 } 144 return true; 145} 146 147 148int32 149Decorator::TabAt(const BPoint& where) const 150{ 151 for (int32 i = 0; i < fTabList.CountItems(); i++) { 152 Decorator::Tab* tab = fTabList.ItemAt(i); 153 if (tab->tabRect.Contains(where)) 154 return i; 155 } 156 157 return -1; 158} 159 160 161void 162Decorator::SetTopTab(int32 tab) 163{ 164 fTopTab = fTabList.ItemAt(tab); 165} 166 167 168/*! \brief Assigns a display driver to the decorator 169 \param driver A valid DrawingEngine object 170*/ 171void 172Decorator::SetDrawingEngine(DrawingEngine* engine) 173{ 174 fDrawingEngine = engine; 175 // lots of subclasses will depend on the driver for text support, so call 176 // _DoLayout() after we have it 177 if (fDrawingEngine) 178 _DoLayout(); 179} 180 181 182/*! \brief Sets the decorator's window flags 183 184 While this call will not update the screen, it will affect how future 185 updates work and immediately affects input handling. 186 187 \param flags New value for the flags 188*/ 189void 190Decorator::SetFlags(int32 tab, uint32 flags, BRegion* updateRegion) 191{ 192 // we're nice to our subclasses - we make sure B_NOT_{H|V|}_RESIZABLE 193 // are in sync (it's only a semantical simplification, not a necessity) 194 if ((flags & (B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE)) 195 == (B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE)) 196 flags |= B_NOT_RESIZABLE; 197 if (flags & B_NOT_RESIZABLE) 198 flags |= B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE; 199 200 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 201 if (decoratorTab == NULL) 202 return; 203 _SetFlags(decoratorTab, flags, updateRegion); 204 _InvalidateFootprint(); 205 // the border might have changed (smaller/larger tab) 206} 207 208 209/*! \brief Called whenever the system fonts are changed. 210*/ 211void 212Decorator::FontsChanged(DesktopSettings& settings, BRegion* updateRegion) 213{ 214 _FontsChanged(settings, updateRegion); 215 _InvalidateFootprint(); 216} 217 218 219/*! \brief Sets the decorator's window look 220 \param look New value for the look 221*/ 222void 223Decorator::SetLook(int32 tab, DesktopSettings& settings, window_look look, 224 BRegion* updateRect) 225{ 226 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 227 if (decoratorTab == NULL) 228 return; 229 _SetLook(decoratorTab, settings, look, updateRect); 230 _InvalidateFootprint(); 231 // the border very likely changed 232} 233 234 235/*! \brief Returns the decorator's window look 236 \return the decorator's window look 237*/ 238window_look 239Decorator::Look(int32 tab) const 240{ 241 return TabAt(tab)->look; 242} 243 244 245/*! \brief Returns the decorator's window flags 246 \return the decorator's window flags 247*/ 248uint32 249Decorator::Flags(int32 tab) const 250{ 251 return TabAt(tab)->flags; 252} 253 254 255/*! \brief Returns the decorator's border rectangle 256 \return the decorator's border rectangle 257*/ 258BRect 259Decorator::BorderRect() const 260{ 261 return fBorderRect; 262} 263 264 265BRect 266Decorator::TitleBarRect() const 267{ 268 return fTitleBarRect; 269} 270 271 272/*! \brief Returns the decorator's tab rectangle 273 \return the decorator's tab rectangle 274*/ 275BRect 276Decorator::TabRect(int32 tab) const 277{ 278 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 279 if (decoratorTab == NULL) 280 return BRect(); 281 return decoratorTab->tabRect; 282} 283 284 285BRect 286Decorator::TabRect(Decorator::Tab* tab) const 287{ 288 return tab->tabRect; 289} 290 291 292/*! \brief Sets the close button's value. 293 294 Note that this does not update the button's look - it just updates the 295 internal button value 296 297 \param is_down Whether the button is down or not 298*/ 299void 300Decorator::SetClose(int32 tab, bool pressed) 301{ 302 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 303 if (decoratorTab == NULL) 304 return; 305 306 if (pressed != decoratorTab->closePressed) { 307 decoratorTab->closePressed = pressed; 308 DrawClose(tab); 309 } 310} 311 312/*! \brief Sets the minimize button's value. 313 314 Note that this does not update the button's look - it just updates the 315 internal button value 316 317 \param is_down Whether the button is down or not 318*/ 319void 320Decorator::SetMinimize(int32 tab, bool pressed) 321{ 322 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 323 if (decoratorTab == NULL) 324 return; 325 326 if (pressed != decoratorTab->minimizePressed) { 327 decoratorTab->minimizePressed = pressed; 328 DrawMinimize(tab); 329 } 330} 331 332/*! \brief Sets the zoom button's value. 333 334 Note that this does not update the button's look - it just updates the 335 internal button value 336 337 \param is_down Whether the button is down or not 338*/ 339void 340Decorator::SetZoom(int32 tab, bool pressed) 341{ 342 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 343 if (decoratorTab == NULL) 344 return; 345 346 if (pressed != decoratorTab->zoomPressed) { 347 decoratorTab->zoomPressed = pressed; 348 DrawZoom(tab); 349 } 350} 351 352 353/*! \brief Updates the value of the decorator title 354 \param string New title value 355*/ 356void 357Decorator::SetTitle(int32 tab, const char* string, BRegion* updateRegion) 358{ 359 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 360 if (decoratorTab == NULL) 361 return; 362 363 decoratorTab->title.SetTo(string); 364 _SetTitle(decoratorTab, string, updateRegion); 365 366 _InvalidateFootprint(); 367 // the border very likely changed 368 369 // TODO: redraw? 370} 371 372 373/*! \brief Returns the decorator's title 374 \return the decorator's title 375*/ 376const char* 377Decorator::Title(int32 tab) const 378{ 379 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 380 if (decoratorTab == NULL) 381 return ""; 382 return decoratorTab->title; 383} 384 385 386const char* 387Decorator::Title(Decorator::Tab* tab) const 388{ 389 return tab->title; 390} 391 392 393bool 394Decorator::SetTabLocation(int32 tab, float location, bool isShifting, 395 BRegion* updateRegion) 396{ 397 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 398 if (decoratorTab == NULL) 399 return false; 400 if (_SetTabLocation(decoratorTab, location, isShifting, updateRegion)) { 401 _InvalidateFootprint(); 402 return true; 403 } 404 return false; 405} 406 407 408 409/*! \brief Changes the focus value of the decorator 410 411 While this call will not update the screen, it will affect how future 412 updates work. 413 414 \param active True if active, false if not 415*/ 416void 417Decorator::SetFocus(int32 tab, bool active) 418{ 419 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 420 if (decoratorTab == NULL) 421 return; 422 decoratorTab->isFocused = active; 423 _SetFocus(decoratorTab); 424 // TODO: maybe it would be cleaner to handle the redraw here. 425} 426 427 428bool 429Decorator::IsFocus(int32 tab) const 430{ 431 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 432 if (decoratorTab == NULL) 433 return false; 434 return decoratorTab->isFocused; 435}; 436 437 438bool 439Decorator::IsFocus(Decorator::Tab* tab) const 440{ 441 return tab->isFocused; 442} 443 444 445void 446Decorator::GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth, 447 int32* maxHeight) const 448{ 449} 450 451 452// #pragma mark - virtual methods 453 454 455/*! \brief Returns a cached footprint if available otherwise recalculate it 456*/ 457const BRegion& 458Decorator::GetFootprint() 459{ 460 if (!fFootprintValid) { 461 _GetFootprint(&fFootprint); 462 fFootprintValid = true; 463 } 464 return fFootprint; 465} 466 467 468/*! \brief Performs hit-testing for the decorator. 469 470 The base class provides a basic implementation, recognizing only button and 471 tab hits. Derived classes must override/enhance it to handle borders and 472 corners correctly. 473 474 \param where The point to be tested. 475 \return Either of the following, depending on what was hit: 476 - \c REGION_NONE: None of the decorator regions. 477 - \c REGION_TAB: The window tab (but none of the buttons embedded). 478 - \c REGION_CLOSE_BUTTON: The close button. 479 - \c REGION_ZOOM_BUTTON: The zoom button. 480 - \c REGION_MINIMIZE_BUTTON: The minimize button. 481 - \c REGION_LEFT_BORDER: The left border. 482 - \c REGION_RIGHT_BORDER: The right border. 483 - \c REGION_TOP_BORDER: The top border. 484 - \c REGION_BOTTOM_BORDER: The bottom border. 485 - \c REGION_LEFT_TOP_CORNER: The left-top corner. 486 - \c REGION_LEFT_BOTTOM_CORNER: The left-bottom corner. 487 - \c REGION_RIGHT_TOP_CORNER: The right-top corner. 488 - \c REGION_RIGHT_BOTTOM_CORNER The right-bottom corner. 489*/ 490Decorator::Region 491Decorator::RegionAt(BPoint where, int32& tabIndex) const 492{ 493 tabIndex = -1; 494 495 for (int32 i = 0; i < fTabList.CountItems(); i++) { 496 Decorator::Tab* tab = fTabList.ItemAt(i); 497 if (tab->closeRect.Contains(where)) { 498 tabIndex = i; 499 return REGION_CLOSE_BUTTON; 500 } 501 if (tab->zoomRect.Contains(where)) { 502 tabIndex = i; 503 return REGION_ZOOM_BUTTON; 504 } 505 if (tab->tabRect.Contains(where)) { 506 tabIndex = i; 507 return REGION_TAB; 508 } 509 } 510 511 return REGION_NONE; 512} 513 514 515/*! \brief Moves the decorator frame and all default rectangles 516 517 If a subclass implements this method, be sure to call Decorator::MoveBy 518 to ensure that internal members are also updated. All members of the 519 Decorator class are automatically moved in this method 520 521 \param x X Offset 522 \param y y Offset 523*/ 524void 525Decorator::MoveBy(float x, float y) 526{ 527 MoveBy(BPoint(x, y)); 528} 529 530 531/*! \brief Moves the decorator frame and all default rectangles 532 533 If a subclass implements this method, be sure to call Decorator::MoveBy 534 to ensure that internal members are also updated. All members of the 535 Decorator class are automatically moved in this method 536 537 \param offset BPoint containing the offsets 538*/ 539void 540Decorator::MoveBy(BPoint offset) 541{ 542 if (fFootprintValid) 543 fFootprint.OffsetBy(offset.x, offset.y); 544 545 _MoveBy(offset); 546} 547 548 549/*! \brief Resizes the decorator frame 550 551 This is a required function for subclasses to implement - the default does 552 nothing. Note that window resize flags should be followed and fFrame should 553 be resized accordingly. It would also be a wise idea to ensure that the 554 window's rectangles are not inverted. 555 556 \param x x offset 557 \param y y offset 558*/ 559void 560Decorator::ResizeBy(float x, float y, BRegion* dirty) 561{ 562 ResizeBy(BPoint(x, y), dirty); 563} 564 565 566void 567Decorator::ResizeBy(BPoint offset, BRegion* dirty) 568{ 569 _ResizeBy(offset, dirty); 570 _InvalidateFootprint(); 571} 572 573 574/*! \brief Sets a specific highlight for a decorator region. 575 576 Can be overridden by derived classes, but the base class version must be 577 called, if the highlight shall be applied. 578 579 \param region The decorator region. 580 \param highlight The value identifying the kind of highlight. 581 \param dirty The dirty region to be extended, if the highlight changes. Can 582 be \c NULL. 583 \return \c true, if the highlight could be applied. 584*/ 585bool 586Decorator::SetRegionHighlight(Region region, uint8 highlight, BRegion* dirty, 587 int32 tab) 588{ 589 int32 index = (int32)region - 1; 590 if (index < 0 || index >= REGION_COUNT - 1) 591 return false; 592 593 if (fRegionHighlights[index] == highlight) 594 return true; 595 fRegionHighlights[index] = highlight; 596 597 if (dirty != NULL) 598 ExtendDirtyRegion(region, *dirty); 599 600 return true; 601} 602 603 604bool 605Decorator::SetSettings(const BMessage& settings, BRegion* updateRegion) 606{ 607 if (_SetSettings(settings, updateRegion)) { 608 _InvalidateFootprint(); 609 return true; 610 } 611 return false; 612} 613 614 615bool 616Decorator::GetSettings(BMessage* settings) const 617{ 618 return false; 619} 620 621 622/*! \brief Updates the decorator's look in the area \a rect 623 624 The default version updates all areas which intersect the frame and tab. 625 626 \param rect The area to update. 627*/ 628void 629Decorator::Draw(BRect rect) 630{ 631 _DrawFrame(rect & fFrame); 632 _DrawTabs(rect & fTitleBarRect); 633} 634 635 636//! Forces a complete decorator update 637void 638Decorator::Draw() 639{ 640 _DrawFrame(fFrame); 641 _DrawTabs(fTitleBarRect); 642} 643 644 645//! draws the frame 646void 647Decorator::DrawFrame() 648{ 649 _DrawFrame(fFrame); 650} 651 652 653//! draws the tab, title, and buttons 654void 655Decorator::DrawTab(int32 tabIndex) 656{ 657 Decorator::Tab* tab = fTabList.ItemAt(tabIndex); 658 if (tab == NULL) 659 return; 660 661 _DrawTab(tab, tab->tabRect); 662 _DrawZoom(tab, false, tab->zoomRect); 663 _DrawMinimize(tab, false, tab->minimizeRect); 664 _DrawTitle(tab, tab->tabRect); 665 _DrawClose(tab, false, tab->closeRect); 666} 667 668 669//! Draws the close button 670void 671Decorator::DrawClose(int32 tab) 672{ 673 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 674 if (decoratorTab == NULL) 675 return; 676 _DrawClose(decoratorTab, true, decoratorTab->closeRect); 677} 678 679 680//! draws the minimize button 681void 682Decorator::DrawMinimize(int32 tab) 683{ 684 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 685 if (decoratorTab == NULL) 686 return; 687 _DrawTab(decoratorTab, decoratorTab->minimizeRect); 688} 689 690 691//! draws the title 692void 693Decorator::DrawTitle(int32 tab) 694{ 695 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 696 if (decoratorTab == NULL) 697 return; 698 _DrawTitle(decoratorTab, decoratorTab->tabRect); 699} 700 701 702//! draws the zoom button 703void 704Decorator::DrawZoom(int32 tab) 705{ 706 Decorator::Tab* decoratorTab = fTabList.ItemAt(tab); 707 if (decoratorTab == NULL) 708 return; 709 _DrawZoom(decoratorTab, true, decoratorTab->zoomRect); 710} 711 712 713rgb_color 714Decorator::UIColor(color_which which) 715{ 716 // TODO: for now - calling ui_color() from within the app_server 717 // will always return the default colors (as there is no be_app) 718 return ui_color(which); 719} 720 721 722/*! \brief Extends a dirty region by a decorator region. 723 724 Must be implemented by derived classes. 725 726 \param region The decorator region. 727 \param dirty The dirty region to be extended. 728*/ 729void 730Decorator::ExtendDirtyRegion(Region region, BRegion& dirty) 731{ 732} 733 734 735//! Function for calculating layout for the decorator 736void 737Decorator::_DoLayout() 738{ 739} 740 741 742/*! \brief Actually draws the frame 743 \param rect Area of the frame to update 744*/ 745void 746Decorator::_DrawFrame(BRect rect) 747{ 748} 749 750 751 752void 753Decorator::_DrawTabs(BRect rect) 754{ 755 Decorator::Tab* focusTab = NULL; 756 for (int32 i = 0; i < fTabList.CountItems(); i++) { 757 Decorator::Tab* tab = fTabList.ItemAt(i); 758 if (tab->isFocused) { 759 focusTab = tab; 760 continue; 761 } 762 _DrawTab(tab, rect); 763 } 764 if (focusTab != NULL) 765 _DrawTab(focusTab, rect); 766} 767 768 769/*! \brief Actually draws the tab 770 771 This function is called when the tab itself needs drawn. Other items, 772 like the window title or buttons, should not be drawn here. 773 774 \param rect Area of the tab to update 775*/ 776void 777Decorator::_DrawTab(Decorator::Tab* tab, BRect rect) 778{ 779} 780 781 782/*! \brief Actually draws the close button 783 784 Unless a subclass has a particularly large button, it is probably 785 unnecessary to check the update rectangle. 786 787 \param rect Area of the button to update 788*/ 789void 790Decorator::_DrawClose(Decorator::Tab* tab, bool direct, BRect rect) 791{ 792} 793 794 795/*! \brief Actually draws the title 796 797 The main tasks for this function are to ensure that the decorator draws 798 the title only in its own area and drawing the title itself. 799 Using B_OP_COPY for drawing the title is recommended because of the marked 800 performance hit of the other drawing modes, but it is not a requirement. 801 802 \param rect area of the title to update 803*/ 804void 805Decorator::_DrawTitle(Decorator::Tab* tab, BRect rect) 806{ 807} 808 809 810/*! \brief Actually draws the zoom button 811 812 Unless a subclass has a particularly large button, it is probably 813 unnecessary to check the update rectangle. 814 815 \param rect Area of the button to update 816*/ 817void 818Decorator::_DrawZoom(Decorator::Tab* tab, bool direct, BRect rect) 819{ 820} 821 822/*! 823 \brief Actually draws the minimize button 824 825 Unless a subclass has a particularly large button, it is probably 826 unnecessary to check the update rectangle. 827 828 \param rect Area of the button to update 829*/ 830void 831Decorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect) 832{ 833} 834 835 836bool 837Decorator::_SetTabLocation(Decorator::Tab* tab, float location, bool isShifting, 838 BRegion* /*updateRegion*/) 839{ 840 return false; 841} 842 843 844//! Hook function called when the decorator changes focus 845void 846Decorator::_SetFocus(Decorator::Tab* tab) 847{ 848} 849 850 851void 852Decorator::_FontsChanged(DesktopSettings& settings, BRegion* updateRegion) 853{ 854} 855 856 857void 858Decorator::_SetLook(Decorator::Tab* tab, DesktopSettings& settings, 859 window_look look, BRegion* updateRect) 860{ 861 tab->look = look; 862} 863 864 865void 866Decorator::_SetFlags(Decorator::Tab* tab, uint32 flags, BRegion* updateRegion) 867{ 868 tab->flags = flags; 869} 870 871 872void 873Decorator::_MoveBy(BPoint offset) 874{ 875 for (int32 i = 0; i < fTabList.CountItems(); i++) { 876 Decorator::Tab* tab = fTabList.ItemAt(i); 877 878 tab->zoomRect.OffsetBy(offset); 879 tab->closeRect.OffsetBy(offset); 880 tab->minimizeRect.OffsetBy(offset); 881 tab->tabRect.OffsetBy(offset); 882 } 883 fTitleBarRect.OffsetBy(offset); 884 fFrame.OffsetBy(offset); 885 fResizeRect.OffsetBy(offset); 886 fBorderRect.OffsetBy(offset); 887} 888 889 890bool 891Decorator::_SetSettings(const BMessage& settings, BRegion* updateRegion) 892{ 893 return false; 894} 895 896 897/*! \brief Returns the "footprint" of the entire window, including decorator 898 899 This function is required by all subclasses. 900 901 \param region Region to be changed to represent the window's screen 902 footprint 903*/ 904void 905Decorator::_GetFootprint(BRegion *region) 906{ 907} 908 909 910void 911Decorator::_InvalidateFootprint() 912{ 913 fFootprintValid = false; 914} 915