1/*
2 * Copyright 2009-2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm, bpmagic@columbus.rr.com
7 *		Adrien Destugues, pulkomandy@gmail.com
8 *		John Scipione, jscipione@gmail.com
9 */
10
11
12/*! Decorator resembling Mac OS 8 and 9 */
13
14
15#include "MacDecorator.h"
16
17#include <new>
18#include <stdio.h>
19
20#include <GradientLinear.h>
21#include <Point.h>
22#include <View.h>
23
24#include "DesktopSettings.h"
25#include "DrawingEngine.h"
26#include "PatternHandler.h"
27#include "RGBColor.h"
28
29
30//#define DEBUG_DECORATOR
31#ifdef DEBUG_DECORATOR
32#	define STRACE(x) printf x
33#else
34#	define STRACE(x) ;
35#endif
36
37
38MacDecorAddOn::MacDecorAddOn(image_id id, const char* name)
39	:
40	DecorAddOn(id, name)
41{
42}
43
44
45Decorator*
46MacDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect,
47	Desktop* desktop)
48{
49	return new (std::nothrow)MacDecorator(settings, rect, desktop);
50}
51
52
53MacDecorator::MacDecorator(DesktopSettings& settings, BRect frame,
54	Desktop* desktop)
55	:
56	SATDecorator(settings, frame, desktop),
57	fButtonHighColor((rgb_color) { 232, 232, 232, 255 }),
58	fButtonLowColor((rgb_color) { 128, 128, 128, 255 }),
59	fFrameHighColor((rgb_color) { 255, 255, 255, 255 }),
60	fFrameMidColor((rgb_color) { 216, 216, 216, 255 }),
61	fFrameLowColor((rgb_color) { 156, 156, 156, 255 }),
62	fFrameLowerColor((rgb_color) { 0, 0, 0, 255 }),
63	fFocusTextColor(settings.UIColor(B_WINDOW_TEXT_COLOR)),
64	fNonFocusTextColor(settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR))
65{
66	STRACE(("MacDecorator()\n"));
67	STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n",
68		frame.left, frame.top, frame.right, frame.bottom));
69}
70
71
72MacDecorator::~MacDecorator()
73{
74	STRACE(("~MacDecorator()\n"));
75}
76
77
78// TODO : Add GetSettings
79
80
81void
82MacDecorator::Draw(BRect updateRect)
83{
84	STRACE(("MacDecorator: Draw(BRect updateRect): "));
85	updateRect.PrintToStream();
86
87	// We need to draw a few things: the tab, the borders,
88	// and the buttons
89	fDrawingEngine->SetDrawState(&fDrawState);
90
91	_DrawFrame(updateRect & fBorderRect);
92	_DrawTabs(updateRect & fTitleBarRect);
93}
94
95
96void
97MacDecorator::Draw()
98{
99	STRACE("MacDecorator::Draw()\n");
100	fDrawingEngine->SetDrawState(&fDrawState);
101
102	_DrawFrame(fBorderRect);
103	_DrawTabs(fTitleBarRect);
104}
105
106
107// TODO : add GetSizeLimits
108
109
110Decorator::Region
111MacDecorator::RegionAt(BPoint where, int32& tab) const
112{
113	// Let the base class version identify hits of the buttons and the tab.
114	Region region = Decorator::RegionAt(where, tab);
115	if (region != REGION_NONE)
116		return region;
117
118	// check the resize corner
119	if (fTopTab->look == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where))
120		return REGION_RIGHT_BOTTOM_CORNER;
121
122	// hit-test the borders
123	if (!(fTopTab->flags & B_NOT_RESIZABLE)
124		&& (fTopTab->look == B_TITLED_WINDOW_LOOK
125			|| fTopTab->look == B_FLOATING_WINDOW_LOOK
126			|| fTopTab->look == B_MODAL_WINDOW_LOOK)
127		&& fBorderRect.Contains(where) && !fFrame.Contains(where)) {
128		return REGION_BOTTOM_BORDER;
129			// TODO: Determine the actual border!
130	}
131
132	return REGION_NONE;
133}
134
135
136bool
137MacDecorator::SetRegionHighlight(Region region, uint8 highlight,
138	BRegion* dirty, int32 tabIndex)
139{
140	Decorator::Tab* tab
141		= static_cast<Decorator::Tab*>(_TabAt(tabIndex));
142	if (tab != NULL) {
143		tab->isHighlighted = highlight != 0;
144		// Invalidate the bitmap caches for the close/minimize/zoom button
145		// when the highlight changes.
146		switch (region) {
147			case REGION_CLOSE_BUTTON:
148				if (highlight != RegionHighlight(region))
149					memset(&tab->closeBitmaps, 0, sizeof(tab->closeBitmaps));
150				break;
151
152			case REGION_MINIMIZE_BUTTON:
153				if (highlight != RegionHighlight(region)) {
154					memset(&tab->minimizeBitmaps, 0,
155						sizeof(tab->minimizeBitmaps));
156				}
157				break;
158
159			case REGION_ZOOM_BUTTON:
160				if (highlight != RegionHighlight(region))
161					memset(&tab->zoomBitmaps, 0, sizeof(tab->zoomBitmaps));
162				break;
163
164			default:
165				break;
166		}
167	}
168
169	return Decorator::SetRegionHighlight(region, highlight, dirty, tabIndex);
170}
171
172
173void
174MacDecorator::_DoLayout()
175{
176	STRACE(("MacDecorator: Do Layout\n"));
177
178	// Here we determine the size of every rectangle that we use
179	// internally when we are given the size of the client rectangle.
180
181	const int32 kDefaultBorderWidth = 6;
182
183	bool hasTab = false;
184
185	if (fTopTab) {
186		switch (fTopTab->look) {
187			case B_MODAL_WINDOW_LOOK:
188				fBorderWidth = kDefaultBorderWidth;
189				break;
190
191			case B_TITLED_WINDOW_LOOK:
192			case B_DOCUMENT_WINDOW_LOOK:
193				hasTab = true;
194				fBorderWidth = kDefaultBorderWidth;
195				break;
196
197			case B_FLOATING_WINDOW_LOOK:
198				hasTab = true;
199				fBorderWidth = 3;
200				break;
201
202			case B_BORDERED_WINDOW_LOOK:
203				fBorderWidth = 1;
204				break;
205
206			default:
207				fBorderWidth = 0;
208		}
209	} else
210		fBorderWidth = 0;
211
212	fBorderRect = fFrame.InsetByCopy(-fBorderWidth, -fBorderWidth);
213
214	// calculate our tab rect
215	if (hasTab) {
216		fBorderRect.top += 3;
217
218		font_height fontHeight;
219		fDrawState.Font().GetHeight(fontHeight);
220
221		// TODO the tab is drawn in a fixed height for now
222		fTitleBarRect.Set(fFrame.left - fBorderWidth,
223			fFrame.top - 22,
224			((fFrame.right - fFrame.left) < 32.0 ?
225				fFrame.left + 32.0 : fFrame.right) + fBorderWidth,
226			fFrame.top - 3);
227
228		for (int32 i = 0; i < fTabList.CountItems(); i++) {
229			Decorator::Tab* tab = fTabList.ItemAt(i);
230
231			tab->tabRect = fTitleBarRect;
232				// TODO actually handle multiple tabs
233
234			tab->zoomRect = fTitleBarRect;
235			tab->zoomRect.left = tab->zoomRect.right - 12;
236			tab->zoomRect.bottom = tab->zoomRect.top + 12;
237			tab->zoomRect.OffsetBy(-4, 4);
238
239			tab->closeRect = tab->zoomRect;
240			tab->minimizeRect = tab->zoomRect;
241
242			tab->closeRect.OffsetTo(fTitleBarRect.left + 4,
243				fTitleBarRect.top + 4);
244
245			tab->zoomRect.OffsetBy(0 - (tab->zoomRect.Width() + 4), 0);
246			if (Title(tab) != NULL && fDrawingEngine != NULL) {
247				tab->truncatedTitle = Title(tab);
248				fDrawingEngine->SetFont(fDrawState.Font());
249				tab->truncatedTitleLength
250					= (int32)fDrawingEngine->StringWidth(Title(tab),
251						strlen(Title(tab)));
252
253				if (tab->truncatedTitleLength < (tab->zoomRect.left
254						- tab->closeRect.right - 10)) {
255					// start with offset from closerect.right
256					tab->textOffset = int(((tab->zoomRect.left - 5)
257						- (tab->closeRect.right + 5)) / 2);
258					tab->textOffset -= int(tab->truncatedTitleLength / 2);
259
260					// now make it the offset from fTabRect.left
261					tab->textOffset += int(tab->closeRect.right + 5
262						- fTitleBarRect.left);
263				} else
264					tab->textOffset = int(tab->closeRect.right) + 5;
265			} else
266				tab->textOffset = 0;
267		}
268	} else {
269		for (int32 i = 0; i < fTabList.CountItems(); i++) {
270			Decorator::Tab* tab = fTabList.ItemAt(i);
271
272			tab->tabRect.Set(0.0, 0.0, -1.0, -1.0);
273			tab->closeRect.Set(0.0, 0.0, -1.0, -1.0);
274			tab->zoomRect.Set(0.0, 0.0, -1.0, -1.0);
275			tab->minimizeRect.Set(0.0, 0.0, -1.0, -1.0);
276		}
277	}
278}
279
280
281void
282MacDecorator::_DrawFrame(BRect invalid)
283{
284	if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
285		return;
286
287	if (fBorderWidth <= 0)
288		return;
289
290	BRect r = fBorderRect;
291	switch (fTopTab->look) {
292		case B_TITLED_WINDOW_LOOK:
293		case B_DOCUMENT_WINDOW_LOOK:
294		case B_MODAL_WINDOW_LOOK:
295		{
296			if (IsFocus(fTopTab)) {
297				BPoint offset = r.LeftTop();
298				BPoint pt2 = r.LeftBottom();
299
300				// Draw the left side of the frame
301				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
302				offset.x++;
303				pt2.x++;
304				pt2.y--;
305
306				fDrawingEngine->StrokeLine(offset, pt2, fFrameHighColor);
307				offset.x++;
308				pt2.x++;
309				pt2.y--;
310
311				fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
312				offset.x++;
313				pt2.x++;
314				fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
315				offset.x++;
316				pt2.x++;
317				pt2.y--;
318
319				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowColor);
320				offset.x++;
321				offset.y += 2;
322				BPoint topleftpt = offset;
323				pt2.x++;
324				pt2.y--;
325
326				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
327
328				offset = r.RightTop();
329				pt2 = r.RightBottom();
330
331				// Draw the right side of the frame
332				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
333				offset.x--;
334				pt2.x--;
335
336				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowColor);
337				offset.x--;
338				pt2.x--;
339
340				fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
341				offset.x--;
342				pt2.x--;
343				fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
344				offset.x--;
345				pt2.x--;
346
347				fDrawingEngine->StrokeLine(offset, pt2, fFrameHighColor);
348				offset.x--;
349				offset.y += 2;
350				BPoint toprightpt = offset;
351				pt2.x--;
352
353				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
354
355				// Draw the top side of the frame that is not in the tab
356				if (fTopTab->look == B_MODAL_WINDOW_LOOK) {
357					offset = r.LeftTop();
358					pt2 = r.RightTop();
359
360					fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
361					offset.x++;
362					offset.y++;
363					pt2.x--;
364					pt2.y++;
365
366					fDrawingEngine->StrokeLine(offset, pt2, fFrameHighColor);
367					offset.x++;
368					offset.y++;
369					pt2.x--;
370					pt2.y++;
371
372					fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
373					offset.x++;
374					offset.y++;
375					pt2.x--;
376					pt2.y++;
377
378					fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
379					offset.x++;
380					offset.y++;
381					pt2.x--;
382					pt2.y++;
383
384					fDrawingEngine->StrokeLine(offset, pt2, fFrameLowColor);
385					offset.x++;
386					offset.y++;
387					pt2.x--;
388					pt2.y++;
389
390					fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
391				} else {
392					// Some odd stuff here where the title bar is melded into the
393					// window border so that the sides are drawn into the title
394					// so we draw this bottom up
395					offset = topleftpt;
396					pt2 = toprightpt;
397
398					fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
399					offset.y--;
400					offset.x++;
401					pt2.y--;
402
403					fDrawingEngine->StrokeLine(offset, pt2, fFrameLowColor);
404				}
405
406				// Draw the bottom side of the frame
407				offset = r.LeftBottom();
408				pt2 = r.RightBottom();
409
410				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
411				offset.x++;
412				offset.y--;
413				pt2.x--;
414				pt2.y--;
415
416				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowColor);
417				offset.x++;
418				offset.y--;
419				pt2.x--;
420				pt2.y--;
421
422				fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
423				offset.x++;
424				offset.y--;
425				pt2.x--;
426				pt2.y--;
427
428				fDrawingEngine->StrokeLine(offset, pt2, fFrameMidColor);
429				offset.x++;
430				offset.y--;
431				pt2.x--;
432				pt2.y--;
433
434				fDrawingEngine->StrokeLine(offset, pt2, fFrameHighColor);
435				offset.x += 2;
436				offset.y--;
437				pt2.x--;
438				pt2.y--;
439
440				fDrawingEngine->StrokeLine(offset, pt2, fFrameLowerColor);
441				offset.y--;
442				pt2.x--;
443				pt2.y--;
444			} else {
445				r.top -= 3;
446				RGBColor inactive(82, 82, 82);
447
448				fDrawingEngine->StrokeLine(r.LeftTop(), r.LeftBottom(),
449					inactive);
450				fDrawingEngine->StrokeLine(r.RightTop(), r.RightBottom(),
451					inactive);
452				fDrawingEngine->StrokeLine(r.LeftBottom(), r.RightBottom(),
453					inactive);
454
455				for (int i = 0; i < 4; i++) {
456					r.InsetBy(1, 1);
457					fDrawingEngine->StrokeLine(r.LeftTop(), r.LeftBottom(),
458						fFrameMidColor);
459					fDrawingEngine->StrokeLine(r.RightTop(), r.RightBottom(),
460						fFrameMidColor);
461					fDrawingEngine->StrokeLine(r.LeftBottom(), r.RightBottom(),
462						fFrameMidColor);
463					fDrawingEngine->StrokeLine(r.LeftTop(), r.RightTop(),
464						fFrameMidColor);
465				}
466
467				r.InsetBy(1, 1);
468				fDrawingEngine->StrokeLine(r.LeftTop(), r.LeftBottom(),
469					inactive);
470				fDrawingEngine->StrokeLine(r.RightTop(), r.RightBottom(),
471					inactive);
472				fDrawingEngine->StrokeLine(r.LeftBottom(), r.RightBottom(),
473					inactive);
474				fDrawingEngine->StrokeLine(r.LeftTop(), r.RightTop(),
475					inactive);
476			}
477			break;
478		}
479		case B_BORDERED_WINDOW_LOOK:
480			fDrawingEngine->StrokeRect(r, fFrameMidColor);
481			break;
482
483		default:
484			// don't draw a border frame
485		break;
486	}
487}
488
489
490void
491MacDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid)
492{
493	// If a window has a tab, this will draw it and any buttons which are
494	// in it.
495	if (!tab->tabRect.IsValid() || !invalid.Intersects(tab->tabRect))
496		return;
497
498	BRect rect(tab->tabRect);
499	fDrawingEngine->SetHighColor(RGBColor(fFrameMidColor));
500	fDrawingEngine->FillRect(rect, fFrameMidColor);
501
502	if (IsFocus(tab)) {
503		fDrawingEngine->StrokeLine(rect.LeftTop(), rect.RightTop(),
504			fFrameLowerColor);
505		fDrawingEngine->StrokeLine(rect.LeftTop(), rect.LeftBottom(),
506			fFrameLowerColor);
507		fDrawingEngine->StrokeLine(rect.RightBottom(), rect.RightTop(),
508			fFrameLowerColor);
509
510		rect.InsetBy(1, 1);
511		rect.bottom++;
512
513		fDrawingEngine->StrokeLine(rect.LeftTop(), rect.RightTop(),
514			fFrameHighColor);
515		fDrawingEngine->StrokeLine(rect.LeftTop(), rect.LeftBottom(),
516			fFrameHighColor);
517		fDrawingEngine->StrokeLine(rect.RightBottom(), rect.RightTop(),
518			fFrameLowColor);
519
520		// Draw the neat little lines on either side of the title if there's
521		// room
522		float left;
523		if ((tab->flags & B_NOT_CLOSABLE) == 0)
524			left = tab->closeRect.right;
525		else
526			left = tab->tabRect.left;
527
528		float right;
529		if ((tab->flags & B_NOT_ZOOMABLE) == 0)
530			right = tab->zoomRect.left;
531		else if ((tab->flags & B_NOT_MINIMIZABLE) == 0)
532			right = tab->minimizeRect.left;
533		else
534			right = tab->tabRect.right;
535
536		if (tab->tabRect.left + tab->textOffset > left + 5) {
537			RGBColor dark(115, 115, 115);
538
539			// Left side
540
541			BPoint offset(left + 5, tab->closeRect.top);
542			BPoint pt2(tab->tabRect.left + tab->textOffset - 5,
543				tab->closeRect.top);
544
545			fDrawState.SetHighColor(RGBColor(fFrameHighColor));
546			for (int32 i = 0; i < 6; i++) {
547				fDrawingEngine->StrokeLine(offset, pt2,
548					fDrawState.HighColor());
549				offset.y += 2;
550				pt2.y += 2;
551			}
552
553			offset.Set(left + 6, tab->closeRect.top + 1);
554			pt2.Set(tab->tabRect.left + tab->textOffset - 4,
555				tab->closeRect.top + 1);
556
557			fDrawState.SetHighColor(dark);
558			for (int32 i = 0; i < 6; i++) {
559				fDrawingEngine->StrokeLine(offset, pt2,
560					fDrawState.HighColor());
561				offset.y += 2;
562				pt2.y += 2;
563			}
564
565			// Right side
566
567			offset.Set(tab->tabRect.left + tab->textOffset
568				+ tab->truncatedTitleLength + 3, tab->zoomRect.top);
569			pt2.Set(right - 8, tab->zoomRect.top);
570
571			if (offset.x < pt2.x) {
572				fDrawState.SetHighColor(RGBColor(fFrameHighColor));
573				for (int32 i = 0; i < 6; i++) {
574					fDrawingEngine->StrokeLine(offset, pt2,
575						fDrawState.HighColor());
576					offset.y += 2;
577					pt2.y += 2;
578				}
579
580				offset.Set(tab->tabRect.left + tab->textOffset
581					+ tab->truncatedTitleLength + 4, tab->zoomRect.top + 1);
582				pt2.Set(right - 7, tab->zoomRect.top + 1);
583
584				fDrawState.SetHighColor(dark);
585				for(int32 i = 0; i < 6; i++) {
586					fDrawingEngine->StrokeLine(offset, pt2,
587						fDrawState.HighColor());
588					offset.y += 2;
589					pt2.y += 2;
590				}
591			}
592		}
593
594		_DrawButtons(tab, rect);
595	} else {
596		RGBColor inactive(82, 82, 82);
597		// Not focused - Just draw a plain light grey area with the title
598		// in the middle
599		fDrawingEngine->StrokeLine(rect.LeftTop(), rect.RightTop(),
600			inactive);
601		fDrawingEngine->StrokeLine(rect.LeftTop(), rect.LeftBottom(),
602			inactive);
603		fDrawingEngine->StrokeLine(rect.RightBottom(), rect.RightTop(),
604			inactive);
605	}
606
607	_DrawTitle(tab, tab->tabRect);
608}
609
610
611void
612MacDecorator::_DrawButtons(Decorator::Tab* tab, const BRect& invalid)
613{
614	if ((tab->flags & B_NOT_CLOSABLE) == 0
615		&& invalid.Intersects(tab->closeRect)) {
616		_DrawClose(tab, false, tab->closeRect);
617	}
618	if ((tab->flags & B_NOT_MINIMIZABLE) == 0
619		&& invalid.Intersects(tab->minimizeRect)) {
620		_DrawMinimize(tab, false, tab->minimizeRect);
621	}
622	if ((tab->flags & B_NOT_ZOOMABLE) == 0
623		&& invalid.Intersects(tab->zoomRect)) {
624		_DrawZoom(tab, false, tab->zoomRect);
625	}
626}
627
628
629void
630MacDecorator::_DrawTitle(Decorator::Tab* tab, BRect rect)
631{
632	fDrawingEngine->SetHighColor(IsFocus(tab)
633		? fFocusTextColor : fNonFocusTextColor);
634
635	fDrawingEngine->SetLowColor(fFrameMidColor);
636
637	tab->truncatedTitle = Title(tab);
638	fDrawState.Font().TruncateString(&tab->truncatedTitle, B_TRUNCATE_END,
639		(tab->zoomRect.left - 5) - (tab->closeRect.right + 5));
640	fDrawingEngine->SetFont(fDrawState.Font());
641
642	fDrawingEngine->DrawString(tab->truncatedTitle, tab->truncatedTitle.Length(),
643		BPoint(fTitleBarRect.left + tab->textOffset,
644			tab->closeRect.bottom - 1));
645}
646
647
648void
649MacDecorator::_DrawClose(Decorator::Tab* tab, bool direct, BRect r)
650{
651	_DrawButton(tab, direct, r, tab->closePressed);
652}
653
654
655void
656MacDecorator::_DrawZoom(Decorator::Tab* tab, bool direct, BRect rect)
657{
658	_DrawButton(tab, direct, rect, tab->zoomPressed);
659
660	rect.top++;
661	rect.left++;
662	rect.bottom = rect.top + 6;
663	rect.right = rect.left + 6;
664
665	fDrawState.SetHighColor(RGBColor(33, 33, 33));
666	fDrawingEngine->StrokeRect(rect, fDrawState.HighColor());
667}
668
669
670void
671MacDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect)
672{
673	_DrawButton(tab, direct, rect, tab->minimizePressed);
674
675	rect.InsetBy(1, 5);
676
677	fDrawState.SetHighColor(RGBColor(33, 33, 33));
678	fDrawingEngine->StrokeRect(rect, fDrawState.HighColor());
679}
680
681
682void
683MacDecorator::_SetTitle(Tab* tab, const char* string, BRegion* updateRegion)
684{
685	// TODO: we could be much smarter about the update region
686	// TODO may this change the other tabs too ? (to make space for a longer
687	// title ?)
688
689	BRect rect = TabRect(tab);
690
691	_DoLayout();
692
693	if (updateRegion == NULL)
694		return;
695
696	rect = rect | TabRect(tab);
697
698	rect.bottom++;
699		// the border will look differently when the title is adjacent
700
701	updateRegion->Include(rect);
702}
703
704
705// TODO : _SetFocus
706
707
708void
709MacDecorator::_MoveBy(BPoint offset)
710{
711	// Move all internal rectangles the appropriate amount
712	for (int32 i = 0; i < fTabList.CountItems(); i++) {
713		Decorator::Tab* tab = fTabList.ItemAt(i);
714		tab->zoomRect.OffsetBy(offset);
715		tab->minimizeRect.OffsetBy(offset);
716		tab->closeRect.OffsetBy(offset);
717		tab->tabRect.OffsetBy(offset);
718	}
719
720	fFrame.OffsetBy(offset);
721	fTitleBarRect.OffsetBy(offset);
722	fResizeRect.OffsetBy(offset);
723	fBorderRect.OffsetBy(offset);
724}
725
726
727void
728MacDecorator::_ResizeBy(BPoint offset, BRegion* dirty)
729{
730	// Move all internal rectangles the appropriate amount
731	fFrame.right += offset.x;
732	fFrame.bottom += offset.y;
733
734	fTitleBarRect.right += offset.x;
735	fBorderRect.right += offset.x;
736	fBorderRect.bottom += offset.y;
737	// fZoomRect.OffsetBy(offset.x, 0);
738	// fMinimizeRect.OffsetBy(offset.x, 0);
739	if (dirty) {
740		dirty->Include(fTitleBarRect);
741		dirty->Include(fBorderRect);
742	}
743
744	// TODO probably some other layouting stuff here
745	_DoLayout();
746}
747
748
749// TODO : _SetSettings
750
751
752Decorator::Tab*
753MacDecorator::_AllocateNewTab()
754{
755	Decorator::Tab* tab = new(std::nothrow) Decorator::Tab;
756	if (tab == NULL)
757		return NULL;
758
759	// Set appropriate colors based on the current focus value. In this case,
760	// each decorator defaults to not having the focus.
761	_SetFocus(tab);
762	return tab;
763}
764
765
766bool
767MacDecorator::_AddTab(DesktopSettings& settings, int32 index,
768	BRegion* updateRegion)
769{
770	_UpdateFont(settings);
771
772	_DoLayout();
773	if (updateRegion != NULL)
774		updateRegion->Include(fTitleBarRect);
775	return true;
776}
777
778
779bool
780MacDecorator::_RemoveTab(int32 index, BRegion* updateRegion)
781{
782	BRect oldTitle = fTitleBarRect;
783	_DoLayout();
784	if (updateRegion != NULL) {
785		updateRegion->Include(oldTitle);
786		updateRegion->Include(fTitleBarRect);
787	}
788	return true;
789}
790
791
792bool
793MacDecorator::_MoveTab(int32 from, int32 to, bool isMoving,
794	BRegion* updateRegion)
795{
796	return false;
797
798#if 0
799	MacDecorator::Tab* toTab = _TabAt(to);
800	if (toTab == NULL)
801		return false;
802
803	if (from < to) {
804		fOldMovingTab.OffsetBy(toTab->tabRect.Width(), 0);
805		toTab->tabRect.OffsetBy(-fOldMovingTab.Width(), 0);
806	} else {
807		fOldMovingTab.OffsetBy(-toTab->tabRect.Width(), 0);
808		toTab->tabRect.OffsetBy(fOldMovingTab.Width(), 0);
809	}
810
811	toTab->tabOffset = uint32(toTab->tabRect.left - fLeftBorder.left);
812	_LayoutTabItems(toTab, toTab->tabRect);
813
814	_CalculateTabsRegion();
815
816	if (updateRegion != NULL)
817		updateRegion->Include(fTitleBarRect);
818	return true;
819#endif
820}
821
822
823void
824MacDecorator::_GetFootprint(BRegion* region)
825{
826	// This function calculates the decorator's footprint in coordinates
827	// relative to the view. This is most often used to set a Window
828	// object's visible region.
829	if (!region)
830		return;
831
832	region->MakeEmpty();
833
834	if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
835		return;
836
837	region->Set(fBorderRect);
838	region->Exclude(fFrame);
839
840	if (fTopTab->look == B_BORDERED_WINDOW_LOOK)
841		return;
842	region->Include(fTitleBarRect);
843}
844
845
846void
847MacDecorator::_UpdateFont(DesktopSettings& settings)
848{
849	ServerFont font;
850	if (fTopTab && fTopTab->look == B_FLOATING_WINDOW_LOOK)
851		settings.GetDefaultPlainFont(font);
852	else
853		settings.GetDefaultBoldFont(font);
854
855	font.SetFlags(B_FORCE_ANTIALIASING);
856	font.SetSpacing(B_STRING_SPACING);
857	fDrawState.SetFont(font);
858}
859
860
861// #pragma mark - Private methods
862
863
864// Draw a mac-style button
865void
866MacDecorator::_DrawButton(Decorator::Tab* tab, bool direct, BRect r,
867	bool down)
868{
869	BRect rect(r);
870
871	BPoint offset(r.LeftTop()), pt2(r.RightTop());
872
873	// Topleft dark grey border
874	pt2.x--;
875	fDrawingEngine->SetHighColor(RGBColor(136, 136, 136));
876	fDrawingEngine->StrokeLine(offset, pt2);
877
878	pt2 = r.LeftBottom();
879	pt2.y--;
880	fDrawingEngine->StrokeLine(offset, pt2);
881
882	// Bottomright white border
883	offset = r.RightBottom();
884	pt2 = r.RightTop();
885	pt2.y++;
886	fDrawingEngine->SetHighColor(RGBColor(255, 255, 255));
887	fDrawingEngine->StrokeLine(offset, pt2);
888
889	pt2 = r.LeftBottom();
890	pt2.x++;
891	fDrawingEngine->StrokeLine(offset, pt2);
892
893	// Black outline
894	rect.InsetBy(1, 1);
895	fDrawingEngine->SetHighColor(RGBColor(33, 33, 33));
896	fDrawingEngine->StrokeRect(rect);
897
898	// Double-shaded button
899	rect.InsetBy(1, 1);
900	fDrawingEngine->SetHighColor(RGBColor(140, 140, 140));
901	fDrawingEngine->StrokeLine(rect.RightBottom(), rect.RightTop());
902	fDrawingEngine->StrokeLine(rect.RightBottom(), rect.LeftBottom());
903	fDrawingEngine->SetHighColor(RGBColor(206, 206, 206));
904	fDrawingEngine->StrokeLine(rect.LeftBottom(), rect.LeftTop());
905	fDrawingEngine->StrokeLine(rect.LeftTop(), rect.RightTop());
906	fDrawingEngine->SetHighColor(RGBColor(255, 255, 255));
907	fDrawingEngine->StrokeLine(rect.LeftTop(), rect.LeftTop());
908
909	rect.InsetBy(1, 1);
910	_DrawBlendedRect(fDrawingEngine, rect, !down);
911}
912
913
914/*!	\brief Draws a rectangle with a gradient.
915  \param down The rectangle should be drawn recessed or not
916*/
917void
918MacDecorator::_DrawBlendedRect(DrawingEngine* engine, BRect rect,
919	bool down/*, bool focus*/)
920{
921	// figure out which colors to use
922	rgb_color startColor, endColor;
923	if (down) {
924		startColor = fButtonLowColor;
925		endColor = fFrameHighColor;
926	} else {
927		startColor = fButtonHighColor;
928		endColor = fFrameLowerColor;
929	}
930
931	// fill
932	BGradientLinear gradient;
933	gradient.SetStart(rect.LeftTop());
934	gradient.SetEnd(rect.RightBottom());
935	gradient.AddColor(startColor, 0);
936	gradient.AddColor(endColor, 255);
937
938	engine->FillRect(rect, gradient);
939}
940
941
942// #pragma mark - DecorAddOn
943
944
945extern "C" DecorAddOn*
946instantiate_decor_addon(image_id id, const char* name)
947{
948	return new (std::nothrow)MacDecorAddOn(id, name);
949}
950