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