1/*
2 * Copyright 2001-2014 Haiku, Inc. All rights reserved.
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 *		Clemens Zeidler, haiku@clemens-zeidler.de
10 */
11
12
13/*! Decorator resembling BeOS R5 */
14
15
16#include "BeDecorator.h"
17
18#include <algorithm>
19#include <cmath>
20#include <new>
21#include <stdio.h>
22
23#include <WindowPrivate.h>
24
25#include <Autolock.h>
26#include <Debug.h>
27#include <GradientLinear.h>
28#include <Rect.h>
29#include <Region.h>
30#include <View.h>
31
32#include "BitmapDrawingEngine.h"
33#include "Desktop.h"
34#include "DesktopSettings.h"
35#include "DrawingEngine.h"
36#include "DrawState.h"
37#include "FontManager.h"
38#include "PatternHandler.h"
39#include "RGBColor.h"
40#include "ServerBitmap.h"
41
42
43//#define DEBUG_DECORATOR
44#ifdef DEBUG_DECORATOR
45#	define STRACE(x) printf x
46#else
47#	define STRACE(x) ;
48#endif
49
50
51static const float kBorderResizeLength = 22.0;
52static const float kResizeKnobSize = 18.0;
53
54
55static const unsigned char f = 0xff; // way to write 0xff shorter
56
57static const unsigned char kInnerShadowBits[] = {
58	f, f, f, f, f, f, f, f, f, 0,
59	f, f, f, f, f, f, 0, f, 0, f,
60	f, f, f, f, f, 0, f, 0, f, 0,
61	f, f, f, f, 0, f, 0, 0, 0, 0,
62	f, f, f, 0, f, 0, 0, 0, 0, 0,
63	f, f, 0, f, 0, 0, 0, 0, 0, 0,
64	f, 0, f, 0, 0, 0, 0, 0, 0, 0,
65	f, f, 0, 0, 0, 0, 0, 0, 0, 0,
66	f, 0, f, 0, 0, 0, 0, 0, 0, 0,
67	0, f, 0, 0, 0, 0, 0, 0, 0, 0
68};
69
70static const unsigned char kOuterShadowBits[] = {
71	f, f, f, f, f, f, f, f, f, f,
72	f, f, f, f, f, f, f, f, f, f,
73	f, f, f, f, f, f, f, f, f, f,
74	f, f, f, f, f, f, f, f, f, f,
75	f, f, f, f, f, f, f, f, f, 0,
76	f, f, f, f, f, f, f, 0, 0, 0,
77	f, f, f, f, f, f, 0, f, 0, 0,
78	f, f, f, f, f, 0, f, 0, 0, 0,
79	f, f, f, f, f, 0, 0, 0, 0, 0,
80	f, f, f, f, 0, 0, 0, 0, 0, 0
81};
82
83static const unsigned char kBigInnerShadowBits[] = {
84	f, f, f, f, f, f, f,
85	f, f, f, f, f, f, 0,
86	f, f, f, f, f, 0, 0,
87	f, f, f, f, 0, f, 0,
88	f, f, f, 0, f, 0, 0,
89	f, f, 0, f, 0, 0, 0,
90	f, 0, 0, 0, 0, 0, 0
91};
92
93static const unsigned char kBigOuterShadowBits[] = {
94	f, f, f, f, f, f, f,
95	f, f, f, f, f, f, 0,
96	f, f, f, f, f, f, 0,
97	f, f, f, f, f, f, 0,
98	f, f, f, f, f, f, 0,
99	f, f, f, f, f, f, 0,
100	f, 0, 0, 0, 0, 0, 0
101};
102
103static const unsigned char kSmallInnerShadowBits[] = {
104	f, f, f, 0, 0,
105	f, f, 0, f, 0,
106	f, 0, f, 0, 0,
107	0, f, 0, 0, 0,
108	0, 0, 0, 0, 0
109};
110
111static const unsigned char kSmallOuterShadowBits[] = {
112	f, f, f, f, f,
113	f, f, f, f, f,
114	f, f, f, f, f,
115	f, f, f, f, 0,
116	f, f, 0, 0, 0
117};
118
119static const unsigned char kGlintBits[] = {
120	0, f, 0,
121	f, 0, f,
122	0, f, f
123};
124
125
126//     #pragma mark - BeDecorAddOn
127
128
129BeDecorAddOn::BeDecorAddOn(image_id id, const char* name)
130	:
131	DecorAddOn(id, name)
132{
133}
134
135
136Decorator*
137BeDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect,
138	Desktop* desktop)
139{
140	return new (std::nothrow)BeDecorator(settings, rect, desktop);
141}
142
143
144//	#pragma mark - BeDecorator
145
146
147// TODO: get rid of DesktopSettings here, and introduce private accessor
148//	methods to the Decorator base class
149BeDecorator::BeDecorator(DesktopSettings& settings, BRect rect,
150	Desktop* desktop)
151	:
152	SATDecorator(settings, rect, desktop),
153	fCStatus(B_NO_INIT)
154{
155	STRACE(("BeDecorator:\n"));
156	STRACE(("\tFrame (%.1f,%.1f,%.1f,%.1f)\n",
157		rect.left, rect.top, rect.right, rect.bottom));
158
159	fCloseBitmap = _CreateTemporaryBitmap(BRect(0, 0, 9, 9));
160	fBigZoomBitmap = _CreateTemporaryBitmap(BRect(0, 0, 6, 6));
161	fSmallZoomBitmap = _CreateTemporaryBitmap(BRect(0, 0, 4, 4));
162	fGlintBitmap = _CreateTemporaryBitmap(BRect(0, 0, 2, 2));
163		// glint bitmap is used by close and zoom buttons
164
165	if (fCloseBitmap == NULL || fBigZoomBitmap == NULL
166		|| fSmallZoomBitmap == NULL || fGlintBitmap == NULL) {
167		fCStatus = B_NO_MEMORY;
168	} else
169		fCStatus = B_OK;
170}
171
172
173BeDecorator::~BeDecorator()
174{
175	STRACE(("BeDecorator: ~BeDecorator()\n"));
176	//delete[] fFrameColors;
177
178	if (fCloseBitmap != NULL)
179		fCloseBitmap->ReleaseReference();
180
181	if (fBigZoomBitmap != NULL)
182		fBigZoomBitmap->ReleaseReference();
183
184	if (fSmallZoomBitmap != NULL)
185		fSmallZoomBitmap->ReleaseReference();
186
187	if (fGlintBitmap != NULL)
188		fGlintBitmap->ReleaseReference();
189}
190
191
192// #pragma mark - Public methods
193
194
195/*!	Returns the frame colors for the specified decorator component.
196
197	The meaning of the color array elements depends on the specified component.
198	For some components some array elements are unused.
199
200	\param component The component for which to return the frame colors.
201	\param highlight The highlight set for the component.
202	\param colors An array of colors to be initialized by the function.
203*/
204void
205BeDecorator::GetComponentColors(Component component, uint8 highlight,
206	ComponentColors _colors, Decorator::Tab* _tab)
207{
208	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
209	switch (component) {
210		case COMPONENT_TAB:
211			if (highlight == HIGHLIGHT_STACK_AND_TILE) {
212				_colors[COLOR_TAB_FRAME_LIGHT]
213					= tint_color(fFocusFrameColor, B_DARKEN_3_TINT);
214				_colors[COLOR_TAB_FRAME_DARK]
215					= tint_color(fFocusFrameColor, B_DARKEN_4_TINT);
216				_colors[COLOR_TAB] = tint_color(fFocusTabColor,
217					B_DARKEN_1_TINT);
218				_colors[COLOR_TAB_LIGHT] = tint_color(fFocusTabColorLight,
219					B_DARKEN_1_TINT);
220				_colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel;
221				_colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow;
222				_colors[COLOR_TAB_TEXT] = fFocusTextColor;
223			} else if (tab && tab->buttonFocus) {
224				_colors[COLOR_TAB_FRAME_LIGHT]
225					= tint_color(fFocusFrameColor, B_DARKEN_2_TINT);
226				_colors[COLOR_TAB_FRAME_DARK]
227					= tint_color(fFocusFrameColor, B_DARKEN_3_TINT);
228				_colors[COLOR_TAB] = fFocusTabColor;
229				_colors[COLOR_TAB_LIGHT] = fFocusTabColorLight;
230				_colors[COLOR_TAB_BEVEL] = fFocusTabColorBevel;
231				_colors[COLOR_TAB_SHADOW] = fFocusTabColorShadow;
232				_colors[COLOR_TAB_TEXT] = fFocusTextColor;
233			} else {
234				_colors[COLOR_TAB_FRAME_LIGHT]
235					= tint_color(fNonFocusFrameColor, B_DARKEN_2_TINT);
236				_colors[COLOR_TAB_FRAME_DARK]
237					= tint_color(fNonFocusFrameColor, B_DARKEN_3_TINT);
238				_colors[COLOR_TAB] = fNonFocusTabColor;
239				_colors[COLOR_TAB_LIGHT] = fNonFocusTabColorLight;
240				_colors[COLOR_TAB_BEVEL] = fNonFocusTabColorBevel;
241				_colors[COLOR_TAB_SHADOW] = fNonFocusTabColorShadow;
242				_colors[COLOR_TAB_TEXT] = fNonFocusTextColor;
243			}
244			break;
245
246		case COMPONENT_CLOSE_BUTTON:
247		case COMPONENT_ZOOM_BUTTON:
248			if (highlight == HIGHLIGHT_STACK_AND_TILE) {
249				_colors[COLOR_BUTTON] = tint_color(fFocusTabColor,
250					B_DARKEN_1_TINT);
251				_colors[COLOR_BUTTON_LIGHT] = tint_color(fFocusTabColorLight,
252					B_DARKEN_1_TINT);
253			} else if (tab && tab->buttonFocus) {
254				_colors[COLOR_BUTTON] = fFocusTabColor;
255				_colors[COLOR_BUTTON_LIGHT] = fFocusTabColorLight;
256			} else {
257				_colors[COLOR_BUTTON] = fNonFocusTabColor;
258				_colors[COLOR_BUTTON_LIGHT] = fNonFocusTabColorLight;
259			}
260			break;
261
262		case COMPONENT_LEFT_BORDER:
263		case COMPONENT_RIGHT_BORDER:
264		case COMPONENT_TOP_BORDER:
265		case COMPONENT_BOTTOM_BORDER:
266		case COMPONENT_RESIZE_CORNER:
267		default:
268		{
269			rgb_color base;
270			if (highlight == HIGHLIGHT_STACK_AND_TILE)
271				base = tint_color(fFocusFrameColor, B_DARKEN_3_TINT);
272			else if (tab && tab->buttonFocus)
273				base = fFocusFrameColor;
274			else
275				base = fNonFocusFrameColor;
276
277			//_colors[0].SetColor(152, 152, 152);
278			//_colors[1].SetColor(255, 255, 255);
279			//_colors[2].SetColor(216, 216, 216);
280			//_colors[3].SetColor(136, 136, 136);
281			//_colors[4].SetColor(152, 152, 152);
282			//_colors[5].SetColor(96, 96, 96);
283
284			_colors[0].red = std::max(0, base.red - 72);
285			_colors[0].green = std::max(0, base.green - 72);
286			_colors[0].blue = std::max(0, base.blue - 72);
287			_colors[0].alpha = 255;
288
289			_colors[1].red = std::min(255, base.red + 64);
290			_colors[1].green = std::min(255, base.green  + 64);
291			_colors[1].blue = std::min(255, base.blue  + 64);
292			_colors[1].alpha = 255;
293
294			_colors[2].red = std::max(0, base.red - 8);
295			_colors[2].green = std::max(0, base.green - 8);
296			_colors[2].blue = std::max(0, base.blue - 8);
297			_colors[2].alpha = 255;
298
299			_colors[3].red = std::max(0, base.red - 88);
300			_colors[3].green = std::max(0, base.green - 88);
301			_colors[3].blue = std::max(0, base.blue - 88);
302			_colors[3].alpha = 255;
303
304			_colors[4].red = std::max(0, base.red - 72);
305			_colors[4].green = std::max(0, base.green - 72);
306			_colors[4].blue = std::max(0, base.blue - 72);
307			_colors[4].alpha = 255;
308
309			_colors[5].red = std::max(0, base.red - 128);
310			_colors[5].green = std::max(0, base.green - 128);
311			_colors[5].blue = std::max(0, base.blue - 128);
312			_colors[5].alpha = 255;
313
314			// for the resize-border highlight dye everything bluish.
315			if (highlight == HIGHLIGHT_RESIZE_BORDER) {
316				for (int32 i = 0; i < 6; i++) {
317					_colors[i].red = std::max((int)_colors[i].red - 80, 0);
318					_colors[i].green = std::max((int)_colors[i].green - 80, 0);
319					_colors[i].blue = 255;
320				}
321			}
322			break;
323		}
324	}
325}
326
327
328// #pragma mark - Protected methods
329
330
331void
332BeDecorator::_DrawFrame(BRect invalid)
333{
334	STRACE(("_DrawFrame(%f,%f,%f,%f)\n", invalid.left, invalid.top,
335		invalid.right, invalid.bottom));
336
337	// NOTE: the DrawingEngine needs to be locked for the entire
338	// time for the clipping to stay valid for this decorator
339
340	if (fTopTab->look == B_NO_BORDER_WINDOW_LOOK)
341		return;
342
343	if (fBorderWidth <= 0)
344		return;
345
346	// Draw the border frame
347	BRect r = BRect(fTopBorder.LeftTop(), fBottomBorder.RightBottom());
348	switch ((int)fTopTab->look) {
349		case B_TITLED_WINDOW_LOOK:
350		case B_DOCUMENT_WINDOW_LOOK:
351		case B_MODAL_WINDOW_LOOK:
352		{
353			// top
354			if (invalid.Intersects(fTopBorder)) {
355				ComponentColors colors;
356				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);
357
358				for (int8 i = 0; i < 5; i++) {
359					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
360						BPoint(r.right - i, r.top + i), colors[i]);
361				}
362				if (fTitleBarRect.IsValid()) {
363					// grey along the bottom of the tab
364					// (overwrites "white" from frame)
365					fDrawingEngine->StrokeLine(
366						BPoint(fTitleBarRect.left + 2,
367							fTitleBarRect.bottom + 1),
368						BPoint(fTitleBarRect.right - 2,
369							fTitleBarRect.bottom + 1),
370						colors[2]);
371				}
372			}
373			// left
374			if (invalid.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
375				ComponentColors colors;
376				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
377
378				for (int8 i = 0; i < 5; i++) {
379					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
380						BPoint(r.left + i, r.bottom - i), colors[i]);
381				}
382			}
383			// bottom
384			if (invalid.Intersects(fBottomBorder)) {
385				ComponentColors colors;
386				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);
387
388				for (int8 i = 0; i < 5; i++) {
389					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
390						BPoint(r.right - i, r.bottom - i),
391						colors[(4 - i) == 4 ? 5 : (4 - i)]);
392				}
393			}
394			// right
395			if (invalid.Intersects(
396					fRightBorder.InsetByCopy(0, -fBorderWidth))) {
397				ComponentColors colors;
398				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);
399
400				for (int8 i = 0; i < 5; i++) {
401					fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
402						BPoint(r.right - i, r.bottom - i),
403						colors[(4 - i) == 4 ? 5 : (4 - i)]);
404				}
405			}
406			break;
407		}
408
409		case B_FLOATING_WINDOW_LOOK:
410		case kLeftTitledWindowLook:
411		{
412			// top
413			if (invalid.Intersects(fTopBorder)) {
414				ComponentColors colors;
415				_GetComponentColors(COMPONENT_TOP_BORDER, colors, fTopTab);
416
417				for (int8 i = 0; i < 3; i++) {
418					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
419						BPoint(r.right - i, r.top + i), colors[i * 2]);
420				}
421				if (fTitleBarRect.IsValid()
422					&& fTopTab->look != kLeftTitledWindowLook) {
423					// grey along the bottom of the tab
424					// (overwrites "white" from frame)
425					fDrawingEngine->StrokeLine(
426						BPoint(fTitleBarRect.left + 2,
427							fTitleBarRect.bottom + 1),
428						BPoint(fTitleBarRect.right - 2,
429							fTitleBarRect.bottom + 1), colors[2]);
430				}
431			}
432			// left
433			if (invalid.Intersects(fLeftBorder.InsetByCopy(0, -fBorderWidth))) {
434				ComponentColors colors;
435				_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
436
437				for (int8 i = 0; i < 3; i++) {
438					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.top + i),
439						BPoint(r.left + i, r.bottom - i), colors[i * 2]);
440				}
441				if (fTopTab->look == kLeftTitledWindowLook
442					&& fTitleBarRect.IsValid()) {
443					// grey along the right side of the tab
444					// (overwrites "white" from frame)
445					fDrawingEngine->StrokeLine(
446						BPoint(fTitleBarRect.right + 1,
447							fTitleBarRect.top + 2),
448						BPoint(fTitleBarRect.right + 1,
449							fTitleBarRect.bottom - 2), colors[2]);
450				}
451			}
452			// bottom
453			if (invalid.Intersects(fBottomBorder)) {
454				ComponentColors colors;
455				_GetComponentColors(COMPONENT_BOTTOM_BORDER, colors, fTopTab);
456
457				for (int8 i = 0; i < 3; i++) {
458					fDrawingEngine->StrokeLine(BPoint(r.left + i, r.bottom - i),
459						BPoint(r.right - i, r.bottom - i),
460						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
461				}
462			}
463			// right
464			if (invalid.Intersects(fRightBorder.InsetByCopy(0, -fBorderWidth))) {
465				ComponentColors colors;
466				_GetComponentColors(COMPONENT_RIGHT_BORDER, colors, fTopTab);
467
468				for (int8 i = 0; i < 3; i++) {
469					fDrawingEngine->StrokeLine(BPoint(r.right - i, r.top + i),
470						BPoint(r.right - i, r.bottom - i),
471						colors[(2 - i) == 2 ? 5 : (2 - i) * 2]);
472				}
473			}
474			break;
475		}
476
477		case B_BORDERED_WINDOW_LOOK:
478		{
479			// TODO: Draw the borders individually!
480			ComponentColors colors;
481			_GetComponentColors(COMPONENT_LEFT_BORDER, colors, fTopTab);
482
483			fDrawingEngine->StrokeRect(r, colors[5]);
484			break;
485		}
486
487		default:
488			// don't draw a border frame
489			break;
490	}
491
492	// Draw the resize knob if we're supposed to
493	if (!(fTopTab->flags & B_NOT_RESIZABLE)) {
494		r = fResizeRect;
495
496		ComponentColors colors;
497		_GetComponentColors(COMPONENT_RESIZE_CORNER, colors, fTopTab);
498
499		switch ((int)fTopTab->look) {
500			case B_DOCUMENT_WINDOW_LOOK:
501			{
502				if (!invalid.Intersects(r))
503					break;
504
505				float x = r.right - 3;
506				float y = r.bottom - 3;
507
508				BRect bg(x - 13, y - 13, x, y);
509
510				BGradientLinear gradient;
511				gradient.SetStart(bg.LeftTop());
512				gradient.SetEnd(bg.RightBottom());
513				gradient.AddColor(colors[1], 0);
514				gradient.AddColor(colors[2], 255);
515
516				fDrawingEngine->FillRect(bg, gradient);
517
518				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
519					BPoint(x - 15, y - 2), colors[0]);
520				fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14),
521					BPoint(x - 14, y - 1), colors[1]);
522				fDrawingEngine->StrokeLine(BPoint(x - 15, y - 15),
523					BPoint(x - 2, y - 15), colors[0]);
524				fDrawingEngine->StrokeLine(BPoint(x - 14, y - 14),
525					BPoint(x - 1, y - 14), colors[1]);
526
527				if (fTopTab && !IsFocus(fTopTab))
528					break;
529
530				static const rgb_color kWhite
531					= (rgb_color){ 255, 255, 255, 255 };
532				for (int8 i = 1; i <= 4; i++) {
533					for (int8 j = 1; j <= i; j++) {
534						BPoint pt1(x - (3 * j) + 1, y - (3 * (5 - i)) + 1);
535						BPoint pt2(x - (3 * j) + 2, y - (3 * (5 - i)) + 2);
536						fDrawingEngine->StrokePoint(pt1, colors[0]);
537						fDrawingEngine->StrokePoint(pt2, kWhite);
538					}
539				}
540				break;
541			}
542
543			case B_TITLED_WINDOW_LOOK:
544			case B_FLOATING_WINDOW_LOOK:
545			case B_MODAL_WINDOW_LOOK:
546			case kLeftTitledWindowLook:
547			{
548				if (!invalid.Intersects(BRect(fRightBorder.right
549						- kBorderResizeLength,
550					fBottomBorder.bottom - kBorderResizeLength,
551					fRightBorder.right - 1, fBottomBorder.bottom - 1))) {
552					break;
553				}
554
555				fDrawingEngine->StrokeLine(BPoint(fRightBorder.left,
556					fBottomBorder.bottom - kBorderResizeLength),
557					BPoint(fRightBorder.right - 1,
558						fBottomBorder.bottom - kBorderResizeLength),
559					colors[0]);
560				fDrawingEngine->StrokeLine(
561					BPoint(fRightBorder.right - kBorderResizeLength,
562						fBottomBorder.top),
563					BPoint(fRightBorder.right - kBorderResizeLength,
564						fBottomBorder.bottom - 1),
565					colors[0]);
566				break;
567			}
568
569			default:
570				// don't draw resize corner
571				break;
572		}
573	}
574}
575
576
577/*!	\brief Actually draws the tab
578
579	This function is called when the tab itself needs drawn. Other items,
580	like the window title or buttons, should not be drawn here.
581
582	\param tab The \a tab to update.
583	\param invalid The area of the \a tab to update.
584*/
585void
586BeDecorator::_DrawTab(Decorator::Tab* tab, BRect invalid)
587{
588	STRACE(("_DrawTab(%.1f, %.1f, %.1f, %.1f)\n",
589			invalid.left, invalid.top, invalid.right, invalid.bottom));
590	const BRect& tabRect = tab->tabRect;
591	// If a window has a tab, this will draw it and any buttons which are
592	// in it.
593	if (!tabRect.IsValid() || !invalid.Intersects(tabRect))
594		return;
595
596	ComponentColors colors;
597	_GetComponentColors(COMPONENT_TAB, colors, tab);
598
599	// outer frame
600	fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.LeftBottom(),
601		colors[COLOR_TAB_FRAME_LIGHT]);
602	fDrawingEngine->StrokeLine(tabRect.LeftTop(), tabRect.RightTop(),
603		colors[COLOR_TAB_FRAME_LIGHT]);
604	if (tab->look != kLeftTitledWindowLook) {
605		fDrawingEngine->StrokeLine(tabRect.RightTop(), tabRect.RightBottom(),
606			colors[COLOR_TAB_FRAME_DARK]);
607	} else {
608		fDrawingEngine->StrokeLine(tabRect.LeftBottom(),
609			tabRect.RightBottom(), colors[COLOR_TAB_FRAME_DARK]);
610	}
611
612	float tabBotton = tabRect.bottom;
613	if (fTopTab != tab)
614		tabBotton -= 1;
615
616	// bevel
617	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
618		BPoint(tabRect.left + 1,
619			tabBotton - (tab->look == kLeftTitledWindowLook ? 1 : 0)),
620		colors[COLOR_TAB_BEVEL]);
621	fDrawingEngine->StrokeLine(BPoint(tabRect.left + 1, tabRect.top + 1),
622		BPoint(tabRect.right - (tab->look == kLeftTitledWindowLook ? 0 : 1),
623			tabRect.top + 1),
624		colors[COLOR_TAB_BEVEL]);
625
626	if (tab->look != kLeftTitledWindowLook) {
627		fDrawingEngine->StrokeLine(BPoint(tabRect.right - 1, tabRect.top + 2),
628			BPoint(tabRect.right - 1, tabBotton),
629			colors[COLOR_TAB_SHADOW]);
630	} else {
631		fDrawingEngine->StrokeLine(
632			BPoint(tabRect.left + 2, tabRect.bottom - 1),
633			BPoint(tabRect.right, tabRect.bottom - 1),
634			colors[COLOR_TAB_SHADOW]);
635	}
636
637	// fill
638	if (fTopTab->look != kLeftTitledWindowLook) {
639		fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2,
640			tabRect.right - 2, tabRect.bottom), colors[COLOR_TAB]);
641	} else {
642		fDrawingEngine->FillRect(BRect(tabRect.left + 2, tabRect.top + 2,
643			tabRect.right, tabRect.bottom - 2), colors[COLOR_TAB]);
644	}
645
646	_DrawTitle(tab, tabRect);
647
648	_DrawButtons(tab, invalid);
649}
650
651
652/*!	\brief Actually draws the title
653
654	The main tasks for this function are to ensure that the decorator draws
655	the title only in its own area and drawing the title itself.
656	Using B_OP_COPY for drawing the title is recommended because of the marked
657	performance hit of the other drawing modes, but it is not a requirement.
658
659	\param _tab The \a tab to update.
660	\param r area of the title to update.
661*/
662void
663BeDecorator::_DrawTitle(Decorator::Tab* _tab, BRect r)
664{
665	STRACE(("_DrawTitle(%f, %f, %f, %f)\n", r.left, r.top, r.right, r.bottom));
666
667	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
668
669	const BRect& tabRect = tab->tabRect;
670	const BRect& closeRect = tab->closeRect;
671	const BRect& zoomRect = tab->zoomRect;
672
673	ComponentColors colors;
674	_GetComponentColors(COMPONENT_TAB, colors, tab);
675
676	fDrawingEngine->SetDrawingMode(B_OP_OVER);
677	fDrawingEngine->SetHighColor(colors[COLOR_TAB_TEXT]);
678	fDrawingEngine->SetLowColor(colors[COLOR_TAB]);
679	fDrawingEngine->SetFont(fDrawState.Font());
680
681	// figure out position of text
682	font_height fontHeight;
683	fDrawState.Font().GetHeight(fontHeight);
684
685	BPoint titlePos;
686	if (fTopTab->look != kLeftTitledWindowLook) {
687		titlePos.x = closeRect.IsValid() ? closeRect.right + tab->textOffset
688			: tabRect.left + tab->textOffset;
689		titlePos.y = floorf(((tabRect.top + 2.0) + tabRect.bottom
690			+ fontHeight.ascent + fontHeight.descent) / 2.0
691			- fontHeight.descent + 0.5);
692	} else {
693		titlePos.x = floorf(((tabRect.left + 2.0) + tabRect.right
694			+ fontHeight.ascent + fontHeight.descent) / 2.0
695			- fontHeight.descent + 0.5);
696		titlePos.y = zoomRect.IsValid() ? zoomRect.top - tab->textOffset
697			: tabRect.bottom - tab->textOffset;
698	}
699
700	fDrawingEngine->SetFont(fDrawState.Font());
701
702	fDrawingEngine->DrawString(tab->truncatedTitle.String(),
703		tab->truncatedTitleLength, titlePos);
704
705	fDrawingEngine->SetDrawingMode(B_OP_COPY);
706}
707
708
709/*!	\brief Actually draws the close button
710
711	Unless a subclass has a particularly large button, it is probably
712	unnecessary to check the update rectangle.
713
714	\param _tab The \a tab to update.
715	\param direct Draw without double buffering.
716	\param rect The area of the button to update.
717*/
718void
719BeDecorator::_DrawClose(Decorator::Tab* _tab, bool direct, BRect rect)
720{
721	STRACE(("_DrawClose(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
722		rect.bottom));
723
724	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
725
726	int32 index = (tab->buttonFocus ? 0 : 1) + (tab->closePressed ? 0 : 2);
727	ServerBitmap* bitmap = tab->closeBitmaps[index];
728	if (bitmap == NULL) {
729		bitmap = _GetBitmapForButton(tab, COMPONENT_CLOSE_BUTTON,
730			tab->closePressed, rect.IntegerWidth(), rect.IntegerHeight());
731		tab->closeBitmaps[index] = bitmap;
732	}
733
734	_DrawButtonBitmap(bitmap, direct, rect);
735}
736
737
738/*!	\brief Actually draws the zoom button
739
740	Unless a subclass has a particularly large button, it is probably
741	unnecessary to check the update rectangle.
742
743	\param _tab The \a tab to update.
744	\param direct Draw without double buffering.
745	\param rect The area of the button to update.
746*/
747void
748BeDecorator::_DrawZoom(Decorator::Tab* _tab, bool direct, BRect rect)
749{
750	STRACE(("_DrawZoom(%f,%f,%f,%f)\n", rect.left, rect.top, rect.right,
751		rect.bottom));
752
753	if (rect.IntegerWidth() < 1)
754		return;
755
756	Decorator::Tab* tab = static_cast<Decorator::Tab*>(_tab);
757	int32 index = (tab->buttonFocus ? 0 : 1) + (tab->zoomPressed ? 0 : 2);
758	ServerBitmap* bitmap = tab->zoomBitmaps[index];
759	if (bitmap == NULL) {
760		bitmap = _GetBitmapForButton(tab, COMPONENT_ZOOM_BUTTON,
761			tab->zoomPressed, rect.IntegerWidth(), rect.IntegerHeight());
762		tab->zoomBitmaps[index] = bitmap;
763	}
764
765	_DrawButtonBitmap(bitmap, direct, rect);
766}
767
768
769void
770BeDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect)
771{
772	// This decorator doesn't have this button
773}
774
775
776void
777BeDecorator::_GetButtonSizeAndOffset(const BRect& tabRect, float* _offset,
778	float* _size, float* _inset) const
779{
780	float tabSize = fTopTab->look == kLeftTitledWindowLook ?
781		tabRect.Width() : tabRect.Height();
782
783	*_offset = 5.0f;
784	*_inset = 0.0f;
785
786	*_size = std::max(0.0f, tabSize - 7.0f);
787}
788
789
790// #pragma mark - Private methods
791
792
793/*!
794	\brief Draws a bevel around a rectangle.
795	\param rect The rectangular area to draw in.
796	\param down Whether or not the button is pressed down.
797	\param light The light color to use.
798	\param shadow The shadow color to use.
799*/
800void
801BeDecorator::_DrawBevelRect(DrawingEngine* engine, const BRect rect, bool down,
802	rgb_color light, rgb_color shadow)
803{
804	if (down) {
805		BRect inner(rect.InsetByCopy(1.0f, 1.0f));
806
807		engine->StrokeLine(rect.LeftBottom(), rect.LeftTop(), shadow);
808		engine->StrokeLine(rect.LeftTop(), rect.RightTop(), shadow);
809		engine->StrokeLine(inner.LeftBottom(), inner.LeftTop(), shadow);
810		engine->StrokeLine(inner.LeftTop(), inner.RightTop(), shadow);
811
812		engine->StrokeLine(rect.RightTop(), rect.RightBottom(), light);
813		engine->StrokeLine(rect.RightBottom(), rect.LeftBottom(), light);
814		engine->StrokeLine(inner.RightTop(), inner.RightBottom(), light);
815		engine->StrokeLine(inner.RightBottom(), inner.LeftBottom(), light);
816	} else {
817		BRect r1(rect);
818		r1.left += 1.0f;
819		r1.top  += 1.0f;
820
821		BRect r2(rect);
822		r2.bottom -= 1.0f;
823		r2.right  -= 1.0f;
824
825		engine->StrokeRect(r2, shadow);
826			// inner dark box
827		engine->StrokeRect(rect, shadow);
828			// outer dark box
829		engine->StrokeRect(r1, light);
830			// light box
831	}
832}
833
834
835/*!
836	\brief Draws a framed rectangle with a gradient.
837	\param rect The rectangular area to draw in.
838	\param startColor The start color of the gradient.
839	\param endColor The end color of the gradient.
840*/
841void
842BeDecorator::_DrawBlendedRect(DrawingEngine* engine, const BRect rect,
843	bool down, rgb_color colorA, rgb_color colorB, rgb_color colorC,
844	rgb_color colorD)
845{
846	BRect fillRect(rect.InsetByCopy(1.0f, 1.0f));
847
848	BGradientLinear gradient;
849	if (down) {
850		gradient.SetStart(fillRect.RightBottom());
851		gradient.SetEnd(fillRect.LeftTop());
852	} else {
853		gradient.SetStart(fillRect.LeftTop());
854		gradient.SetEnd(fillRect.RightBottom());
855	}
856
857	gradient.AddColor(colorA, 0);
858	gradient.AddColor(colorB, 95);
859	gradient.AddColor(colorC, 159);
860	gradient.AddColor(colorD, 255);
861
862	engine->FillRect(fillRect, gradient);
863}
864
865
866void
867BeDecorator::_DrawButtonBitmap(ServerBitmap* bitmap, bool direct, BRect rect)
868{
869	if (bitmap == NULL)
870		return;
871
872	bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
873	fDrawingEngine->SetCopyToFrontEnabled(direct);
874	drawing_mode oldMode;
875	fDrawingEngine->SetDrawingMode(B_OP_OVER, oldMode);
876	fDrawingEngine->DrawBitmap(bitmap, rect.OffsetToCopy(0, 0), rect);
877	fDrawingEngine->SetDrawingMode(oldMode);
878	fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
879}
880
881
882ServerBitmap*
883BeDecorator::_GetBitmapForButton(Decorator::Tab* tab, Component item,
884	bool down, int32 width, int32 height)
885{
886	uint8* data;
887	size_t size;
888	size_t offset;
889
890	// TODO: the list of shared bitmaps is never freed
891	struct decorator_bitmap {
892		Component			item;
893		bool				down;
894		int32				width;
895		int32				height;
896		rgb_color			baseColor;
897		rgb_color			lightColor;
898		UtilityBitmap*		bitmap;
899		decorator_bitmap*	next;
900	};
901
902	static BLocker sBitmapListLock("decorator lock", true);
903	static decorator_bitmap* sBitmapList = NULL;
904
905	// BeOS R5 colors
906	// button:  active: 255, 203, 0  inactive: 232, 232, 232
907	// light1:  active: 255, 238, 0  inactive: 255, 255, 255
908	// light2:  active: 255, 255, 26 inactive: 255, 255, 255
909	// shadow1: active: 235, 183, 0  inactive: 211, 211, 211
910	// shadow2 is a bit lighter on zoom than on close button
911
912	ComponentColors colors;
913	_GetComponentColors(item, colors, tab);
914
915	const rgb_color buttonColor(colors[COLOR_BUTTON]);
916
917	bool isGrayscale = buttonColor.red == buttonColor.green
918		&& buttonColor.green == buttonColor.blue;
919
920	rgb_color buttonColorLight1(buttonColor);
921	buttonColorLight1.red = std::min(255, buttonColor.red + 35),
922	buttonColorLight1.green = std::min(255, buttonColor.green + 35),
923	buttonColorLight1.blue = std::min(255, buttonColor.blue
924		+ (isGrayscale ? 35 : 0));
925		// greyscale color stays grayscale
926
927	rgb_color buttonColorLight2(buttonColor);
928	buttonColorLight2.red = std::min(255, buttonColor.red + 52),
929	buttonColorLight2.green = std::min(255, buttonColor.green + 52),
930	buttonColorLight2.blue = std::min(255, buttonColor.blue + 26);
931
932	rgb_color buttonColorShadow1(buttonColor);
933	buttonColorShadow1.red = std::max(0, buttonColor.red - 21),
934	buttonColorShadow1.green = std::max(0, buttonColor.green - 21),
935	buttonColorShadow1.blue = std::max(0, buttonColor.blue - 21);
936
937	BAutolock locker(sBitmapListLock);
938
939	// search our list for a matching bitmap
940	// TODO: use a hash map instead?
941	decorator_bitmap* current = sBitmapList;
942	while (current) {
943		if (current->item == item && current->down == down
944			&& current->width == width && current->height == height
945			&& current->baseColor == colors[COLOR_BUTTON]
946			&& current->lightColor == colors[COLOR_BUTTON_LIGHT]) {
947			return current->bitmap;
948		}
949
950		current = current->next;
951	}
952
953	static BitmapDrawingEngine* sBitmapDrawingEngine = NULL;
954
955	// didn't find any bitmap, create a new one
956	if (sBitmapDrawingEngine == NULL)
957		sBitmapDrawingEngine = new(std::nothrow) BitmapDrawingEngine();
958	if (sBitmapDrawingEngine == NULL
959		|| sBitmapDrawingEngine->SetSize(width, height) != B_OK) {
960		return NULL;
961	}
962
963	BRect rect(0, 0, width - 1, height - 1);
964
965	STRACE(("BeDecorator creating bitmap for %s %s at size %ldx%ld\n",
966		item == COMPONENT_CLOSE_BUTTON ? "close" : "zoom",
967		down ? "down" : "up", width, height));
968	switch (item) {
969		case COMPONENT_CLOSE_BUTTON:
970		{
971			// BeOS R5 shadow2: active: 183, 131, 0 inactive: 160, 160, 160
972			rgb_color buttonColorShadow2(buttonColor);
973			buttonColorShadow2.red = std::max(0, buttonColor.red - 72),
974			buttonColorShadow2.green = std::max(0, buttonColor.green - 72),
975			buttonColorShadow2.blue = std::max(0, buttonColor.blue - 72);
976
977			// fill the background
978			sBitmapDrawingEngine->FillRect(rect, buttonColor);
979
980			// draw outer bevel
981			_DrawBevelRect(sBitmapDrawingEngine, rect, tab->closePressed,
982				buttonColorLight2, buttonColorShadow2);
983
984			if (fCStatus != B_OK) {
985				// If we ran out of memory while initializing bitmaps
986				// fall back to a linear gradient.
987				rect.InsetBy(1, 1);
988				_DrawBlendedRect(sBitmapDrawingEngine, rect, tab->closePressed,
989					buttonColorLight2, buttonColorLight1, buttonColor,
990					buttonColorShadow1);
991
992				break;
993			}
994
995			// inset by bevel
996			rect.InsetBy(2, 2);
997
998			// fill bg
999			sBitmapDrawingEngine->FillRect(rect, buttonColorLight1);
1000
1001			// treat background color as transparent
1002			sBitmapDrawingEngine->SetDrawingMode(B_OP_OVER);
1003			sBitmapDrawingEngine->SetLowColor(buttonColorLight1);
1004
1005			if (tab->closePressed) {
1006				// Draw glint in bottom right, then combined inner and outer
1007				// shadow in top left.
1008				// Read the source bitmap in forward while writing the
1009				// destination in reverse to rotate the bitmap by 180��.
1010
1011				data = fGlintBitmap->Bits();
1012				size = sizeof(kGlintBits);
1013				for (size_t i = 0; i < size; i++) {
1014					offset = (size - 1 - i) * 4;
1015					if (kGlintBits[i] == 0) {
1016						// draw glint color
1017						data[offset + 0] = buttonColorLight2.blue;
1018						data[offset + 1] = buttonColorLight2.green;
1019						data[offset + 2] = buttonColorLight2.red;
1020					} else {
1021						// draw background color
1022						data[offset + 0] = buttonColorLight1.blue;
1023						data[offset + 1] = buttonColorLight1.green;
1024						data[offset + 2] = buttonColorLight1.red;
1025					}
1026				}
1027				// glint is 3x3
1028				const BRect rightBottom(BRect(rect.right - 2, rect.bottom - 2,
1029					rect.right, rect.bottom));
1030				sBitmapDrawingEngine->DrawBitmap(fGlintBitmap,
1031					fGlintBitmap->Bounds(), rightBottom);
1032
1033				data = fCloseBitmap->Bits();
1034				size = sizeof(kOuterShadowBits);
1035				for (size_t i = 0; i < size; i++) {
1036					offset = (size - 1 - i) * 4;
1037					if (kOuterShadowBits[i] == 0) {
1038						// draw outer shadow
1039						data[offset + 0] = buttonColorShadow1.blue;
1040						data[offset + 1] = buttonColorShadow1.green;
1041						data[offset + 2] = buttonColorShadow1.red;
1042					} else if (kInnerShadowBits[i] == 0) {
1043						// draw inner shadow
1044						data[offset + 0] = buttonColor.blue;
1045						data[offset + 1] = buttonColor.green;
1046						data[offset + 2] = buttonColor.red;
1047					} else {
1048						// draw background color
1049						data[offset + 0] = buttonColorLight1.blue;
1050						data[offset + 1] = buttonColorLight1.green;
1051						data[offset + 2] = buttonColorLight1.red;
1052					}
1053				}
1054				// shadow is 10x10
1055				const BRect leftTop(rect.left, rect.top,
1056					rect.left + 9, rect.top + 9);
1057				sBitmapDrawingEngine->DrawBitmap(fCloseBitmap,
1058					fCloseBitmap->Bounds(), leftTop);
1059			} else {
1060				// draw glint, then draw combined outer and inner shadows
1061
1062				data = fGlintBitmap->Bits();
1063				size = sizeof(kGlintBits);
1064				for (size_t i = 0; i < size; i++) {
1065					offset = i * 4 + 0;
1066					if (kGlintBits[i] == 0) {
1067						// draw glint color
1068						data[offset + 0] = buttonColorLight2.blue;
1069						data[offset + 1] = buttonColorLight2.green;
1070						data[offset + 2] = buttonColorLight2.red;
1071					} else {
1072						// draw background color
1073						data[offset + 0] = buttonColorLight1.blue;
1074						data[offset + 1] = buttonColorLight1.green;
1075						data[offset + 2] = buttonColorLight1.red;
1076					}
1077				}
1078				// glint is 3x3
1079				const BRect leftTop(rect.left, rect.top,
1080					rect.left + 2, rect.top + 2);
1081				sBitmapDrawingEngine->DrawBitmap(fGlintBitmap,
1082					fGlintBitmap->Bounds(), leftTop);
1083
1084				data = fCloseBitmap->Bits();
1085				size = sizeof(kOuterShadowBits);
1086				for (size_t i = 0; i < size; i++) {
1087					offset = i * 4 + 0;
1088					if (kOuterShadowBits[i] == 0) {
1089						// draw outer shadow
1090						data[offset + 0] = buttonColorShadow1.blue;
1091						data[offset + 1] = buttonColorShadow1.green;
1092						data[offset + 2] = buttonColorShadow1.red;
1093					} else if (kInnerShadowBits[i] == 0) {
1094						// draw inner shadow
1095						data[offset + 0] = buttonColor.blue;
1096						data[offset + 1] = buttonColor.green;
1097						data[offset + 2] = buttonColor.red;
1098					} else {
1099						// draw background color
1100						data[offset + 0] = buttonColorLight1.blue;
1101						data[offset + 1] = buttonColorLight1.green;
1102						data[offset + 2] = buttonColorLight1.red;
1103					}
1104				}
1105				// shadow is 10x10
1106				const BRect rightBottom(BRect(rect.right - 9, rect.bottom - 9,
1107					rect.right, rect.bottom));
1108				sBitmapDrawingEngine->DrawBitmap(fCloseBitmap,
1109					fCloseBitmap->Bounds(), rightBottom);
1110			}
1111
1112			// restore drawing mode
1113			sBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
1114
1115			break;
1116		}
1117
1118		case COMPONENT_ZOOM_BUTTON:
1119		{
1120			// BeOS R5 shadow2: active: 210, 158, 0 inactive: 187, 187, 187
1121			rgb_color buttonColorShadow2(buttonColor);
1122			buttonColorShadow2.red = std::max(0, buttonColor.red - 45),
1123			buttonColorShadow2.green = std::max(0, buttonColor.green - 45),
1124			buttonColorShadow2.blue = std::max(0, buttonColor.blue - 45);
1125
1126			// fill the background
1127			sBitmapDrawingEngine->FillRect(rect, buttonColor);
1128
1129			// big rect
1130			BRect bigRect(rect);
1131			bigRect.left += floorf(width * 3.0f / 14.0f);
1132			bigRect.top += floorf(height * 3.0f / 14.0f);
1133
1134			// small rect
1135			BRect smallRect(rect);
1136			smallRect.right -= floorf(width * 5.0f / 14.0f);
1137			smallRect.bottom -= floorf(height * 5.0f / 14.0f);
1138
1139			// draw big rect bevel
1140			_DrawBevelRect(sBitmapDrawingEngine, bigRect, tab->zoomPressed,
1141				buttonColorLight2, buttonColorShadow2);
1142
1143			if (fCStatus != B_OK) {
1144				// If we ran out of memory while initializing bitmaps
1145				// fall back to a linear gradient.
1146
1147				// already drew bigRect bevel, fill with linear gradient
1148				bigRect.InsetBy(1, 1);
1149				_DrawBlendedRect(sBitmapDrawingEngine, bigRect,
1150					tab->zoomPressed, buttonColorLight2, buttonColorLight1,
1151					buttonColor, buttonColorShadow1);
1152
1153				// draw small rect bevel then fill with linear gradient
1154				_DrawBevelRect(sBitmapDrawingEngine, smallRect,
1155					tab->zoomPressed, buttonColorLight2, buttonColorShadow2);
1156				if (!tab->zoomPressed) {
1157					// undraw bottom left and top right corners
1158					sBitmapDrawingEngine->StrokePoint(smallRect.LeftBottom(),
1159						buttonColor);
1160					sBitmapDrawingEngine->StrokePoint(smallRect.RightTop(),
1161						buttonColor);
1162				}
1163				smallRect.InsetBy(1, 1);
1164				_DrawBlendedRect(sBitmapDrawingEngine, smallRect,
1165					tab->zoomPressed, buttonColorLight2, buttonColorLight1,
1166					buttonColor, buttonColorShadow1);
1167
1168				break;
1169			}
1170
1171			// inset past bevel
1172			bigRect.InsetBy(2, 2);
1173
1174			// fill big rect bg
1175			sBitmapDrawingEngine->FillRect(bigRect, buttonColorLight1);
1176
1177			// some elements are covered by the small rect
1178			// so only draw the parts that get shown
1179			if (tab->zoomPressed) {
1180				// draw glint
1181				// Read the source bitmap in forward while writing the
1182				// destination in reverse to rotate the bitmap by 180��.
1183				data = fGlintBitmap->Bits();
1184				size = sizeof(kGlintBits);
1185				for (size_t i = 0; i < sizeof(kGlintBits); i++) {
1186					offset = (size - 1 - i) * 4;
1187					if (kGlintBits[i] == 0) {
1188						// draw glint
1189						data[offset + 0] = buttonColorLight2.blue;
1190						data[offset + 1] = buttonColorLight2.green;
1191						data[offset + 2] = buttonColorLight2.red;
1192					} else {
1193						// draw background color
1194						data[offset + 0] = buttonColorLight1.blue;
1195						data[offset + 1] = buttonColorLight1.green;
1196						data[offset + 2] = buttonColorLight1.red;
1197					}
1198				}
1199				// glint is 3x3
1200				const BRect rightBottom(BRect(bigRect.right - 2,
1201					bigRect.bottom - 2, bigRect.right, bigRect.bottom));
1202				sBitmapDrawingEngine->DrawBitmap(fGlintBitmap,
1203					fGlintBitmap->Bounds(), rightBottom);
1204			} else {
1205				// draw combined inner and outer shadow
1206				data = fBigZoomBitmap->Bits();
1207				size = sizeof(kBigOuterShadowBits);
1208				for (size_t i = 0; i < sizeof(kBigOuterShadowBits); i++) {
1209					offset = i * 4;
1210					if (kBigOuterShadowBits[i] == 0) {
1211						// draw outer shadow
1212						data[offset + 0] = buttonColorShadow1.blue;
1213						data[offset + 1] = buttonColorShadow1.green;
1214						data[offset + 2] = buttonColorShadow1.red;
1215					} else if (kBigInnerShadowBits[i] == 0) {
1216						// draw inner shadow
1217						data[offset + 0] = buttonColor.blue;
1218						data[offset + 1] = buttonColor.green;
1219						data[offset + 2] = buttonColor.red;
1220					} else {
1221						// draw background color
1222						data[offset + 0] = buttonColorLight1.blue;
1223						data[offset + 1] = buttonColorLight1.green;
1224						data[offset + 2] = buttonColorLight1.red;
1225					}
1226				}
1227				// shadow is 7x7
1228				const BRect rightBottom(BRect(bigRect.right - 6,
1229					bigRect.bottom - 6, bigRect.right, bigRect.bottom));
1230				sBitmapDrawingEngine->DrawBitmap(fBigZoomBitmap,
1231					fBigZoomBitmap->Bounds(), rightBottom);
1232			}
1233
1234			sBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
1235
1236			// draw small rect bevel
1237			_DrawBevelRect(sBitmapDrawingEngine, smallRect, tab->zoomPressed,
1238				buttonColorLight2, buttonColorShadow2);
1239
1240			if (!tab->zoomPressed) {
1241				// undraw bottom left and top right corners
1242				sBitmapDrawingEngine->StrokePoint(smallRect.LeftBottom(),
1243					buttonColor);
1244				sBitmapDrawingEngine->StrokePoint(smallRect.RightTop(),
1245					buttonColor);
1246			}
1247
1248			// inset past bevel
1249			smallRect.InsetBy(2, 2);
1250
1251			// fill small rect bg
1252			sBitmapDrawingEngine->FillRect(smallRect, buttonColorLight1);
1253
1254			// treat background color as transparent
1255			sBitmapDrawingEngine->SetDrawingMode(B_OP_OVER);
1256			sBitmapDrawingEngine->SetLowColor(buttonColorLight1);
1257
1258			// draw small bitmap
1259			data = fSmallZoomBitmap->Bits();
1260			size = sizeof(kSmallOuterShadowBits);
1261			if (tab->zoomPressed) {
1262				// draw combined inner and outer shadow
1263				// Read the source bitmap in forward while writing the
1264				// destination in reverse to rotate the bitmap by 180��.
1265				for (size_t i = 0; i < size; i++) {
1266					offset = (size - 1 - i) * 4;
1267					if (kSmallOuterShadowBits[i] == 0) {
1268						// draw outer shadow
1269						data[offset + 0] = buttonColorShadow1.blue;
1270						data[offset + 1] = buttonColorShadow1.green;
1271						data[offset + 2] = buttonColorShadow1.red;
1272					} else if (kSmallInnerShadowBits[i] == 0) {
1273						// draw inner shadow
1274						data[offset + 0] = buttonColor.blue;
1275						data[offset + 1] = buttonColor.green;
1276						data[offset + 2] = buttonColor.red;
1277					} else {
1278						// draw background color
1279						data[offset + 0] = buttonColorLight1.blue;
1280						data[offset + 1] = buttonColorLight1.green;
1281						data[offset + 2] = buttonColorLight1.red;
1282					}
1283				}
1284				// shadow is 5x5
1285				const BRect smallLeftTop(BRect(smallRect.left,
1286					smallRect.top, smallRect.left + 4, smallRect.top + 4));
1287				sBitmapDrawingEngine->DrawBitmap(fSmallZoomBitmap,
1288					fSmallZoomBitmap->Bounds(), smallLeftTop);
1289			} else {
1290				// draw combined inner and outer shadow
1291				for (size_t i = 0; i < size; i++) {
1292					offset = i * 4;
1293					if (kSmallOuterShadowBits[i] == 0) {
1294						// draw outer shadow
1295						data[offset + 0] = buttonColorShadow1.blue;
1296						data[offset + 1] = buttonColorShadow1.green;
1297						data[offset + 2] = buttonColorShadow1.red;
1298					} else if (kSmallInnerShadowBits[i] == 0) {
1299						// draw inner shadow
1300						data[offset + 0] = buttonColor.blue;
1301						data[offset + 1] = buttonColor.green;
1302						data[offset + 2] = buttonColor.red;
1303					} else {
1304						// draw background color
1305						data[offset + 0] = buttonColorLight1.blue;
1306						data[offset + 1] = buttonColorLight1.green;
1307						data[offset + 2] = buttonColorLight1.red;
1308					}
1309				}
1310				// shadow is 5x5
1311				const BRect smallRightBottom(BRect(smallRect.right - 4,
1312					smallRect.bottom - 4, smallRect.right, smallRect.bottom));
1313				sBitmapDrawingEngine->DrawBitmap(fSmallZoomBitmap,
1314					fSmallZoomBitmap->Bounds(), smallRightBottom);
1315			}
1316
1317			// draw glint last (single pixel)
1318			sBitmapDrawingEngine->StrokePoint(tab->zoomPressed
1319					? smallRect.RightBottom() : smallRect.LeftTop(),
1320				buttonColorLight2);
1321
1322			// restore drawing mode
1323			sBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
1324
1325			break;
1326		}
1327
1328		default:
1329			break;
1330	}
1331
1332	UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height,
1333		B_RGB32);
1334	if (bitmap == NULL)
1335		return NULL;
1336
1337	// bitmap ready, put it into the list
1338	decorator_bitmap* entry = new(std::nothrow) decorator_bitmap;
1339	if (entry == NULL) {
1340		delete bitmap;
1341		return NULL;
1342	}
1343
1344	entry->item = item;
1345	entry->down = down;
1346	entry->width = width;
1347	entry->height = height;
1348	entry->bitmap = bitmap;
1349	entry->baseColor = colors[COLOR_BUTTON];
1350	entry->lightColor = colors[COLOR_BUTTON_LIGHT];
1351	entry->next = sBitmapList;
1352	sBitmapList = entry;
1353	return bitmap;
1354}
1355
1356
1357ServerBitmap*
1358BeDecorator::_CreateTemporaryBitmap(BRect bounds) const
1359{
1360	UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(bounds,
1361		B_RGB32, 0);
1362	if (bitmap == NULL)
1363		return NULL;
1364
1365	if (!bitmap->IsValid()) {
1366		delete bitmap;
1367		return NULL;
1368	}
1369
1370	memset(bitmap->Bits(), 0, bitmap->BitsLength());
1371		// background opacity is 0
1372
1373	return bitmap;
1374}
1375
1376
1377void
1378BeDecorator::_GetComponentColors(Component component,
1379	ComponentColors _colors, Decorator::Tab* tab)
1380{
1381	// get the highlight for our component
1382	Region region = REGION_NONE;
1383	switch (component) {
1384		case COMPONENT_TAB:
1385			region = REGION_TAB;
1386			break;
1387		case COMPONENT_CLOSE_BUTTON:
1388			region = REGION_CLOSE_BUTTON;
1389			break;
1390		case COMPONENT_ZOOM_BUTTON:
1391			region = REGION_ZOOM_BUTTON;
1392			break;
1393		case COMPONENT_LEFT_BORDER:
1394			region = REGION_LEFT_BORDER;
1395			break;
1396		case COMPONENT_RIGHT_BORDER:
1397			region = REGION_RIGHT_BORDER;
1398			break;
1399		case COMPONENT_TOP_BORDER:
1400			region = REGION_TOP_BORDER;
1401			break;
1402		case COMPONENT_BOTTOM_BORDER:
1403			region = REGION_BOTTOM_BORDER;
1404			break;
1405		case COMPONENT_RESIZE_CORNER:
1406			region = REGION_RIGHT_BOTTOM_CORNER;
1407			break;
1408	}
1409
1410	return GetComponentColors(component, RegionHighlight(region), _colors, tab);
1411}
1412
1413
1414extern "C" DecorAddOn* (instantiate_decor_addon)(image_id id, const char* name)
1415{
1416	return new (std::nothrow)BeDecorAddOn(id, name);
1417}
1418