1/*
2 * Copyright 2001-2011, Haiku, Inc.
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 *		Philippe Saint-Pierre, stpere@gmail.com
9 *		Ryan Leavengood <leavengood@gmail.com>
10 *		Clemens Zeidler <haiku@clemens-zeidler.de>
11 *		Ingo Weinhold <ingo_weinhold@gmx.de>
12 */
13
14
15/*!	Default and fallback decorator for the app_server - the yellow tabs */
16
17
18#include "DefaultDecorator.h"
19
20#include <algorithm>
21#include <cmath>
22#include <new>
23#include <stdio.h>
24
25#include <Autolock.h>
26#include <Debug.h>
27#include <GradientLinear.h>
28#include <Rect.h>
29#include <View.h>
30
31#include <WindowPrivate.h>
32
33#include "BitmapDrawingEngine.h"
34#include "DesktopSettings.h"
35#include "DrawingEngine.h"
36#include "DrawState.h"
37#include "FontManager.h"
38#include "PatternHandler.h"
39#include "ServerBitmap.h"
40
41
42//#define DEBUG_DECORATOR
43#ifdef DEBUG_DECORATOR
44#	define STRACE(x) printf x
45#else
46#	define STRACE(x) ;
47#endif
48
49
50DefaultDecorator::Tab::Tab()
51	:
52	tabOffset(0),
53	tabLocation(0.0),
54	isHighlighted(false)
55{
56	closeBitmaps[0] = closeBitmaps[1] = closeBitmaps[2] = closeBitmaps[3]
57		= zoomBitmaps[0] = zoomBitmaps[1] = zoomBitmaps[2] = zoomBitmaps[3]
58		= NULL;
59}
60
61
62static const float kBorderResizeLength = 22.0;
63static const float kResizeKnobSize = 18.0;
64
65
66static inline uint8
67blend_color_value(uint8 a, uint8 b, float position)
68{
69	int16 delta = (int16)b - a;
70	int32 value = a + (int32)(position * delta);
71	if (value > 255)
72		return 255;
73	if (value < 0)
74		return 0;
75
76	return (uint8)value;
77}
78
79
80//	#pragma mark -
81
82
83const rgb_color DefaultDecorator::kFrameColors[4] = {
84	{ 152, 152, 152, 255 },
85	{ 240, 240, 240, 255 },
86	{ 152, 152, 152, 255 },
87	{ 108, 108, 108, 255 }
88};
89
90
91// TODO: get rid of DesktopSettings here, and introduce private accessor
92//	methods to the Decorator base class
93DefaultDecorator::DefaultDecorator(DesktopSettings& settings, BRect rect)
94	:
95	Decorator(settings, rect),
96	// focus color constants
97	kFocusFrameColor(settings.UIColor(B_WINDOW_BORDER_COLOR)),
98	kFocusTabColor(settings.UIColor(B_WINDOW_TAB_COLOR)),
99	kFocusTabColorLight(tint_color(kFocusTabColor,
100 		(B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2)),
101	kFocusTabColorBevel(tint_color(kFocusTabColor, B_LIGHTEN_2_TINT)),
102	kFocusTabColorShadow(tint_color(kFocusTabColor,
103 		(B_DARKEN_1_TINT + B_NO_TINT) / 2)),
104	kFocusTextColor(settings.UIColor(B_WINDOW_TEXT_COLOR)),
105	// non-focus color constants
106	kNonFocusFrameColor(settings.UIColor(B_WINDOW_INACTIVE_BORDER_COLOR)),
107	kNonFocusTabColor(settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR)),
108	kNonFocusTabColorLight(tint_color(kNonFocusTabColor,
109 		(B_LIGHTEN_MAX_TINT + B_LIGHTEN_2_TINT) / 2)),
110	kNonFocusTabColorBevel(tint_color(kNonFocusTabColor, B_LIGHTEN_2_TINT)),
111	kNonFocusTabColorShadow(tint_color(kNonFocusTabColor,
112 		(B_DARKEN_1_TINT + B_NO_TINT) / 2)),
113	kNonFocusTextColor(settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR)),
114
115	fOldMovingTab(0, 0, -1, -1)
116{
117	// TODO: If the decorator was created with a frame too small, it should
118	// resize itself!
119
120	STRACE(("DefaultDecorator:\n"));
121	STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n",
122		rect.left, rect.top, rect.right, rect.bottom));
123}
124
125
126DefaultDecorator::~DefaultDecorator()
127{
128	STRACE(("DefaultDecorator: ~DefaultDecorator()\n"));
129}
130
131
132float
133DefaultDecorator::TabLocation(int32 tab) const
134{
135	DefaultDecorator::Tab* decoratorTab = _TabAt(tab);
136	if (decoratorTab == NULL)
137		return 0.0f;
138	return (float)decoratorTab->tabOffset;
139}
140
141
142bool
143DefaultDecorator::GetSettings(BMessage* settings) const
144{
145	if (!fTitleBarRect.IsValid())
146		return false;
147
148	if (settings->AddRect("tab frame", fTitleBarRect) != B_OK)
149		return false;
150
151	if (settings->AddFloat("border width", fBorderWidth) != B_OK)
152		return false;
153
154	// TODO only add the location of the tab of the window who requested the
155	// settings
156	for (int32 i = 0; i < fTabList.CountItems(); i++) {
157		DefaultDecorator::Tab* tab = _TabAt(i);
158		if (settings->AddFloat("tab location", (float)tab->tabOffset) != B_OK)
159			return false;
160	}
161	return true;
162}
163
164
165// #pragma mark -
166
167
168void
169DefaultDecorator::Draw(BRect update)
170{
171	STRACE(("DefaultDecorator: Draw(%.1f,%.1f,%.1f,%.1f)\n",
172		update.left, update.top, update.right, update.bottom));
173
174	// We need to draw a few things: the tab, the resize knob, the borders,
175	// and the buttons
176	fDrawingEngine->SetDrawState(&fDrawState);
177
178	_DrawFrame(update);
179	_DrawTabs(update);
180}
181
182
183void
184DefaultDecorator::Draw()
185{
186	// Easy way to draw everything - no worries about drawing only certain
187	// things
188	fDrawingEngine->SetDrawState(&fDrawState);
189
190	_DrawFrame(BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom()));
191	_DrawTabs(fTitleBarRect);
192}
193
194
195void
196DefaultDecorator::GetSizeLimits(int32* minWidth, int32* minHeight,
197	int32* maxWidth, int32* maxHeight) const
198{
199	float minTabSize = 0;
200	if (CountTabs() > 0)
201		minTabSize = _TabAt(0)->minTabSize;
202	if (fTitleBarRect.IsValid()) {
203		*minWidth = (int32)roundf(max_c(*minWidth,
204			minTabSize - 2 * fBorderWidth));
205	}
206	if (fResizeRect.IsValid()) {
207		*minHeight = (int32)roundf(max_c(*minHeight,
208			fResizeRect.Height() - fBorderWidth));
209	}
210}
211
212
213Decorator::Region
214DefaultDecorator::RegionAt(BPoint where, int32& tab) const
215{
216	// Let the base class version identify hits of the buttons and the tab.
217	Region region = Decorator::RegionAt(where, tab);
218	if (region != REGION_NONE)
219		return region;
220
221	// check the resize corner
222	if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where))
223		return REGION_RIGHT_BOTTOM_CORNER;
224
225	// hit-test the borders
226	if (fLeftBorder.Contains(where))
227		return REGION_LEFT_BORDER;
228	if (fTopBorder.Contains(where))
229		return REGION_TOP_BORDER;
230
231	// Part of the bottom and right borders may be a resize-region, so we have
232	// to check explicitly, if it has been it.
233	if (fRightBorder.Contains(where))
234		region = REGION_RIGHT_BORDER;
235	else if (fBottomBorder.Contains(where))
236		region = REGION_BOTTOM_BORDER;
237	else
238		return REGION_NONE;
239
240	// check resize area
241	if ((fTopTab->flags & B_NOT_RESIZABLE) == 0
242		&& (fTopTab->look == B_TITLED_WINDOW_LOOK
243			|| fTopTab->look == B_FLOATING_WINDOW_LOOK
244			|| fTopTab->look == B_MODAL_WINDOW_LOOK
245			|| fTopTab->look == kLeftTitledWindowLook)) {
246		BRect resizeRect(BPoint(fBottomBorder.right - kBorderResizeLength,
247			fBottomBorder.bottom - kBorderResizeLength),
248			fBottomBorder.RightBottom());
249		if (resizeRect.Contains(where))
250			return REGION_RIGHT_BOTTOM_CORNER;
251	}
252
253	return region;
254}
255
256
257bool
258DefaultDecorator::SetRegionHighlight(Region region, uint8 highlight,
259	BRegion* dirty, int32 tabIndex)
260{
261	DefaultDecorator::Tab* tab = _TabAt(tabIndex);
262	if (tab != NULL) {
263		tab->isHighlighted = highlight != 0;
264		// Invalidate the bitmap caches for the close/zoom button, when the
265		// highlight changes.
266		switch (region) {
267			case REGION_CLOSE_BUTTON:
268				if (highlight != RegionHighlight(region))
269					memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps));
270				break;
271			case REGION_ZOOM_BUTTON:
272				if (highlight != RegionHighlight(region))
273					memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps));
274				break;
275			default:
276				break;
277		}
278	}
279
280	return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex);
281}
282
283
284void
285DefaultDecorator::ExtendDirtyRegion(Region region, BRegion& dirty)
286{
287	switch (region) {
288		case REGION_TAB:
289			dirty.Include(fTitleBarRect);
290			break;
291
292		case REGION_CLOSE_BUTTON:
293			if ((fTopTab->flags & B_NOT_CLOSABLE) == 0)
294				for (int32 i = 0; i < fTabList.CountItems(); i++)
295					dirty.Include(fTabList.ItemAt(i)->closeRect);
296			break;
297
298		case REGION_ZOOM_BUTTON:
299			if ((fTopTab->flags & B_NOT_ZOOMABLE) == 0)
300				for (int32 i = 0; i < fTabList.CountItems(); i++)
301					dirty.Include(fTabList.ItemAt(i)->zoomRect);
302			break;
303
304		case REGION_LEFT_BORDER:
305			if (fLeftBorder.IsValid()) {
306				// fLeftBorder doesn't include the corners, so we have to add
307				// them manually.
308				BRect rect(fLeftBorder);
309				rect.top = fTopBorder.top;
310				rect.bottom = fBottomBorder.bottom;
311				dirty.Include(rect);
312			}
313			break;
314
315		case REGION_RIGHT_BORDER:
316			if (fRightBorder.IsValid()) {
317				// fRightBorder doesn't include the corners, so we have to add
318				// them manually.
319				BRect rect(fRightBorder);
320				rect.top = fTopBorder.top;
321				rect.bottom = fBottomBorder.bottom;
322				dirty.Include(rect);
323			}
324			break;
325
326		case REGION_TOP_BORDER:
327			dirty.Include(fTopBorder);
328			break;
329
330		case REGION_BOTTOM_BORDER:
331			dirty.Include(fBottomBorder);
332			break;
333
334		case REGION_RIGHT_BOTTOM_CORNER:
335			if ((fTopTab->flags & B_NOT_RESIZABLE) == 0)
336				dirty.Include(fResizeRect);
337			break;
338
339		default:
340			break;
341	}
342}
343
344
345float
346DefaultDecorator::BorderWidth()
347{
348	return fBorderWidth;
349}
350
351
352float
353DefaultDecorator::TabHeight()
354{
355	if (fTitleBarRect.IsValid())
356		return fTitleBarRect.Height();
357	return BorderWidth();
358}
359
360
361void
362DefaultDecorator::_DoLayout()
363{
364	STRACE(("DefaultDecorator: Do Layout\n"));
365	// Here we determine the size of every rectangle that we use
366	// internally when we are given the size of the client rectangle.
367
368	bool hasTab = false;
369
370	switch ((int)fTopTab->look) {
371		case B_MODAL_WINDOW_LOOK:
372			fBorderWidth = 5;
373			break;
374
375		case B_TITLED_WINDOW_LOOK:
376		case B_DOCUMENT_WINDOW_LOOK:
377			hasTab = true;
378			fBorderWidth = 5;
379			break;
380		case B_FLOATING_WINDOW_LOOK:
381		case kLeftTitledWindowLook:
382			hasTab = true;
383			fBorderWidth = 3;
384			break;
385
386		case B_BORDERED_WINDOW_LOOK:
387			fBorderWidth = 1;
388			break;
389
390		default:
391			fBorderWidth = 0;
392	}
393
394	// calculate left/top/right/bottom borders
395	if (fBorderWidth > 0) {
396		// NOTE: no overlapping, the left and right border rects
397		// don't include the corners!
398		fLeftBorder.Set(fFrame.left - fBorderWidth, fFrame.top,
399			fFrame.left - 1, fFrame.bottom);
400
401		fRightBorder.Set(fFrame.right + 1, fFrame.top ,
402			fFrame.right + fBorderWidth, fFrame.bottom);
403
404		fTopBorder.Set(fFrame.left - fBorderWidth, fFrame.top - fBorderWidth,
405			fFrame.right + fBorderWidth, fFrame.top - 1);
406
407		fBottomBorder.Set(fFrame.left - fBorderWidth, fFrame.bottom + 1,
408			fFrame.right + fBorderWidth, fFrame.bottom + fBorderWidth);
409	} else {
410		// no border
411		fLeftBorder.Set(0.0, 0.0, -1.0, -1.0);
412		fRightBorder.Set(0.0, 0.0, -1.0, -1.0);
413		fTopBorder.Set(0.0, 0.0, -1.0, -1.0);
414		fBottomBorder.Set(0.0, 0.0, -1.0, -1.0);
415	}
416
417	// calculate resize rect
418	if (fBorderWidth > 1) {
419		fResizeRect.Set(fBottomBorder.right - kResizeKnobSize,
420			fBottomBorder.bottom - kResizeKnobSize, fBottomBorder.right,
421			fBottomBorder.bottom);
422	} else {
423		// no border or one pixel border (menus and such)
424		fResizeRect.Set(0, 0, -1, -1);
425	}
426
427	if (hasTab) {
428		_DoTabLayout();
429		return;
430	} else {
431		// no tab
432		for (int32 i = 0; i < fTabList.CountItems(); i++) {
433			Decorator::Tab* tab = fTabList.ItemAt(i);
434			tab->tabRect.Set(0.0, 0.0, -1.0, -1.0);
435		}
436		fTabsRegion.MakeEmpty();
437		fTitleBarRect.Set(0.0, 0.0, -1.0, -1.0);
438	}
439}
440
441
442void
443DefaultDecorator::_DoTabLayout()
444{
445	float tabOffset = 0;
446	if (fTabList.CountItems() == 1) {
447		float tabSize;
448		tabOffset = _SingleTabOffsetAndSize(tabSize);
449	}
450
451	float sumTabWidth = 0;
452	// calculate our tab rect
453	for (int32 i = 0; i < fTabList.CountItems(); i++) {
454		DefaultDecorator::Tab* tab = _TabAt(i);
455
456		BRect& tabRect = tab->tabRect;
457		// distance from one item of the tab bar to another.
458		// In this case the text and close/zoom rects
459		tab->textOffset = _DefaultTextOffset();
460
461		font_height fontHeight;
462		fDrawState.Font().GetHeight(fontHeight);
463
464		if (tab->look != kLeftTitledWindowLook) {
465			tabRect.Set(fFrame.left - fBorderWidth,
466				fFrame.top - fBorderWidth
467					- ceilf(fontHeight.ascent + fontHeight.descent + 7.0),
468				((fFrame.right - fFrame.left) < 35.0 ?
469					fFrame.left + 35.0 : fFrame.right) + fBorderWidth,
470				fFrame.top - fBorderWidth);
471		} else {
472			tabRect.Set(fFrame.left - fBorderWidth
473				- ceilf(fontHeight.ascent + fontHeight.descent + 5.0),
474					fFrame.top - fBorderWidth, fFrame.left - fBorderWidth,
475				fFrame.bottom + fBorderWidth);
476		}
477
478		// format tab rect for a floating window - make the rect smaller
479		if (tab->look == B_FLOATING_WINDOW_LOOK) {
480			tabRect.InsetBy(0, 2);
481			tabRect.OffsetBy(0, 2);
482		}
483
484		float offset;
485		float size;
486		float inset;
487		_GetButtonSizeAndOffset(tabRect, &offset, &size, &inset);
488
489		// tab->minTabSize contains just the room for the buttons
490		tab->minTabSize = inset * 2 + tab->textOffset;
491		if ((tab->flags & B_NOT_CLOSABLE) == 0)
492			tab->minTabSize += offset + size;
493		if ((tab->flags & B_NOT_ZOOMABLE) == 0)
494			tab->minTabSize += offset + size;
495
496		// tab->maxTabSize contains tab->minTabSize + the width required for the
497		// title
498		tab->maxTabSize = fDrawingEngine
499			? ceilf(fDrawingEngine->StringWidth(Title(tab), strlen(Title(tab)),
500				fDrawState.Font())) : 0.0;
501		if (tab->maxTabSize > 0.0)
502			tab->maxTabSize += tab->textOffset;
503		tab->maxTabSize += tab->minTabSize;
504
505		float tabSize = (tab->look != kLeftTitledWindowLook
506			? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2;
507		if (tabSize < tab->minTabSize)
508			tabSize = tab->minTabSize;
509		if (tabSize > tab->maxTabSize)
510			tabSize = tab->maxTabSize;
511
512		// layout buttons and truncate text
513		if (tab->look != kLeftTitledWindowLook)
514			tabRect.right = tabRect.left + tabSize;
515		else
516			tabRect.bottom = tabRect.top + tabSize;
517
518		// make sure fTabOffset is within limits and apply it to
519		// the tabRect
520		tab->tabOffset = (uint32)tabOffset;
521		if (tab->tabLocation != 0.0 && fTabList.CountItems() == 1
522			&& tab->tabOffset > (fRightBorder.right - fLeftBorder.left
523				- tabRect.Width())) {
524			tab->tabOffset = uint32(fRightBorder.right - fLeftBorder.left
525				- tabRect.Width());
526		}
527		tabRect.OffsetBy(tab->tabOffset, 0);
528		tabOffset += tabRect.Width();
529
530		sumTabWidth += tabRect.Width();
531	}
532
533	float windowWidth = fFrame.Width() + 2 * fBorderWidth;
534	if (CountTabs() > 1 && sumTabWidth > windowWidth)
535		_DistributeTabSize(sumTabWidth - windowWidth);
536
537	// finally, layout the buttons and text within the tab rect
538	for (int32 i = 0; i < fTabList.CountItems(); i++) {
539		Decorator::Tab* tab = fTabList.ItemAt(i);
540
541		if (i == 0)
542			fTitleBarRect = tab->tabRect;
543		else
544			fTitleBarRect = fTitleBarRect | tab->tabRect;
545
546		_LayoutTabItems(tab, tab->tabRect);
547	}
548	fTabsRegion = fTitleBarRect;
549}
550
551
552static bool
553int_equal(float x, float y)
554{
555	return abs(x - y) <= 1;
556}
557
558
559void
560DefaultDecorator::_DistributeTabSize(float delta)
561{
562	ASSERT(CountTabs() > 1);
563
564	float maxTabSize = 0;
565	float secMaxTabSize = 0;
566	int32 nTabsWithMaxSize = 0;
567	for (int32 i = 0; i < fTabList.CountItems(); i++) {
568		Decorator::Tab* tab = fTabList.ItemAt(i);
569		float tabWidth = tab->tabRect.Width();
570		if (int_equal(maxTabSize, tabWidth)) {
571			nTabsWithMaxSize++;
572			continue;
573		}
574		if (maxTabSize < tabWidth) {
575			secMaxTabSize = maxTabSize;
576			maxTabSize = tabWidth;
577			nTabsWithMaxSize = 1;
578		} else if (secMaxTabSize <= tabWidth)
579			secMaxTabSize = tabWidth;
580	}
581
582	float minus = ceil(std::min(maxTabSize - secMaxTabSize, delta));
583	delta -= minus;
584	minus /= nTabsWithMaxSize;
585
586	Decorator::Tab* prevTab = NULL;
587	for (int32 i = 0; i < fTabList.CountItems(); i++) {
588		Decorator::Tab* tab = fTabList.ItemAt(i);
589		if (int_equal(maxTabSize, tab->tabRect.Width()))
590			tab->tabRect.right -= minus;
591
592		if (prevTab) {
593			tab->tabRect.OffsetBy(prevTab->tabRect.right - tab->tabRect.left,
594				0);
595		}
596
597		prevTab = tab;
598	}
599
600	if (delta > 0) {
601		_DistributeTabSize(delta);
602		return;
603	}
604
605	// done
606	prevTab->tabRect.right = floor(fFrame.right + fBorderWidth);
607
608	for (int32 i = 0; i < fTabList.CountItems(); i++) {
609		DefaultDecorator::Tab* tab = _TabAt(i);
610		tab->tabOffset = uint32(tab->tabRect.left - fLeftBorder.left);
611	}
612}
613
614
615Decorator::Tab*
616DefaultDecorator::_AllocateNewTab()
617{
618	Decorator::Tab* tab = new(std::nothrow) DefaultDecorator::Tab;
619	if (tab == NULL)
620		return NULL;
621	// Set appropriate colors based on the current focus value. In this case,
622	// each decorator defaults to not having the focus.
623	_SetFocus(tab);
624	return tab;
625}
626
627
628DefaultDecorator::Tab*
629DefaultDecorator::_TabAt(int32 index) const
630{
631	return static_cast<DefaultDecorator::Tab*>(fTabList.ItemAt(index));
632}
633
634
635void
636DefaultDecorator::_DrawFrame(BRect invalid)
637{
638	STRACE(("_DrawFrame(%f,%f,%f,%f)\n", invalid.left, invalid.top,
639		invalid.right, invalid.bottom));
640
641	// NOTE: the DrawingEngine needs to be locked for the entire
642	// time for the clipping to stay valid for this decorator
643
644	if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
645		return;
646
647	if (fBorderWidth <= 0)
648		return;
649
650	// Draw the border frame
651	BRect r = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom());
652	switch ((int)fTopTab->look) {
653		case B_TITLED_WINDOW_LOOK:
654		case B_DOCUMENT_WINDOW_LOOK:
655		case B_MODAL_WINDOW_LOOK:
656		{
657			// top
658			if (invalid.Intersects(fTopBorder)) {
659				ComponentColors colors;
660				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);
661
662				for (int8 i = 0; i < 5; i++) {
663					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
664						BPoint(r.right - i, r.top + i), colors[i]);
665				}
666				if (fTitleBarRect.IsValid()) {
667					// grey along the bottom of the tab
668					// (overwrites "white" from frame)
669					fDrawingEngine->StrokeLine(
670						BPoint(fTitleBarRect.left + 2,
671							fTitleBarRect.bottom + 1),
672						BPoint(fTitleBarRect.right - 2,
673							fTitleBarRect.bottom + 1),
674						colors[2]);
675				}
676			}
677			// left
678			if (invalid.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
679				ComponentColors colors;
680				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
681
682				for (int8 i = 0; i < 5; i++) {
683					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
684						BPoint(r.left + i, r.bottom - i), colors[i]);
685				}
686			}
687			// bottom
688			if (invalid.Intersects(fBottomBorder)) {
689				ComponentColors colors;
690				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);
691
692				for (int8 i = 0; i < 5; i++) {
693					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
694						BPoint(r.right - i, r.bottom - i),
695						colors[(4 - i) == 4 ? 5 : (4 - i)]);
696				}
697			}
698			// right
699			if (invalid.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
700				ComponentColors colors;
701				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);
702
703				for (int8 i = 0; i < 5; i++) {
704					fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
705						BPoint(r.right - i, r.bottom - i),
706						colors[(4 - i) == 4 ? 5 : (4 - i)]);
707				}
708			}
709			break;
710		}
711
712		case B_FLOATING_WINDOW_LOOK:
713		case kLeftTitledWindowLook:
714		{
715			// top
716			if (invalid.Intersects(fTopBorder)) {
717				ComponentColors colors;
718				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);
719
720				for (int8 i = 0; i < 3; i++) {
721					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
722						BPoint(r.right - i, r.top + i), colors[i * 2]);
723				}
724				if (fTitleBarRect.IsValid()
725					&& fTopTab->look != kLeftTitledWindowLook) {
726					// grey along the bottom of the tab
727					// (overwrites "white" from frame)
728					fDrawingEngine->StrokeLine(
729						BPoint(fTitleBarRect.left + 2,
730							fTitleBarRect.bottom + 1),
731						BPoint(fTitleBarRect.right - 2,
732							fTitleBarRect.bottom + 1), colors[2]);
733				}
734			}
735			// left
736			if (invalid.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
737				ComponentColors colors;
738				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
739
740				for (int8 i = 0; i < 3; i++) {
741					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
742						BPoint(r.left + i, r.bottom - i), colors[i * 2]);
743				}
744				if (fTopTab->look == kLeftTitledWindowLook
745					&& fTitleBarRect.IsValid()) {
746					// grey along the right side of the tab
747					// (overwrites "white" from frame)
748					fDrawingEngine->StrokeLine(
749						BPoint(fTitleBarRect.right + 1,
750							fTitleBarRect.top + 2),
751						BPoint(fTitleBarRect.right + 1,
752							fTitleBarRect.bottom - 2), colors[2]);
753				}
754			}
755			// bottom
756			if (invalid.Intersects(fBottomBorder)) {
757				ComponentColors colors;
758				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);
759
760				for (int8 i = 0; i < 3; i++) {
761					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
762						BPoint(r.right - i, r.bottom - i),
763						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
764				}
765			}
766			// right
767			if (invalid.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
768				ComponentColors colors;
769				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);
770
771				for (int8 i = 0; i < 3; i++) {
772					fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
773						BPoint(r.right - i, r.bottom - i),
774						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
775				}
776			}
777			break;
778		}
779
780		case B_BORDERED_WINDOW_LOOK:
781		{
782			// TODO: Draw the borders individually!
783			ComponentColors colors;
784			_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
785
786			fDrawingEngine->StrokeRect(r, colors[5]);
787			break;
788		}
789
790		default:
791			// don't draw a border frame
792			break;
793	}
794
795	// Draw the resize knob if we're supposed to
796	if (!(fTopTab->flags & B_NOT_RESIZABLE)) {
797		r = fResizeRect;
798
799		ComponentColors colors;
800		_GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab);
801
802		switch ((int)fTopTab->look) {
803			case B_DOCUMENT_WINDOW_LOOK:
804			{
805				if (!invalid.Intersects(r))
806					break;
807
808				float x = r.right - 3;
809				float y = r.bottom - 3;
810
811				BRect bg(x - 13, y - 13, x, y);
812
813				BGradientLinear gradient;
814				gradient.SetStart(bg.LeftTop());
815				gradient.SetEnd(bg.RightBottom());
816				gradient.AddColor(colors[1], 0);
817				gradient.AddColor(colors[2], 255);
818
819				fDrawingEngine->FillRect(bg, gradient);
820
821				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
822					BPoint(x - 15, y - 2), colors[0]);
823				fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14),
824					BPoint(x - 14, y - 1), colors[1]);
825				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
826					BPoint(x - 2, y - 15), colors[0]);
827				fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14),
828					BPoint(x - 1, y - 14), colors[1]);
829
830				if (fTopTab && !IsFocus(fTopTab))
831					break;
832
833				static const rgb_color kWhite
834					= (rgb_color){ 255, 255, 255, 255 };
835				for (int8 i = 1; i <= 4; i++) {
836					for (int8 j = 1; j <= i; j++) {
837						BPoint pt1(x - (3 * j) + 1, y - (3 * (5 - i)) + 1);
838						BPoint pt2(x - (3 * j) + 2, y - (3 * (5 - i)) + 2);
839						fDrawingEngine->StrokePoint(pt1, colors[0]);
840						fDrawingEngine->StrokePoint(pt2, kWhite);
841					}
842				}
843				break;
844			}
845
846			case B_TITLED_WINDOW_LOOK:
847			case B_FLOATING_WINDOW_LOOK:
848			case B_MODAL_WINDOW_LOOK:
849			case kLeftTitledWindowLook:
850			{
851				if (!invalid.Intersects(BRect(fRightBorder.right - kBorderResizeLength,
852					fBottomBorder.bottom - kBorderResizeLength, fRightBorder.right - 1,
853					fBottomBorder.bottom - 1)))
854					break;
855
856				fDrawingEngine->StrokeLine(
857					BPoint(fRightBorder.left, fBottomBorder.bottom - kBorderResizeLength),
858					BPoint(fRightBorder.right - 1, fBottomBorder.bottom - kBorderResizeLength),
859					colors[0]);
860				fDrawingEngine->StrokeLine(
861					BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.top),
862					BPoint(fRightBorder.right - kBorderResizeLength, fBottomBorder.bottom - 1),
863					colors[0]);
864				break;
865			}
866
867			default:
868				// don't draw resize corner
869				break;
870		}
871	}
872}
873
874
875void
876DefaultDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid)
877{
878	STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n",
879		invalid.left, invalid.top, invalid.right, invalid.bottom));
880	const BRect& tabRect = tab->tabRect;
881	// If a window has a tab, this will draw it and any buttons which are
882	// in it.
883	if (!tabRect.IsValid() || !invalid.Intersects(tabRect))
884		return;
885
886	ComponentColors colors;
887	_GetComponentColors(COMPONENT_TAB, colors, tab);
888
889	// outer frame
890	fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(),
891		colors[COLOR_TAB_FRAME_LIGHT]);
892	fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(),
893		colors[COLOR_TAB_FRAME_LIGHT]);
894	if (tab->look != kLeftTitledWindowLook) {
895		fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(),
896			colors[COLOR_TAB_FRAME_DARK]);
897	} else {
898		fDrawingEngine->StrokeLine(tabRect.LeftBottom(),
899			tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]);
900	}
901
902	float tabBotton = tabRect.bottom;
903	if (fTopTab != tab)
904		tabBotton -= 1;
905
906	// bevel
907	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
908		BPoint(tabRect.left + 1,
909			tabBotton - (tab->look == kLeftTitledWindowLook ? 1 : 0)),
910		colors[COLOR_TAB_BEVEL]);
911	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
912		BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1),
913			tabRect.top + 1),
914		colors[COLOR_TAB_BEVEL]);
915
916	if (tab->look != kLeftTitledWindowLook) {
917		fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2),
918			BPoint(tabRect.right - 1, tabBotton),
919			colors[COLOR_TAB_SHADOW]);
920	} else {
921		fDrawingEngine->StrokeLine(
922			BPoint(tabRect.left + 2, tabRect.bottom - 1),
923			BPoint(tabRect.right, tabRect.bottom - 1),
924			colors[COLOR_TAB_SHADOW]);
925	}
926
927	// fill
928	BGradientLinear gradient;
929	gradient.SetStart(tabRect.LeftTop());
930	gradient.AddColor(colors[COLOR_TAB_LIGHT], 0);
931	gradient.AddColor(colors[COLOR_TAB], 255);
932
933	if (tab->look != kLeftTitledWindowLook) {
934		gradient.SetEnd(tabRect.LeftBottom());
935		fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2,
936			tabRect.right - 2, tabBotton), gradient);
937	} else {
938		gradient.SetEnd(tabRect.RightTop());
939		fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2,
940			tabRect.right, tabRect.bottom - 2), gradient);
941	}
942
943	_DrawTitle(tab, tabRect);
944
945	DrawButtons(tab, invalid);
946}
947
948
949void
950DefaultDecorator::_DrawClose(Decorator::Tab* _tab, bool direct, BRect rect)
951{
952	STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
953		rect.bottom));
954
955	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
956
957	int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2);
958	ServerBitmap* bitmap = tab->closeBitmaps[index];
959	if (bitmap == NULL) {
960		bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON,
961			tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight());
962		tab->closeBitmaps[index] = bitmap;
963	}
964
965	_DrawButtonBitmap(bitmap, direct, rect);
966}
967
968
969void
970DefaultDecorator::_DrawTitle(Decorator::Tab* _tab, BRect r)
971{
972	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
973
974	const BRect& tabRect = tab->tabRect;
975	const BRect& closeRect = tab->closeRect;
976	const BRect& zoomRect = tab->zoomRect;
977
978	STRACE(("_DrawTitle(%f,%f,%f,%f)\n", r.left, r.top, r.right, r.bottom));
979
980	ComponentColors colors;
981	_GetComponentColors(COMPONENT_TAB, colors, tab);
982
983	fDrawingEngine->SetDrawingMode(B_OP_OVER);
984	fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]);
985	fDrawingEngine->SetFont(fDrawState.Font());
986
987	// figure out position of text
988	font_height fontHeight;
989	fDrawState.Font().GetHeight(fontHeight);
990
991	BPoint titlePos;
992	if (tab->look != kLeftTitledWindowLook) {
993		titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset
994			: tabRect.left + tab->textOffset;
995		titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom
996			+ fontHeight.ascent + fontHeight.descent) / 2.0
997			- fontHeight.descent + 0.5);
998	} else {
999		titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right
1000			+ fontHeight.ascent + fontHeight.descent) / 2.0
1001			- fontHeight.descent + 0.5);
1002		titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset
1003			: tabRect.bottom - tab->textOffset;
1004	}
1005
1006	fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitleLength,
1007		titlePos);
1008
1009	fDrawingEngine->SetDrawingMode(B_OP_COPY);
1010}
1011
1012
1013void
1014DefaultDecorator::_DrawZoom(Decorator::Tab* _tab, bool direct, BRect rect)
1015{
1016	STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
1017		rect.bottom));
1018	if (rect.IntegerWidth() < 1)
1019		return;
1020	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
1021
1022	int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2);
1023	ServerBitmap* bitmap = tab->zoomBitmaps[index];
1024	if (bitmap == NULL) {
1025		bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON,
1026			tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight());
1027		tab->zoomBitmaps[index] = bitmap;
1028	}
1029
1030	_DrawButtonBitmap(bitmap, direct, rect);
1031}
1032
1033
1034void
1035DefaultDecorator::_SetTitle(Decorator::Tab* tab, const char* string,
1036	BRegion* updateRegion)
1037{
1038	// TODO: we could be much smarter about the update region
1039
1040	BRect rect = TabRect(tab);
1041
1042	_DoLayout();
1043
1044	if (updateRegion == NULL)
1045		return;
1046
1047	rect = rect | TabRect(tab);
1048
1049	rect.bottom++;
1050		// the border will look differently when the title is adjacent
1051
1052	updateRegion->Include(rect);
1053}
1054
1055
1056void
1057DefaultDecorator::_FontsChanged(DesktopSettings& settings,
1058	BRegion* updateRegion)
1059{
1060	// get previous extent
1061	if (updateRegion != NULL)
1062		updateRegion->Include(&GetFootprint());
1063
1064	_UpdateFont(settings);
1065	_InvalidateBitmaps();
1066	_DoLayout();
1067
1068	_InvalidateFootprint();
1069	if (updateRegion != NULL)
1070		updateRegion->Include(&GetFootprint());
1071}
1072
1073
1074void
1075DefaultDecorator::_SetLook(Decorator::Tab* tab, DesktopSettings& settings,
1076	window_look look, BRegion* updateRegion)
1077{
1078	// TODO: we could be much smarter about the update region
1079
1080	// get previous extent
1081	if (updateRegion != NULL)
1082		updateRegion->Include(&GetFootprint());
1083
1084	tab->look = look;
1085
1086	_UpdateFont(settings);
1087	_InvalidateBitmaps();
1088	_DoLayout();
1089
1090	_InvalidateFootprint();
1091	if (updateRegion != NULL)
1092		updateRegion->Include(&GetFootprint());
1093}
1094
1095
1096void
1097DefaultDecorator::_SetFlags(Decorator::Tab* tab, uint32 flags,
1098	BRegion* updateRegion)
1099{
1100	// TODO: we could be much smarter about the update region
1101
1102	// get previous extent
1103	if (updateRegion != NULL)
1104		updateRegion->Include(&GetFootprint());
1105
1106	tab->flags = flags;
1107	_DoLayout();
1108
1109	_InvalidateFootprint();
1110	if (updateRegion != NULL)
1111		updateRegion->Include(&GetFootprint());
1112}
1113
1114
1115void
1116DefaultDecorator::_SetFocus(Decorator::Tab* _tab)
1117{
1118	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
1119	tab->buttonFocus = IsFocus(tab)
1120		|| ((tab->look == B_FLOATING_WINDOW_LOOK
1121			|| tab->look == kLeftTitledWindowLook)
1122			&& (tab->flags & B_AVOID_FOCUS) != 0);
1123	if (CountTabs() > 1)
1124		_LayoutTabItems(tab, tab->tabRect);
1125}
1126
1127
1128void
1129DefaultDecorator::_MoveBy(BPoint offset)
1130{
1131	STRACE(("DefaultDecorator: Move By (%.1f, %.1f)\n", offset.x, offset.y));
1132	// Move all internal rectangles the appropriate amount
1133	for (int32 i = 0; i < fTabList.CountItems(); i++) {
1134		Decorator::Tab* tab = fTabList.ItemAt(i);
1135
1136		tab->zoomRect.OffsetBy(offset);
1137		tab->closeRect.OffsetBy(offset);
1138		tab->tabRect.OffsetBy(offset);
1139	}
1140	fFrame.OffsetBy(offset);
1141	fTitleBarRect.OffsetBy(offset);
1142	fTabsRegion.OffsetBy(offset);
1143	fResizeRect.OffsetBy(offset);
1144	fBorderRect.OffsetBy(offset);
1145
1146	fLeftBorder.OffsetBy(offset);
1147	fRightBorder.OffsetBy(offset);
1148	fTopBorder.OffsetBy(offset);
1149	fBottomBorder.OffsetBy(offset);
1150}
1151
1152
1153void
1154DefaultDecorator::_ResizeBy(BPoint offset, BRegion* dirty)
1155{
1156	STRACE(("DefaultDecorator: Resize By (%.1f, %.1f)\n", offset.x, offset.y));
1157	// Move all internal rectangles the appropriate amount
1158	fFrame.right += offset.x;
1159	fFrame.bottom += offset.y;
1160
1161	// Handle invalidation of resize rect
1162	if (dirty && !(fTopTab->flags & B_NOT_RESIZABLE)) {
1163		BRect realResizeRect;
1164		switch ((int)fTopTab->look) {
1165			case B_DOCUMENT_WINDOW_LOOK:
1166				realResizeRect = fResizeRect;
1167				// Resize rect at old location
1168				dirty->Include(realResizeRect);
1169				realResizeRect.OffsetBy(offset);
1170				// Resize rect at new location
1171				dirty->Include(realResizeRect);
1172				break;
1173			case B_TITLED_WINDOW_LOOK:
1174			case B_FLOATING_WINDOW_LOOK:
1175			case B_MODAL_WINDOW_LOOK:
1176			case kLeftTitledWindowLook:
1177				// The bottom border resize line
1178				realResizeRect.Set(fRightBorder.right - kBorderResizeLength, fBottomBorder.top,
1179					fRightBorder.right - kBorderResizeLength, fBottomBorder.bottom - 1);
1180				// Old location
1181				dirty->Include(realResizeRect);
1182				realResizeRect.OffsetBy(offset);
1183				// New location
1184				dirty->Include(realResizeRect);
1185
1186				// The right border resize line
1187				realResizeRect.Set(fRightBorder.left, fBottomBorder.bottom - kBorderResizeLength,
1188					fRightBorder.right - 1, fBottomBorder.bottom - kBorderResizeLength);
1189				// Old location
1190				dirty->Include(realResizeRect);
1191				realResizeRect.OffsetBy(offset);
1192				// New location
1193				dirty->Include(realResizeRect);
1194				break;
1195			default:
1196				break;
1197		}
1198	}
1199
1200	fResizeRect.OffsetBy(offset);
1201
1202	fBorderRect.right += offset.x;
1203	fBorderRect.bottom += offset.y;
1204
1205	fLeftBorder.bottom += offset.y;
1206	fTopBorder.right += offset.x;
1207
1208	fRightBorder.OffsetBy(offset.x, 0.0);
1209	fRightBorder.bottom	+= offset.y;
1210
1211	fBottomBorder.OffsetBy(0.0, offset.y);
1212	fBottomBorder.right	+= offset.x;
1213
1214	if (dirty) {
1215		if (offset.x > 0.0) {
1216			BRect t(fRightBorder.left - offset.x, fTopBorder.top,
1217				fRightBorder.right, fTopBorder.bottom);
1218			dirty->Include(t);
1219			t.Set(fRightBorder.left - offset.x, fBottomBorder.top,
1220				fRightBorder.right, fBottomBorder.bottom);
1221			dirty->Include(t);
1222			dirty->Include(fRightBorder);
1223		} else if (offset.x < 0.0) {
1224			dirty->Include(BRect(fRightBorder.left, fTopBorder.top,
1225				fRightBorder.right, fBottomBorder.bottom));
1226		}
1227		if (offset.y > 0.0) {
1228			BRect t(fLeftBorder.left, fLeftBorder.bottom - offset.y,
1229				fLeftBorder.right, fLeftBorder.bottom);
1230			dirty->Include(t);
1231			t.Set(fRightBorder.left, fRightBorder.bottom - offset.y,
1232				fRightBorder.right, fRightBorder.bottom);
1233			dirty->Include(t);
1234			dirty->Include(fBottomBorder);
1235		} else if (offset.y < 0.0) {
1236			dirty->Include(fBottomBorder);
1237		}
1238	}
1239
1240	// resize tab and layout tab items
1241	if (fTitleBarRect.IsValid()) {
1242		if (fTabList.CountItems() > 1) {
1243			_DoTabLayout();
1244			if (dirty != NULL)
1245				dirty->Include(fTitleBarRect);
1246			return;
1247		}
1248
1249		DefaultDecorator::Tab* tab = _TabAt(0);
1250		BRect& tabRect = tab->tabRect;
1251		BRect oldTabRect(tabRect);
1252
1253		float tabSize;
1254		float tabOffset = _SingleTabOffsetAndSize(tabSize);
1255
1256		float delta = tabOffset - tab->tabOffset;
1257		tab->tabOffset = (uint32)tabOffset;
1258		if (fTopTab->look != kLeftTitledWindowLook)
1259			tabRect.OffsetBy(delta, 0.0);
1260		else
1261			tabRect.OffsetBy(0.0, delta);
1262
1263		if (tabSize < tab->minTabSize)
1264			tabSize = tab->minTabSize;
1265		if (tabSize > tab->maxTabSize)
1266			tabSize = tab->maxTabSize;
1267
1268		if (fTopTab->look != kLeftTitledWindowLook
1269			&& tabSize != tabRect.Width()) {
1270			tabRect.right = tabRect.left + tabSize;
1271		} else if (fTopTab->look == kLeftTitledWindowLook
1272			&& tabSize != tabRect.Height()) {
1273			tabRect.bottom = tabRect.top + tabSize;
1274		}
1275
1276		if (oldTabRect != tabRect) {
1277			_LayoutTabItems(tab, tabRect);
1278
1279			if (dirty) {
1280				// NOTE: the tab rect becoming smaller only would
1281				// handled be the Desktop anyways, so it is sufficient
1282				// to include it into the dirty region in it's
1283				// final state
1284				BRect redraw(tabRect);
1285				if (delta != 0.0) {
1286					redraw = redraw | oldTabRect;
1287					if (fTopTab->look != kLeftTitledWindowLook)
1288						redraw.bottom++;
1289					else
1290						redraw.right++;
1291				}
1292				dirty->Include(redraw);
1293			}
1294		}
1295		fTitleBarRect = tabRect;
1296		fTabsRegion = fTitleBarRect;
1297	}
1298}
1299
1300
1301bool
1302DefaultDecorator::_SetTabLocation(Decorator::Tab* _tab, float location,
1303	bool isShifting, BRegion* updateRegion)
1304{
1305	STRACE(("DefaultDecorator: Set Tab Location(%.1f)\n", location));
1306	if (CountTabs() > 1) {
1307		if (isShifting == false) {
1308			_DoTabLayout();
1309			if (updateRegion != NULL)
1310				updateRegion->Include(fTitleBarRect);
1311			fOldMovingTab = BRect(0, 0, -1, -1);
1312			return true;
1313		} else {
1314			if (fOldMovingTab.IsValid() == false)
1315				fOldMovingTab = _tab->tabRect;
1316		}
1317	}
1318
1319	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
1320	BRect& tabRect = tab->tabRect;
1321	if (tabRect.IsValid() == false)
1322		return false;
1323
1324	if (location < 0)
1325		location = 0;
1326
1327	float maxLocation
1328		= fRightBorder.right - fLeftBorder.left - tabRect.Width();
1329	if (CountTabs() > 1)
1330		maxLocation = fTitleBarRect.right - fLeftBorder.left - tabRect.Width();
1331
1332	if (location > maxLocation)
1333		location = maxLocation;
1334
1335	float delta = floor(location - tab->tabOffset);
1336	if (delta == 0.0)
1337		return false;
1338
1339	// redraw old rect (1 pixel on the border must also be updated)
1340	BRect rect(tabRect);
1341	rect.bottom++;
1342	if (updateRegion != NULL)
1343		updateRegion->Include(rect);
1344
1345	tabRect.OffsetBy(delta, 0);
1346	tab->tabOffset = (int32)location;
1347	_LayoutTabItems(_tab, tabRect);
1348	tab->tabLocation = maxLocation > 0.0 ? tab->tabOffset / maxLocation : 0.0;
1349
1350	if (fTabList.CountItems() == 1)
1351		fTitleBarRect = tabRect;
1352
1353	_CalculateTabsRegion();
1354
1355	// redraw new rect as well
1356	rect = tabRect;
1357	rect.bottom++;
1358	if (updateRegion != NULL)
1359		updateRegion->Include(rect);
1360
1361	return true;
1362}
1363
1364
1365bool
1366DefaultDecorator::_SetSettings(const BMessage& settings, BRegion* updateRegion)
1367{
1368	float tabLocation;
1369	bool modified = false;
1370	for (int32 i = 0; i < fTabList.CountItems(); i++) {
1371		if (settings.FindFloat("tab location", i, &tabLocation) != B_OK)
1372			return false;
1373		modified |= SetTabLocation(i, tabLocation, updateRegion);
1374	}
1375	return modified;
1376}
1377
1378
1379bool
1380DefaultDecorator::_AddTab(DesktopSettings& settings, int32 index,
1381	BRegion* updateRegion)
1382{
1383	_UpdateFont(settings);
1384
1385	_DoLayout();
1386	if (updateRegion != NULL)
1387		updateRegion->Include(fTitleBarRect);
1388	return true;
1389}
1390
1391
1392bool
1393DefaultDecorator::_RemoveTab(int32 index, BRegion* updateRegion)
1394{
1395	BRect oldTitle = fTitleBarRect;
1396	_DoLayout();
1397	if (updateRegion != NULL) {
1398		updateRegion->Include(oldTitle);
1399		updateRegion->Include(fTitleBarRect);
1400	}
1401	return true;
1402}
1403
1404
1405bool
1406DefaultDecorator::_MoveTab(int32 from, int32 to, bool isMoving,
1407	BRegion* updateRegion)
1408{
1409	DefaultDecorator::Tab* toTab = _TabAt(to);
1410	if (toTab == NULL)
1411		return false;
1412
1413	if (from < to) {
1414		fOldMovingTab.OffsetBy(toTab->tabRect.Width(), 0);
1415		toTab->tabRect.OffsetBy(-fOldMovingTab.Width(), 0);
1416	} else {
1417		fOldMovingTab.OffsetBy(-toTab->tabRect.Width(), 0);
1418		toTab->tabRect.OffsetBy(fOldMovingTab.Width(), 0);
1419	}
1420
1421	toTab->tabOffset = uint32(toTab->tabRect.left - fLeftBorder.left);
1422	_LayoutTabItems(toTab, toTab->tabRect);
1423
1424	_CalculateTabsRegion();
1425
1426	if (updateRegion != NULL)
1427		updateRegion->Include(fTitleBarRect);
1428	return true;
1429}
1430
1431
1432void
1433DefaultDecorator::_GetFootprint(BRegion *region)
1434{
1435	STRACE(("DefaultDecorator: Get Footprint\n"));
1436	// This function calculates the decorator's footprint in coordinates
1437	// relative to the view. This is most often used to set a Window
1438	// object's visible region.
1439	if (!region)
1440		return;
1441
1442	region->MakeEmpty();
1443
1444	if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
1445		return;
1446
1447	region->Include(fTopBorder);
1448	region->Include(fLeftBorder);
1449	region->Include(fRightBorder);
1450	region->Include(fBottomBorder);
1451
1452	if (fTopTab->look == B_BORDERED_WINDOW_LOOK)
1453		return;
1454
1455	region->Include(&fTabsRegion);
1456
1457	if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK) {
1458		// include the rectangular resize knob on the bottom right
1459		float knobSize = kResizeKnobSize - fBorderWidth;
1460		region->Include(BRect(fFrame.right - knobSize, fFrame.bottom - knobSize,
1461			fFrame.right, fFrame.bottom));
1462	}
1463}
1464
1465
1466void
1467DefaultDecorator::DrawButtons(Decorator::Tab* tab, const BRect& invalid)
1468{
1469	// Draw the buttons if we're supposed to
1470	if (!(tab->flags & B_NOT_CLOSABLE) && invalid.Intersects(tab->closeRect))
1471		_DrawClose(tab, false, tab->closeRect);
1472	if (!(tab->flags & B_NOT_ZOOMABLE) && invalid.Intersects(tab->zoomRect))
1473		_DrawZoom(tab, false, tab->zoomRect);
1474}
1475
1476
1477/*!	Returns the frame colors for the specified decorator component.
1478
1479	The meaning of the color array elements depends on the specified component.
1480	For some components some array elements are unused.
1481
1482	\param component The component for which to return the frame colors.
1483	\param highlight The highlight set for the component.
1484	\param colors An array of colors to be initialized by the function.
1485*/
1486void
1487DefaultDecorator::GetComponentColors(Component component, uint8 highlight,
1488	ComponentColors _colors, Decorator::Tab* _tab)
1489{
1490	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
1491	switch (component) {
1492		case COMPONENT_TAB:
1493			if (tab && tab->buttonFocus) {
1494				_colors[COLOR_TAB_FRAME_LIGHT]
1495					= tint_color(kFocusFrameColor, B_DARKEN_2_TINT);
1496				_colors[COLOR_TAB_FRAME_DARK]
1497					= tint_color(kFocusFrameColor, B_DARKEN_3_TINT);
1498				_colors[COLOR_TAB] = kFocusTabColor;
1499				_colors[COLOR_TAB_LIGHT] = kFocusTabColorLight;
1500				_colors[COLOR_TAB_BEVEL] = kFocusTabColorBevel;
1501				_colors[COLOR_TAB_SHADOW] = kFocusTabColorShadow;
1502				_colors[COLOR_TAB_TEXT] = kFocusTextColor;
1503			} else {
1504				_colors[COLOR_TAB_FRAME_LIGHT]
1505					= tint_color(kNonFocusFrameColor, B_DARKEN_2_TINT);
1506				_colors[COLOR_TAB_FRAME_DARK]
1507					= tint_color(kNonFocusFrameColor, B_DARKEN_3_TINT);
1508				_colors[COLOR_TAB] = kNonFocusTabColor;
1509				_colors[COLOR_TAB_LIGHT] = kNonFocusTabColorLight;
1510				_colors[COLOR_TAB_BEVEL] = kNonFocusTabColorBevel;
1511				_colors[COLOR_TAB_SHADOW] = kNonFocusTabColorShadow;
1512				_colors[COLOR_TAB_TEXT] = kNonFocusTextColor;
1513			}
1514			break;
1515
1516		case COMPONENT_CLOSE_BUTTON:
1517		case COMPONENT_ZOOM_BUTTON:
1518			if (tab && tab->buttonFocus) {
1519				_colors[COLOR_BUTTON] = kFocusTabColor;
1520				_colors[COLOR_BUTTON_LIGHT] = kFocusTabColorLight;
1521			} else {
1522				_colors[COLOR_BUTTON] = kNonFocusTabColor;
1523				_colors[COLOR_BUTTON_LIGHT] = kNonFocusTabColorLight;
1524			}
1525			break;
1526
1527		case COMPONENT_LEFT_BORDER:
1528		case COMPONENT_RIGHT_BORDER:
1529		case COMPONENT_TOP_BORDER:
1530		case COMPONENT_BOTTOM_BORDER:
1531		case COMPONENT_RESIZE_CORNER:
1532		default:
1533			if (tab && tab->buttonFocus) {
1534				_colors[0] = tint_color(kFocusFrameColor, B_DARKEN_2_TINT);
1535				_colors[1] = tint_color(kFocusFrameColor, B_LIGHTEN_2_TINT);
1536				_colors[2] = kFocusFrameColor;
1537				_colors[3] = tint_color(kFocusFrameColor,
1538					(B_DARKEN_1_TINT + B_NO_TINT) / 2);
1539				_colors[4] = tint_color(kFocusFrameColor, B_DARKEN_2_TINT);
1540				_colors[5] = tint_color(kFocusFrameColor, B_DARKEN_3_TINT);
1541			} else {
1542				_colors[0] = tint_color(kNonFocusFrameColor, B_DARKEN_2_TINT);
1543				_colors[1] = tint_color(kNonFocusFrameColor, B_LIGHTEN_2_TINT);
1544				_colors[2] = kNonFocusFrameColor;
1545				_colors[3] = tint_color(kNonFocusFrameColor,
1546					(B_DARKEN_1_TINT + B_NO_TINT) / 2);
1547				_colors[4] = tint_color(kNonFocusFrameColor, B_DARKEN_2_TINT);
1548				_colors[5] = tint_color(kNonFocusFrameColor, B_DARKEN_3_TINT);
1549			}
1550
1551			// for the resize-border highlight dye everything bluish.
1552			if (highlight == HIGHLIGHT_RESIZE_BORDER) {
1553				for (int32 i = 0; i < 6; i++) {
1554					_colors[i].red = std::max((int)_colors[i].red - 80, 0);
1555					_colors[i].green = std::max((int)_colors[i].green - 80, 0);
1556					_colors[i].blue = 255;
1557				}
1558			}
1559			break;
1560	}
1561}
1562
1563
1564void
1565DefaultDecorator::_UpdateFont(DesktopSettings& settings)
1566{
1567	ServerFont font;
1568	if (fTopTab->look == B_FLOATING_WINDOW_LOOK
1569		|| fTopTab->look == kLeftTitledWindowLook) {
1570		settings.GetDefaultPlainFont(font);
1571		if (fTopTab->look == kLeftTitledWindowLook)
1572			font.SetRotation(90.0f);
1573	} else
1574		settings.GetDefaultBoldFont(font);
1575
1576	font.SetFlags(B_FORCE_ANTIALIASING);
1577	font.SetSpacing(B_STRING_SPACING);
1578	fDrawState.SetFont(font);
1579}
1580
1581
1582void
1583DefaultDecorator::_DrawButtonBitmap(ServerBitmap* bitmap, bool direct,
1584	BRect rect)
1585{
1586	if (bitmap == NULL)
1587		return;
1588
1589	bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
1590	fDrawingEngine->SetCopyToFrontEnabled(direct);
1591	drawing_mode oldMode;
1592	fDrawingEngine->SetDrawingMode(B_OP_OVER, oldMode);
1593	fDrawingEngine->DrawBitmap(bitmap, rect.OffsetToCopy(0, 0), rect);
1594	fDrawingEngine->SetDrawingMode(oldMode);
1595	fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
1596}
1597
1598
1599/*!	\brief Draws a framed rectangle with a gradient.
1600	\param down The rectangle should be drawn recessed or not.
1601	\param colors A button color array with the colors to be used.
1602*/
1603void
1604DefaultDecorator::_DrawBlendedRect(DrawingEngine* engine, BRect rect,
1605	bool down, const ComponentColors& colors)
1606{
1607	// figure out which colors to use
1608	rgb_color startColor, endColor;
1609	if (down) {
1610		startColor = tint_color(colors[COLOR_BUTTON], B_DARKEN_1_TINT);
1611		endColor = colors[COLOR_BUTTON_LIGHT];
1612	} else {
1613		startColor = tint_color(colors[COLOR_BUTTON], B_LIGHTEN_MAX_TINT);
1614		endColor = colors[COLOR_BUTTON];
1615	}
1616
1617	// fill
1618	rect.InsetBy(1, 1);
1619	BGradientLinear gradient;
1620	gradient.SetStart(rect.LeftTop());
1621	gradient.SetEnd(rect.RightBottom());
1622	gradient.AddColor(startColor, 0);
1623	gradient.AddColor(endColor, 255);
1624
1625	engine->FillRect(rect, gradient);
1626
1627	// outline
1628	rect.InsetBy(-1, -1);
1629	engine->StrokeRect(rect, tint_color(colors[COLOR_BUTTON], B_DARKEN_2_TINT));
1630}
1631
1632
1633void
1634DefaultDecorator::_GetButtonSizeAndOffset(const BRect& tabRect, float* _offset,
1635	float* _size, float* _inset) const
1636{
1637	float tabSize = fTopTab->look == kLeftTitledWindowLook ?
1638		tabRect.Width() : tabRect.Height();
1639
1640	bool smallTab = fTopTab->look == B_FLOATING_WINDOW_LOOK
1641		|| fTopTab->look == kLeftTitledWindowLook;
1642
1643	*_offset = smallTab ? floorf(fDrawState.Font().Size() / 2.6)
1644		: floorf(fDrawState.Font().Size() / 2.3);
1645	*_inset = smallTab ? floorf(fDrawState.Font().Size() / 5.0)
1646		: floorf(fDrawState.Font().Size() / 6.0);
1647
1648	// "+ 2" so that the rects are centered within the solid area
1649	// (without the 2 pixels for the top border)
1650	*_size = tabSize - 2 * *_offset + *_inset;
1651}
1652
1653
1654void
1655DefaultDecorator::_LayoutTabItems(Decorator::Tab* _tab, const BRect& tabRect)
1656{
1657	DefaultDecorator::Tab* tab = static_cast<DefaultDecorator::Tab*>(_tab);
1658
1659	float offset;
1660	float size;
1661	float inset;
1662	_GetButtonSizeAndOffset(tabRect, &offset, &size, &inset);
1663
1664	// default textOffset
1665	tab->textOffset = _DefaultTextOffset();
1666
1667	BRect& closeRect = tab->closeRect;
1668	BRect& zoomRect = tab->zoomRect;
1669
1670	// calulate close rect based on the tab rectangle
1671	if (tab->look != kLeftTitledWindowLook) {
1672		closeRect.Set(tabRect.left + offset, tabRect.top + offset,
1673			tabRect.left + offset + size, tabRect.top + offset + size);
1674
1675		zoomRect.Set(tabRect.right - offset - size, tabRect.top + offset,
1676			tabRect.right - offset, tabRect.top + offset + size);
1677
1678		// hidden buttons have no width
1679		if ((tab->flags & B_NOT_CLOSABLE) != 0)
1680			closeRect.right = closeRect.left - offset;
1681		if ((tab->flags & B_NOT_ZOOMABLE) != 0)
1682			zoomRect.left = zoomRect.right + offset;
1683	} else {
1684		closeRect.Set(tabRect.left + offset, tabRect.top + offset,
1685			tabRect.left + offset + size, tabRect.top + offset + size);
1686
1687		zoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size,
1688			tabRect.left + size + offset, tabRect.bottom - offset);
1689
1690		// hidden buttons have no height
1691		if ((tab->flags & B_NOT_CLOSABLE) != 0)
1692			closeRect.bottom = closeRect.top - offset;
1693		if ((tab->flags & B_NOT_ZOOMABLE) != 0)
1694			zoomRect.top = zoomRect.bottom + offset;
1695	}
1696
1697	// calculate room for title
1698	// TODO: the +2 is there because the title often appeared
1699	//	truncated for no apparent reason - OTOH the title does
1700	//	also not appear perfectly in the middle
1701	if (tab->look != kLeftTitledWindowLook)
1702		size = (zoomRect.left - closeRect.right) - tab->textOffset * 2 + inset;
1703	else
1704		size = (zoomRect.top - closeRect.bottom) - tab->textOffset * 2 + inset;
1705
1706	bool stackMode = fTabList.CountItems() > 1;
1707	if (stackMode && IsFocus(tab) == false) {
1708		zoomRect.Set(0, 0, 0, 0);
1709		size = (tab->tabRect.right - closeRect.right) - tab->textOffset * 2
1710			+ inset;
1711	}
1712	uint8 truncateMode = B_TRUNCATE_MIDDLE;
1713	if (stackMode) {
1714		if (tab->tabRect.Width() < 100)
1715			truncateMode = B_TRUNCATE_END;
1716		float titleWidth = fDrawState.Font().StringWidth(Title(tab),
1717			BString(Title(tab)).Length());
1718		if (size < titleWidth) {
1719			float oldTextOffset = tab->textOffset;
1720			tab->textOffset -= (titleWidth - size) / 2;
1721			const float kMinTextOffset = 5.;
1722			if (tab->textOffset < kMinTextOffset)
1723				tab->textOffset = kMinTextOffset;
1724			size += oldTextOffset * 2;
1725			size -= tab->textOffset * 2;
1726		}
1727	}
1728	tab->truncatedTitle = Title(tab);
1729	fDrawState.Font().TruncateString(&tab->truncatedTitle, truncateMode, size);
1730	tab->truncatedTitleLength = tab->truncatedTitle.Length();
1731}
1732
1733
1734void
1735DefaultDecorator::_InvalidateBitmaps()
1736{
1737	for (int32 i = 0; i < fTabList.CountItems(); i++) {
1738		DefaultDecorator::Tab* tab = _TabAt(i);
1739		for (int32 index = 0; index < 4; index++) {
1740			tab->closeBitmaps[index] = NULL;
1741			tab->zoomBitmaps[index] = NULL;
1742		}
1743	}
1744}
1745
1746
1747ServerBitmap*
1748DefaultDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item,
1749	bool down, int32 width, int32 height)
1750{
1751	// TODO: the list of shared bitmaps is never freed
1752	struct decorator_bitmap {
1753		Component			item;
1754		bool				down;
1755		int32				width;
1756		int32				height;
1757		rgb_color			baseColor;
1758		rgb_color			lightColor;
1759		UtilityBitmap*		bitmap;
1760		decorator_bitmap*	next;
1761	};
1762
1763	static BLocker sBitmapListLock("decorator lock", true);
1764	static decorator_bitmap* sBitmapList = NULL;
1765
1766	ComponentColors colors;
1767	_GetComponentColors(item, colors, tab);
1768
1769	BAutolock locker(sBitmapListLock);
1770
1771	// search our list for a matching bitmap
1772	// TODO: use a hash map instead?
1773	decorator_bitmap* current = sBitmapList;
1774	while (current) {
1775		if (current->item == item && current->down == down
1776			&& current->width == width && current->height == height
1777			&& current->baseColor == colors[COLOR_BUTTON]
1778			&& current->lightColor == colors[COLOR_BUTTON_LIGHT]) {
1779			return current->bitmap;
1780		}
1781
1782		current = current->next;
1783	}
1784
1785	static BitmapDrawingEngine* sBitmapDrawingEngine = NULL;
1786
1787	// didn't find any bitmap, create a new one
1788	if (sBitmapDrawingEngine == NULL)
1789		sBitmapDrawingEngine = new(std::nothrow) BitmapDrawingEngine();
1790	if (sBitmapDrawingEngine == NULL
1791		|| sBitmapDrawingEngine->SetSize(width, height) != B_OK)
1792		return NULL;
1793
1794	BRect rect(0, 0, width - 1, height - 1);
1795
1796	STRACE(("DefaultDecorator creating bitmap for %s %s at size %ldx%ld\n",
1797		item == COMPONENT_CLOSE_BUTTON ? "close" : "zoom",
1798		down ? "down" : "up", width, height));
1799	switch (item) {
1800		case COMPONENT_CLOSE_BUTTON:
1801			_DrawBlendedRect(sBitmapDrawingEngine, rect, down, colors);
1802			break;
1803
1804		case COMPONENT_ZOOM_BUTTON:
1805		{
1806			// init the background
1807			sBitmapDrawingEngine->FillRect(rect, B_TRANSPARENT_COLOR);
1808
1809			float inset = floorf(width / 4.0);
1810			BRect zoomRect(rect);
1811			zoomRect.left += inset;
1812			zoomRect.top += inset;
1813			_DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors);
1814
1815			inset = floorf(width / 2.1);
1816			zoomRect = rect;
1817			zoomRect.right -= inset;
1818			zoomRect.bottom -= inset;
1819			_DrawBlendedRect(sBitmapDrawingEngine, zoomRect, down, colors);
1820			break;
1821		}
1822
1823		default:
1824			break;
1825	}
1826
1827	UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height,
1828		B_RGB32);
1829	if (bitmap == NULL)
1830		return NULL;
1831
1832	// bitmap ready, put it into the list
1833	decorator_bitmap* entry = new(std::nothrow) decorator_bitmap;
1834	if (entry == NULL) {
1835		delete bitmap;
1836		return NULL;
1837	}
1838
1839	entry->item = item;
1840	entry->down = down;
1841	entry->width = width;
1842	entry->height = height;
1843	entry->bitmap = bitmap;
1844	entry->baseColor = colors[COLOR_BUTTON];
1845	entry->lightColor = colors[COLOR_BUTTON_LIGHT];
1846	entry->next = sBitmapList;
1847	sBitmapList = entry;
1848	return bitmap;
1849}
1850
1851
1852void
1853DefaultDecorator::_GetComponentColors(Component component,
1854	ComponentColors _colors, Decorator::Tab* tab)
1855{
1856	// get the highlight for our component
1857	Region region = REGION_NONE;
1858	switch (component) {
1859		case COMPONENT_TAB:
1860			region = REGION_TAB;
1861			break;
1862		case COMPONENT_CLOSE_BUTTON:
1863			region = REGION_CLOSE_BUTTON;
1864			break;
1865		case COMPONENT_ZOOM_BUTTON:
1866			region = REGION_ZOOM_BUTTON;
1867			break;
1868		case COMPONENT_LEFT_BORDER:
1869			region = REGION_LEFT_BORDER;
1870			break;
1871		case COMPONENT_RIGHT_BORDER:
1872			region = REGION_RIGHT_BORDER;
1873			break;
1874		case COMPONENT_TOP_BORDER:
1875			region = REGION_TOP_BORDER;
1876			break;
1877		case COMPONENT_BOTTOM_BORDER:
1878			region = REGION_BOTTOM_BORDER;
1879			break;
1880		case COMPONENT_RESIZE_CORNER:
1881			region = REGION_RIGHT_BOTTOM_CORNER;
1882			break;
1883	}
1884
1885	return GetComponentColors(component, RegionHighlight(region), _colors, tab);
1886}
1887
1888
1889float
1890DefaultDecorator::_DefaultTextOffset() const
1891{
1892	return (fTopTab->look == B_FLOATING_WINDOW_LOOK
1893		|| fTopTab->look == kLeftTitledWindowLook) ? 10 : 18;
1894}
1895
1896
1897float
1898DefaultDecorator::_SingleTabOffsetAndSize(float& tabSize)
1899{
1900	float maxLocation;
1901	if (fTopTab->look != kLeftTitledWindowLook) {
1902		tabSize = fRightBorder.right - fLeftBorder.left;
1903	} else {
1904		tabSize = fBottomBorder.bottom - fTopBorder.top;
1905	}
1906	DefaultDecorator::Tab* tab = _TabAt(0);
1907	maxLocation = tabSize - tab->maxTabSize;
1908	if (maxLocation < 0)
1909		maxLocation = 0;
1910
1911	return floorf(tab->tabLocation * maxLocation);
1912}
1913
1914
1915void
1916DefaultDecorator::_CalculateTabsRegion()
1917{
1918	fTabsRegion.MakeEmpty();
1919	for (int32 i = 0; i < fTabList.CountItems(); i++)
1920		fTabsRegion.Include(fTabList.ItemAt(i)->tabRect);
1921}
1922