xref: /haiku/src/servers/app/decorator/Decorator.cpp (revision a61df2290cbce78766c5bf2d6a5f72f59910b93d)
1 /*
2  * Copyright 2001-2020 Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus, superstippi@gmx.de
7  *		DarkWyrm, bpmagic@columbus.rr.com
8  *		John Scipione, jscipione@gmail.com
9  *		Ingo Weinhold, ingo_weinhold@gmx.de
10  *		Clemens Zeidler, haiku@clemens-zeidler.de
11  *		Joseph Groover, looncraz@looncraz.net
12  *		Tri-Edge AI
13  *		Jacob Secunda, secundja@gmail.com
14  */
15 
16 
17 /*!	Base class for window decorators */
18 
19 
20 #include "Decorator.h"
21 
22 #include <stdio.h>
23 
24 #include <Region.h>
25 
26 #include "Desktop.h"
27 #include "DesktopSettings.h"
28 #include "DrawingEngine.h"
29 
30 
Tab()31 Decorator::Tab::Tab()
32 	:
33 	tabRect(),
34 
35 	zoomRect(),
36 	closeRect(),
37 	minimizeRect(),
38 
39 	closePressed(false),
40 	zoomPressed(false),
41 	minimizePressed(false),
42 
43 	look(B_TITLED_WINDOW_LOOK),
44 	flags(0),
45 	isFocused(false),
46 	title(""),
47 
48 	tabOffset(0),
49 	tabLocation(0.0f),
50 	textOffset(10.0f),
51 
52 	truncatedTitle(""),
53 	truncatedTitleLength(0),
54 
55 	buttonFocus(false),
56 	isHighlighted(false),
57 
58 	minTabSize(0.0f),
59 	maxTabSize(0.0f)
60 {
61 	closeBitmaps[0] = closeBitmaps[1] = closeBitmaps[2] = closeBitmaps[3]
62 		= minimizeBitmaps[0] = minimizeBitmaps[1] = minimizeBitmaps[2]
63 		= minimizeBitmaps[3] = zoomBitmaps[0] = zoomBitmaps[1] = zoomBitmaps[2]
64 		= zoomBitmaps[3] = NULL;
65 }
66 
67 
68 /*!	\brief Constructor
69 
70 	Does general initialization of internal data members and creates a colorset
71 	object.
72 
73 	\param settings DesktopSettings pointer.
74 	\param frame Decorator frame rectangle
75 */
Decorator(DesktopSettings & settings,BRect frame,Desktop * desktop)76 Decorator::Decorator(DesktopSettings& settings, BRect frame,
77 					Desktop* desktop)
78 	:
79 	fLocker("Decorator"),
80 
81 	fDrawingEngine(NULL),
82 	fDrawState(),
83 
84 	fTitleBarRect(),
85 	fFrame(frame),
86 	fResizeRect(),
87 	fBorderRect(),
88 	fOutlineBorderRect(),
89 
90 	fLeftBorder(),
91 	fTopBorder(),
92 	fBottomBorder(),
93 	fRightBorder(),
94 
95 	fLeftOutlineBorder(),
96 	fTopOutlineBorder(),
97 	fBottomOutlineBorder(),
98 	fRightOutlineBorder(),
99 
100 	fBorderWidth(-1),
101 	fOutlineBorderWidth(-1),
102 
103 	fTopTab(NULL),
104 
105 	fDesktop(desktop),
106 	fFootprintValid(false)
107 {
108 	memset(&fRegionHighlights, HIGHLIGHT_NONE, sizeof(fRegionHighlights));
109 }
110 
111 
112 /*!	\brief Destructor
113 
114 	Frees the color set and the title string
115 */
~Decorator()116 Decorator::~Decorator()
117 {
118 }
119 
120 
121 Decorator::Tab*
AddTab(DesktopSettings & settings,const char * title,window_look look,uint32 flags,int32 index,BRegion * updateRegion)122 Decorator::AddTab(DesktopSettings& settings, const char* title,
123 	window_look look, uint32 flags, int32 index, BRegion* updateRegion)
124 {
125 	AutoWriteLocker _(fLocker);
126 
127 	Decorator::Tab* tab = _AllocateNewTab();
128 	if (tab == NULL)
129 		return NULL;
130 	tab->title = title;
131 	tab->look = look;
132 	tab->flags = flags;
133 
134 	bool ok = false;
135 	if (index >= 0) {
136 		if (fTabList.AddItem(tab, index) == true)
137 			ok = true;
138 	} else if (fTabList.AddItem(tab) == true)
139 		ok = true;
140 
141 	if (ok == false) {
142 		delete tab;
143 		return NULL;
144 	}
145 
146 	Decorator::Tab* oldTop = fTopTab;
147 	fTopTab = tab;
148 	if (_AddTab(settings, index, updateRegion) == false) {
149 		fTabList.RemoveItem(tab);
150 		delete tab;
151 		fTopTab = oldTop;
152 		return NULL;
153 	}
154 
155 	_InvalidateFootprint();
156 	return tab;
157 }
158 
159 
160 bool
RemoveTab(int32 index,BRegion * updateRegion)161 Decorator::RemoveTab(int32 index, BRegion* updateRegion)
162 {
163 	AutoWriteLocker _(fLocker);
164 
165 	// add removed tab area to update region before removing it
166 	if (updateRegion != NULL)
167 		updateRegion->Include(TabRect(index));
168 
169 	Decorator::Tab* tab = fTabList.RemoveItemAt(index);
170 	if (tab == NULL)
171 		return false;
172 
173 	_RemoveTab(index, updateRegion);
174 
175 	delete tab;
176 	_InvalidateFootprint();
177 	return true;
178 }
179 
180 
181 bool
MoveTab(int32 from,int32 to,bool isMoving,BRegion * updateRegion)182 Decorator::MoveTab(int32 from, int32 to, bool isMoving, BRegion* updateRegion)
183 {
184 	AutoWriteLocker _(fLocker);
185 
186 	if (_MoveTab(from, to, isMoving, updateRegion) == false)
187 		return false;
188 	if (fTabList.MoveItem(from, to) == false) {
189 		// move the tab back
190 		_MoveTab(from, to, isMoving, updateRegion);
191 		return false;
192 	}
193 	return true;
194 }
195 
196 
197 int32
TabAt(const BPoint & where) const198 Decorator::TabAt(const BPoint& where) const
199 {
200 	AutoReadLocker _(fLocker);
201 
202 	for (int32 i = 0; i < fTabList.CountItems(); i++) {
203 		Decorator::Tab* tab = fTabList.ItemAt(i);
204 		if (tab->tabRect.Contains(where))
205 			return i;
206 	}
207 
208 	return -1;
209 }
210 
211 
212 void
SetTopTab(int32 tab)213 Decorator::SetTopTab(int32 tab)
214 {
215 	AutoWriteLocker _(fLocker);
216 	fTopTab = fTabList.ItemAt(tab);
217 }
218 
219 
220 /*!	\brief Assigns a display driver to the decorator
221 	\param driver A valid DrawingEngine object
222 */
223 void
SetDrawingEngine(DrawingEngine * engine)224 Decorator::SetDrawingEngine(DrawingEngine* engine)
225 {
226 	AutoWriteLocker _(fLocker);
227 
228 	fDrawingEngine = engine;
229 	// lots of subclasses will depend on the driver for text support, so call
230 	// _DoLayout() after we have it
231 	if (fDrawingEngine != NULL) {
232 		_DoLayout();
233 		_DoOutlineLayout();
234 	}
235 }
236 
237 
238 /*!	\brief Sets the decorator's window flags
239 
240 	While this call will not update the screen, it will affect how future
241 	updates work and immediately affects input handling.
242 
243 	\param flags New value for the flags
244 */
245 void
SetFlags(int32 tab,uint32 flags,BRegion * updateRegion)246 Decorator::SetFlags(int32 tab, uint32 flags, BRegion* updateRegion)
247 {
248 	AutoWriteLocker _(fLocker);
249 
250 	// we're nice to our subclasses - we make sure B_NOT_{H|V|}_RESIZABLE
251 	// are in sync (it's only a semantical simplification, not a necessity)
252 	if ((flags & (B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE))
253 			== (B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE))
254 		flags |= B_NOT_RESIZABLE;
255 	if (flags & B_NOT_RESIZABLE)
256 		flags |= B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE;
257 
258 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
259 	if (decoratorTab == NULL)
260 		return;
261 	_SetFlags(decoratorTab, flags, updateRegion);
262 	_InvalidateFootprint();
263 		// the border might have changed (smaller/larger tab)
264 }
265 
266 
267 /*!	\brief Called whenever the system fonts are changed.
268 */
269 void
FontsChanged(DesktopSettings & settings,BRegion * updateRegion)270 Decorator::FontsChanged(DesktopSettings& settings, BRegion* updateRegion)
271 {
272 	AutoWriteLocker _(fLocker);
273 
274 	_FontsChanged(settings, updateRegion);
275 	_InvalidateFootprint();
276 }
277 
278 
279 /*!	\brief Called when a system colors change.
280 */
281 void
ColorsChanged(DesktopSettings & settings,BRegion * updateRegion)282 Decorator::ColorsChanged(DesktopSettings& settings, BRegion* updateRegion)
283 {
284 	AutoWriteLocker _(fLocker);
285 
286 	UpdateColors(settings);
287 
288 	if (updateRegion != NULL)
289 		updateRegion->Include(&GetFootprint());
290 
291 	_InvalidateBitmaps();
292 }
293 
294 
295 /*!	\brief Sets the decorator's window look
296 	\param look New value for the look
297 */
298 void
SetLook(int32 tab,DesktopSettings & settings,window_look look,BRegion * updateRect)299 Decorator::SetLook(int32 tab, DesktopSettings& settings, window_look look,
300 	BRegion* updateRect)
301 {
302 	AutoWriteLocker _(fLocker);
303 
304 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
305 	if (decoratorTab == NULL)
306 		return;
307 
308 	_SetLook(decoratorTab, settings, look, updateRect);
309 	_InvalidateFootprint();
310 		// the border very likely changed
311 }
312 
313 
314 /*!	\brief Returns the decorator's window look
315 	\return the decorator's window look
316 */
317 window_look
Look(int32 tab) const318 Decorator::Look(int32 tab) const
319 {
320 	AutoReadLocker _(fLocker);
321 	return TabAt(tab)->look;
322 }
323 
324 
325 /*!	\brief Returns the decorator's window flags
326 	\return the decorator's window flags
327 */
328 uint32
Flags(int32 tab) const329 Decorator::Flags(int32 tab) const
330 {
331 	AutoReadLocker _(fLocker);
332 	return TabAt(tab)->flags;
333 }
334 
335 
336 /*!	\brief Returns the decorator's border rectangle
337 	\return the decorator's border rectangle
338 */
339 BRect
BorderRect() const340 Decorator::BorderRect() const
341 {
342 	AutoReadLocker _(fLocker);
343 	return fBorderRect;
344 }
345 
346 
347 BRect
TitleBarRect() const348 Decorator::TitleBarRect() const
349 {
350 	AutoReadLocker _(fLocker);
351 	return fTitleBarRect;
352 }
353 
354 
355 /*!	\brief Returns the decorator's tab rectangle
356 	\return the decorator's tab rectangle
357 */
358 BRect
TabRect(int32 tab) const359 Decorator::TabRect(int32 tab) const
360 {
361 	AutoReadLocker _(fLocker);
362 
363 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
364 	if (decoratorTab == NULL)
365 		return BRect();
366 	return decoratorTab->tabRect;
367 }
368 
369 
370 BRect
TabRect(Decorator::Tab * tab) const371 Decorator::TabRect(Decorator::Tab* tab) const
372 {
373 	return tab->tabRect;
374 }
375 
376 
377 /*!	\brief Sets the close button's value.
378 
379 	Note that this does not update the button's look - it just updates the
380 	internal button value
381 
382 	\param tab The tab index
383 	\param pressed Whether the button is down or not
384 */
385 void
SetClose(int32 tab,bool pressed)386 Decorator::SetClose(int32 tab, bool pressed)
387 {
388 	AutoWriteLocker _(fLocker);
389 
390 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
391 	if (decoratorTab == NULL)
392 		return;
393 
394 	if (pressed != decoratorTab->closePressed) {
395 		decoratorTab->closePressed = pressed;
396 		DrawClose(tab);
397 	}
398 }
399 
400 
401 /*!	\brief Sets the minimize button's value.
402 
403 	Note that this does not update the button's look - it just updates the
404 	internal button value
405 
406 	\param is_down Whether the button is down or not
407 */
408 void
SetMinimize(int32 tab,bool pressed)409 Decorator::SetMinimize(int32 tab, bool pressed)
410 {
411 	AutoWriteLocker _(fLocker);
412 
413 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
414 	if (decoratorTab == NULL)
415 		return;
416 
417 	if (pressed != decoratorTab->minimizePressed) {
418 		decoratorTab->minimizePressed = pressed;
419 		DrawMinimize(tab);
420 	}
421 }
422 
423 /*!	\brief Sets the zoom button's value.
424 
425 	Note that this does not update the button's look - it just updates the
426 	internal button value
427 
428 	\param is_down Whether the button is down or not
429 */
430 void
SetZoom(int32 tab,bool pressed)431 Decorator::SetZoom(int32 tab, bool pressed)
432 {
433 	AutoWriteLocker _(fLocker);
434 
435 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
436 	if (decoratorTab == NULL)
437 		return;
438 
439 	if (pressed != decoratorTab->zoomPressed) {
440 		decoratorTab->zoomPressed = pressed;
441 		DrawZoom(tab);
442 	}
443 }
444 
445 
446 /*!	\brief Updates the value of the decorator title
447 	\param string New title value
448 */
449 void
SetTitle(int32 tab,const char * string,BRegion * updateRegion)450 Decorator::SetTitle(int32 tab, const char* string, BRegion* updateRegion)
451 {
452 	AutoWriteLocker _(fLocker);
453 
454 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
455 	if (decoratorTab == NULL)
456 		return;
457 
458 	decoratorTab->title.SetTo(string);
459 	_SetTitle(decoratorTab, string, updateRegion);
460 
461 	_InvalidateFootprint();
462 		// the border very likely changed
463 
464 	// TODO: redraw?
465 }
466 
467 
468 /*!	\brief Returns the decorator's title
469 	\return the decorator's title
470 */
471 const char*
Title(int32 tab) const472 Decorator::Title(int32 tab) const
473 {
474 	AutoReadLocker _(fLocker);
475 
476 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
477 	if (decoratorTab == NULL)
478 		return "";
479 
480 	return decoratorTab->title;
481 }
482 
483 
484 const char*
Title(Decorator::Tab * tab) const485 Decorator::Title(Decorator::Tab* tab) const
486 {
487 	AutoReadLocker _(fLocker);
488 	return tab->title;
489 }
490 
491 
492 float
TabLocation(int32 tab) const493 Decorator::TabLocation(int32 tab) const
494 {
495 	AutoReadLocker _(fLocker);
496 
497 	Decorator::Tab* decoratorTab = _TabAt(tab);
498 	if (decoratorTab == NULL)
499 		return 0.0f;
500 
501 	return (float)decoratorTab->tabOffset;
502 }
503 
504 
505 bool
SetTabLocation(int32 tab,float location,bool isShifting,BRegion * updateRegion)506 Decorator::SetTabLocation(int32 tab, float location, bool isShifting,
507 	BRegion* updateRegion)
508 {
509 	AutoWriteLocker _(fLocker);
510 
511 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
512 	if (decoratorTab == NULL)
513 		return false;
514 	if (_SetTabLocation(decoratorTab, location, isShifting, updateRegion)) {
515 		_InvalidateFootprint();
516 		return true;
517 	}
518 	return false;
519 }
520 
521 
522 
523 /*!	\brief Changes the focus value of the decorator
524 
525 	While this call will not update the screen, it will affect how future
526 	updates work.
527 
528 	\param active True if active, false if not
529 */
530 void
SetFocus(int32 tab,bool active)531 Decorator::SetFocus(int32 tab, bool active)
532 {
533 	AutoWriteLocker _(fLocker);
534 
535 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
536 	if (decoratorTab == NULL)
537 		return;
538 	decoratorTab->isFocused = active;
539 	_SetFocus(decoratorTab);
540 	// TODO: maybe it would be cleaner to handle the redraw here.
541 }
542 
543 
544 bool
IsFocus(int32 tab) const545 Decorator::IsFocus(int32 tab) const
546 {
547 	AutoReadLocker _(fLocker);
548 
549 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
550 	if (decoratorTab == NULL)
551 		return false;
552 
553 	return decoratorTab->isFocused;
554 };
555 
556 
557 bool
IsFocus(Decorator::Tab * tab) const558 Decorator::IsFocus(Decorator::Tab* tab) const
559 {
560 	AutoReadLocker _(fLocker);
561 	return tab->isFocused;
562 }
563 
564 
565 //	#pragma mark - virtual methods
566 
567 
568 /*!	\brief Returns a cached footprint if available otherwise recalculate it
569 */
570 const BRegion&
GetFootprint()571 Decorator::GetFootprint()
572 {
573 	AutoReadLocker _(fLocker);
574 
575 	if (!fFootprintValid) {
576 		fFootprint.MakeEmpty();
577 
578 		_GetFootprint(&fFootprint);
579 
580 		if (IsOutlineResizing())
581 			_GetOutlineFootprint(&fFootprint);
582 
583 		fFootprintValid = true;
584 	}
585 
586 	return fFootprint;
587 }
588 
589 
590 /*!	\brief Returns our Desktop object pointer
591 */
592 ::Desktop*
GetDesktop()593 Decorator::GetDesktop()
594 {
595 	AutoReadLocker _(fLocker);
596 	return fDesktop;
597 }
598 
599 
600 /*!	\brief Performs hit-testing for the decorator.
601 
602 	The base class provides a basic implementation, recognizing only button and
603 	tab hits. Derived classes must override/enhance it to handle borders and
604 	corners correctly.
605 
606 	\param where The point to be tested.
607 	\return Either of the following, depending on what was hit:
608 		- \c REGION_NONE: None of the decorator regions.
609 		- \c REGION_TAB: The window tab (but none of the buttons embedded).
610 		- \c REGION_CLOSE_BUTTON: The close button.
611 		- \c REGION_ZOOM_BUTTON: The zoom button.
612 		- \c REGION_MINIMIZE_BUTTON: The minimize button.
613 		- \c REGION_LEFT_BORDER: The left border.
614 		- \c REGION_RIGHT_BORDER: The right border.
615 		- \c REGION_TOP_BORDER: The top border.
616 		- \c REGION_BOTTOM_BORDER: The bottom border.
617 		- \c REGION_LEFT_TOP_CORNER: The left-top corner.
618 		- \c REGION_LEFT_BOTTOM_CORNER: The left-bottom corner.
619 		- \c REGION_RIGHT_TOP_CORNER: The right-top corner.
620 		- \c REGION_RIGHT_BOTTOM_CORNER The right-bottom corner.
621 */
622 Decorator::Region
RegionAt(BPoint where,int32 & tabIndex) const623 Decorator::RegionAt(BPoint where, int32& tabIndex) const
624 {
625 	AutoReadLocker _(fLocker);
626 
627 	tabIndex = -1;
628 
629 	for (int32 i = 0; i < fTabList.CountItems(); i++) {
630 		Decorator::Tab* tab = fTabList.ItemAt(i);
631 		if (tab->closeRect.Contains(where)) {
632 			tabIndex = i;
633 			return REGION_CLOSE_BUTTON;
634 		}
635 		if (tab->zoomRect.Contains(where)) {
636 			tabIndex = i;
637 			return REGION_ZOOM_BUTTON;
638 		}
639 		if (tab->tabRect.Contains(where)) {
640 			tabIndex = i;
641 			return REGION_TAB;
642 		}
643 	}
644 
645 	return REGION_NONE;
646 }
647 
648 
649 /*!	\brief Moves the decorator frame and all default rectangles
650 
651 	If a subclass implements this method, be sure to call Decorator::MoveBy
652 	to ensure that internal members are also updated. All members of the
653 	Decorator class are automatically moved in this method
654 
655 	\param x X Offset
656 	\param y y Offset
657 */
658 void
MoveBy(float x,float y)659 Decorator::MoveBy(float x, float y)
660 {
661 	MoveBy(BPoint(x, y));
662 }
663 
664 
665 /*!	\brief Moves the decorator frame and all default rectangles
666 
667 	If a subclass implements this method, be sure to call Decorator::MoveBy
668 	to ensure that internal members are also updated. All members of the
669 	Decorator class are automatically moved in this method
670 
671 	\param offset BPoint containing the offsets
672 */
673 void
MoveBy(BPoint offset)674 Decorator::MoveBy(BPoint offset)
675 {
676 	AutoWriteLocker _(fLocker);
677 
678 	if (fFootprintValid)
679 		fFootprint.OffsetBy(offset.x, offset.y);
680 
681 	_MoveBy(offset);
682 	_MoveOutlineBy(offset);
683 }
684 
685 
686 /*!	\brief Resizes the decorator frame
687 
688 	This is a required function for subclasses to implement - the default does
689 	nothing. Note that window resize flags should be followed and fFrame should
690 	be resized accordingly. It would also be a wise idea to ensure that the
691 	window's rectangles are not inverted.
692 
693 	\param x x offset
694 	\param y y offset
695 */
696 void
ResizeBy(float x,float y,BRegion * dirty)697 Decorator::ResizeBy(float x, float y, BRegion* dirty)
698 {
699 	ResizeBy(BPoint(x, y), dirty);
700 }
701 
702 
703 void
ResizeBy(BPoint offset,BRegion * dirty)704 Decorator::ResizeBy(BPoint offset, BRegion* dirty)
705 {
706 	AutoWriteLocker _(fLocker);
707 
708 	_ResizeBy(offset, dirty);
709 	_ResizeOutlineBy(offset, dirty);
710 
711 	_InvalidateFootprint();
712 }
713 
714 
715 void
SetOutlinesDelta(BPoint delta,BRegion * dirty)716 Decorator::SetOutlinesDelta(BPoint delta, BRegion* dirty)
717 {
718 	_SetOutlinesDelta(delta, dirty);
719 	_InvalidateFootprint();
720 }
721 
722 
723 void
ExtendDirtyRegion(Region region,BRegion & dirty)724 Decorator::ExtendDirtyRegion(Region region, BRegion& dirty)
725 {
726 	AutoReadLocker _(fLocker);
727 
728 	switch (region) {
729 		case REGION_TAB:
730 			dirty.Include(fTitleBarRect);
731 			break;
732 
733 		case REGION_CLOSE_BUTTON:
734 			if ((fTopTab->flags & B_NOT_CLOSABLE) == 0) {
735 				for (int32 i = 0; i < fTabList.CountItems(); i++)
736 					dirty.Include(fTabList.ItemAt(i)->closeRect);
737 			}
738 			break;
739 
740 		case REGION_MINIMIZE_BUTTON:
741 			if ((fTopTab->flags & B_NOT_MINIMIZABLE) == 0) {
742 				for (int32 i = 0; i < fTabList.CountItems(); i++)
743 					dirty.Include(fTabList.ItemAt(i)->minimizeRect);
744 			}
745 			break;
746 
747 		case REGION_ZOOM_BUTTON:
748 			if ((fTopTab->flags & B_NOT_ZOOMABLE) == 0) {
749 				for (int32 i = 0; i < fTabList.CountItems(); i++)
750 					dirty.Include(fTabList.ItemAt(i)->zoomRect);
751 			}
752 			break;
753 
754 		case REGION_LEFT_BORDER:
755 			if (fLeftBorder.IsValid()) {
756 				// fLeftBorder doesn't include the corners, so we have to add
757 				// them manually.
758 				BRect rect(fLeftBorder);
759 				rect.top = fTopBorder.top;
760 				rect.bottom = fBottomBorder.bottom;
761 				dirty.Include(rect);
762 			}
763 			break;
764 
765 		case REGION_RIGHT_BORDER:
766 			if (fRightBorder.IsValid()) {
767 				// fRightBorder doesn't include the corners, so we have to add
768 				// them manually.
769 				BRect rect(fRightBorder);
770 				rect.top = fTopBorder.top;
771 				rect.bottom = fBottomBorder.bottom;
772 				dirty.Include(rect);
773 			}
774 			break;
775 
776 		case REGION_TOP_BORDER:
777 			dirty.Include(fTopBorder);
778 			break;
779 
780 		case REGION_BOTTOM_BORDER:
781 			dirty.Include(fBottomBorder);
782 			break;
783 
784 		case REGION_RIGHT_BOTTOM_CORNER:
785 			if ((fTopTab->flags & B_NOT_RESIZABLE) == 0)
786 				dirty.Include(fResizeRect);
787 			break;
788 
789 		default:
790 			break;
791 	}
792 }
793 
794 
795 /*!	\brief Sets a specific highlight for a decorator region.
796 
797 	Can be overridden by derived classes, but the base class version must be
798 	called, if the highlight shall be applied.
799 
800 	\param region The decorator region.
801 	\param highlight The value identifying the kind of highlight.
802 	\param dirty The dirty region to be extended, if the highlight changes. Can
803 		be \c NULL.
804 	\return \c true, if the highlight could be applied.
805 */
806 bool
SetRegionHighlight(Region region,uint8 highlight,BRegion * dirty,int32 tab)807 Decorator::SetRegionHighlight(Region region, uint8 highlight, BRegion* dirty,
808 	int32 tab)
809 {
810 	AutoWriteLocker _(fLocker);
811 
812 	int32 index = (int32)region - 1;
813 	if (index < 0 || index >= REGION_COUNT - 1)
814 		return false;
815 
816 	if (fRegionHighlights[index] == highlight)
817 		return true;
818 	fRegionHighlights[index] = highlight;
819 
820 	if (dirty != NULL)
821 		ExtendDirtyRegion(region, *dirty);
822 
823 	return true;
824 }
825 
826 
827 bool
SetSettings(const BMessage & settings,BRegion * updateRegion)828 Decorator::SetSettings(const BMessage& settings, BRegion* updateRegion)
829 {
830 	AutoWriteLocker _(fLocker);
831 
832 	if (_SetSettings(settings, updateRegion)) {
833 		_InvalidateFootprint();
834 		return true;
835 	}
836 	return false;
837 }
838 
839 
840 bool
GetSettings(BMessage * settings) const841 Decorator::GetSettings(BMessage* settings) const
842 {
843 	AutoReadLocker _(fLocker);
844 
845 	if (!fTitleBarRect.IsValid())
846 		return false;
847 
848 	if (settings->AddRect("tab frame", fTitleBarRect) != B_OK)
849 		return false;
850 
851 	if (settings->AddFloat("border width", fBorderWidth) != B_OK)
852 		return false;
853 
854 	// TODO only add the location of the tab of the window who requested the
855 	// settings
856 	for (int32 i = 0; i < fTabList.CountItems(); i++) {
857 		Decorator::Tab* tab = _TabAt(i);
858 		if (settings->AddFloat("tab location", (float)tab->tabOffset) != B_OK)
859 			return false;
860 	}
861 
862 	return true;
863 }
864 
865 
866 void
GetSizeLimits(int32 * minWidth,int32 * minHeight,int32 * maxWidth,int32 * maxHeight) const867 Decorator::GetSizeLimits(int32* minWidth, int32* minHeight,
868 	int32* maxWidth, int32* maxHeight) const
869 {
870 	AutoReadLocker _(fLocker);
871 
872 	float minTabSize = 0;
873 	if (CountTabs() > 0)
874 		minTabSize = _TabAt(0)->minTabSize;
875 
876 	if (fTitleBarRect.IsValid()) {
877 		*minWidth = (int32)roundf(max_c(*minWidth,
878 			minTabSize - 2 * fBorderWidth));
879 	}
880 	if (fResizeRect.IsValid()) {
881 		*minHeight = (int32)roundf(max_c(*minHeight,
882 			fResizeRect.Height() - fBorderWidth));
883 	}
884 }
885 
886 
887 //! draws the tab, title, and buttons
888 void
DrawTab(int32 tabIndex)889 Decorator::DrawTab(int32 tabIndex)
890 {
891 	AutoReadLocker _(fLocker);
892 
893 	Decorator::Tab* tab = fTabList.ItemAt(tabIndex);
894 	if (tab == NULL)
895 		return;
896 
897 	_DrawTab(tab, tab->tabRect);
898 	_DrawZoom(tab, false, tab->zoomRect);
899 	_DrawMinimize(tab, false, tab->minimizeRect);
900 	_DrawTitle(tab, tab->tabRect);
901 	_DrawClose(tab, false, tab->closeRect);
902 }
903 
904 
905 //! draws the title
906 void
DrawTitle(int32 tab)907 Decorator::DrawTitle(int32 tab)
908 {
909 	AutoReadLocker _(fLocker);
910 
911 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
912 	if (decoratorTab == NULL)
913 		return;
914 	_DrawTitle(decoratorTab, decoratorTab->tabRect);
915 }
916 
917 
918 //! Draws the close button
919 void
DrawClose(int32 tab)920 Decorator::DrawClose(int32 tab)
921 {
922 	AutoReadLocker _(fLocker);
923 
924 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
925 	if (decoratorTab == NULL)
926 		return;
927 
928 	_DrawClose(decoratorTab, true, decoratorTab->closeRect);
929 }
930 
931 
932 //! draws the minimize button
933 void
DrawMinimize(int32 tab)934 Decorator::DrawMinimize(int32 tab)
935 {
936 	AutoReadLocker _(fLocker);
937 
938 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
939 	if (decoratorTab == NULL)
940 		return;
941 
942 	_DrawTab(decoratorTab, decoratorTab->minimizeRect);
943 }
944 
945 
946 //! draws the zoom button
947 void
DrawZoom(int32 tab)948 Decorator::DrawZoom(int32 tab)
949 {
950 	AutoReadLocker _(fLocker);
951 
952 	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
953 	if (decoratorTab == NULL)
954 		return;
955 	_DrawZoom(decoratorTab, true, decoratorTab->zoomRect);
956 }
957 
958 
959 rgb_color
UIColor(color_which which)960 Decorator::UIColor(color_which which)
961 {
962 	AutoReadLocker _(fLocker);
963 	DesktopSettings settings(fDesktop);
964 	return settings.UIColor(which);
965 }
966 
967 
968 float
BorderWidth()969 Decorator::BorderWidth()
970 {
971 	AutoReadLocker _(fLocker);
972 	return fBorderWidth;
973 }
974 
975 
976 float
TabHeight()977 Decorator::TabHeight()
978 {
979 	AutoReadLocker _(fLocker);
980 
981 	if (fTitleBarRect.IsValid())
982 		return fTitleBarRect.Height();
983 
984 	return fBorderWidth;
985 }
986 
987 
988 // #pragma mark - Protected methods
989 
990 
991 Decorator::Tab*
_AllocateNewTab()992 Decorator::_AllocateNewTab()
993 {
994 	Decorator::Tab* tab = new(std::nothrow) Decorator::Tab;
995 	if (tab == NULL)
996 		return NULL;
997 
998 	// Set appropriate colors based on the current focus value. In this case,
999 	// each decorator defaults to not having the focus.
1000 	_SetFocus(tab);
1001 	return tab;
1002 }
1003 
1004 
1005 void
_DrawTabs(BRect rect)1006 Decorator::_DrawTabs(BRect rect)
1007 {
1008 	Decorator::Tab* focusTab = NULL;
1009 	for (int32 i = 0; i < fTabList.CountItems(); i++) {
1010 		Decorator::Tab* tab = fTabList.ItemAt(i);
1011 		if (tab->isFocused) {
1012 			focusTab = tab;
1013 			continue;
1014 		}
1015 		_DrawTab(tab, rect);
1016 	}
1017 
1018 	if (focusTab != NULL)
1019 		_DrawTab(focusTab, rect);
1020 }
1021 
1022 
1023 //! Hook function called when the decorator changes focus
1024 void
_SetFocus(Decorator::Tab * tab)1025 Decorator::_SetFocus(Decorator::Tab* tab)
1026 {
1027 }
1028 
1029 
1030 bool
_SetTabLocation(Decorator::Tab * tab,float location,bool isShifting,BRegion *)1031 Decorator::_SetTabLocation(Decorator::Tab* tab, float location, bool isShifting,
1032 	BRegion* /*updateRegion*/)
1033 {
1034 	return false;
1035 }
1036 
1037 
1038 Decorator::Tab*
_TabAt(int32 index) const1039 Decorator::_TabAt(int32 index) const
1040 {
1041 	return static_cast<Decorator::Tab*>(fTabList.ItemAt(index));
1042 }
1043 
1044 
1045 void
_FontsChanged(DesktopSettings & settings,BRegion * updateRegion)1046 Decorator::_FontsChanged(DesktopSettings& settings, BRegion* updateRegion)
1047 {
1048 	// get previous extent
1049 	if (updateRegion != NULL)
1050 		updateRegion->Include(&GetFootprint());
1051 
1052 	_InvalidateBitmaps();
1053 
1054 	_UpdateFont(settings);
1055 	_DoLayout();
1056 	_DoOutlineLayout();
1057 
1058 	_InvalidateFootprint();
1059 	if (updateRegion != NULL)
1060 		updateRegion->Include(&GetFootprint());
1061 }
1062 
1063 
1064 void
_SetLook(Decorator::Tab * tab,DesktopSettings & settings,window_look look,BRegion * updateRegion)1065 Decorator::_SetLook(Decorator::Tab* tab, DesktopSettings& settings,
1066 	window_look look, BRegion* updateRegion)
1067 {
1068 	// TODO: we could be much smarter about the update region
1069 
1070 	// get previous extent
1071 	if (updateRegion != NULL)
1072 		updateRegion->Include(&GetFootprint());
1073 
1074 	tab->look = look;
1075 
1076 	_UpdateFont(settings);
1077 	_DoLayout();
1078 	_DoOutlineLayout();
1079 
1080 	_InvalidateFootprint();
1081 	if (updateRegion != NULL)
1082 		updateRegion->Include(&GetFootprint());
1083 }
1084 
1085 
1086 void
_SetFlags(Decorator::Tab * tab,uint32 flags,BRegion * updateRegion)1087 Decorator::_SetFlags(Decorator::Tab* tab, uint32 flags, BRegion* updateRegion)
1088 {
1089 	// TODO: we could be much smarter about the update region
1090 
1091 	// get previous extent
1092 	if (updateRegion != NULL)
1093 		updateRegion->Include(&GetFootprint());
1094 
1095 	tab->flags = flags;
1096 	_DoLayout();
1097 	_DoOutlineLayout();
1098 
1099 	_InvalidateFootprint();
1100 	if (updateRegion != NULL)
1101 		updateRegion->Include(&GetFootprint());
1102 }
1103 
1104 
1105 void
_MoveBy(BPoint offset)1106 Decorator::_MoveBy(BPoint offset)
1107 {
1108 	for (int32 i = 0; i < fTabList.CountItems(); i++) {
1109 		Decorator::Tab* tab = fTabList.ItemAt(i);
1110 
1111 		tab->zoomRect.OffsetBy(offset);
1112 		tab->closeRect.OffsetBy(offset);
1113 		tab->minimizeRect.OffsetBy(offset);
1114 		tab->tabRect.OffsetBy(offset);
1115 	}
1116 	fTitleBarRect.OffsetBy(offset);
1117 	fFrame.OffsetBy(offset);
1118 	fResizeRect.OffsetBy(offset);
1119 	fBorderRect.OffsetBy(offset);
1120 }
1121 
1122 
1123 void
_MoveOutlineBy(BPoint offset)1124 Decorator::_MoveOutlineBy(BPoint offset)
1125 {
1126 	fOutlineBorderRect.OffsetBy(offset);
1127 
1128 	fLeftOutlineBorder.OffsetBy(offset);
1129 	fRightOutlineBorder.OffsetBy(offset);
1130 	fTopOutlineBorder.OffsetBy(offset);
1131 	fBottomOutlineBorder.OffsetBy(offset);
1132 }
1133 
1134 
1135 void
_ResizeOutlineBy(BPoint offset,BRegion * dirty)1136 Decorator::_ResizeOutlineBy(BPoint offset, BRegion* dirty)
1137 {
1138 	fOutlineBorderRect.right += offset.x;
1139 	fOutlineBorderRect.bottom += offset.y;
1140 
1141 	fLeftOutlineBorder.bottom += offset.y;
1142 	fTopOutlineBorder.right += offset.x;
1143 
1144 	fRightOutlineBorder.OffsetBy(offset.x, 0.0);
1145 	fRightOutlineBorder.bottom += offset.y;
1146 
1147 	fBottomOutlineBorder.OffsetBy(0.0, offset.y);
1148 	fBottomOutlineBorder.right += offset.x;
1149 }
1150 
1151 
1152 void
_SetOutlinesDelta(BPoint delta,BRegion * dirty)1153 Decorator::_SetOutlinesDelta(BPoint delta, BRegion* dirty)
1154 {
1155 	BPoint offset = delta - fOutlinesDelta;
1156 	fOutlinesDelta = delta;
1157 
1158 	dirty->Include(fLeftOutlineBorder);
1159 	dirty->Include(fRightOutlineBorder);
1160 	dirty->Include(fTopOutlineBorder);
1161 	dirty->Include(fBottomOutlineBorder);
1162 
1163 	fOutlineBorderRect.right += offset.x;
1164 	fOutlineBorderRect.bottom += offset.y;
1165 
1166 	fLeftOutlineBorder.bottom += offset.y;
1167 	fTopOutlineBorder.right += offset.x;
1168 
1169 	fRightOutlineBorder.OffsetBy(offset.x, 0.0);
1170 	fRightOutlineBorder.bottom	+= offset.y;
1171 
1172 	fBottomOutlineBorder.OffsetBy(0.0, offset.y);
1173 	fBottomOutlineBorder.right	+= offset.x;
1174 
1175 	dirty->Include(fLeftOutlineBorder);
1176 	dirty->Include(fRightOutlineBorder);
1177 	dirty->Include(fTopOutlineBorder);
1178 	dirty->Include(fBottomOutlineBorder);
1179 }
1180 
1181 
1182 bool
_SetSettings(const BMessage & settings,BRegion * updateRegion)1183 Decorator::_SetSettings(const BMessage& settings, BRegion* updateRegion)
1184 {
1185 	return false;
1186 }
1187 
1188 
1189 /*!	\brief Returns the "footprint" of the entire window, including decorator
1190 
1191 	This function is required by all subclasses.
1192 
1193 	\param region Region to be changed to represent the window's screen
1194 		footprint
1195 */
1196 void
_GetFootprint(BRegion * region)1197 Decorator::_GetFootprint(BRegion *region)
1198 {
1199 }
1200 
1201 
1202 void
_GetOutlineFootprint(BRegion * region)1203 Decorator::_GetOutlineFootprint(BRegion* region)
1204 {
1205 	if (region == NULL)
1206 		return;
1207 
1208 	region->Include(fTopOutlineBorder);
1209 	region->Include(fLeftOutlineBorder);
1210 	region->Include(fRightOutlineBorder);
1211 	region->Include(fBottomOutlineBorder);
1212 }
1213 
1214 
1215 void
_InvalidateFootprint()1216 Decorator::_InvalidateFootprint()
1217 {
1218 	fFootprintValid = false;
1219 }
1220 
1221 
1222 void
_InvalidateBitmaps()1223 Decorator::_InvalidateBitmaps()
1224 {
1225 	for (int32 i = 0; i < fTabList.CountItems(); i++) {
1226 		Decorator::Tab* tab = static_cast<Decorator::Tab*>(_TabAt(i));
1227 		for (int32 index = 0; index < 4; index++) {
1228 			tab->closeBitmaps[index] = NULL;
1229 			tab->minimizeBitmaps[index] = NULL;
1230 			tab->zoomBitmaps[index] = NULL;
1231 		}
1232 	}
1233 }
1234