xref: /haiku/src/add-ons/decorators/BeDecorator/BeDecorator.cpp (revision 8e71287b6f36b5687084d2671e642572478d1c92)
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 
51 static const float kBorderResizeLength = 22.0;
52 static const float kResizeKnobSize = 18.0;
53 
54 
55 static const unsigned char f = 0xff; // way to write 0xff shorter
56 
57 static 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 
70 static 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 
83 static 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 
93 static 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 
103 static 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 
111 static 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 
119 static 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 
BeDecorAddOn(image_id id,const char * name)129 BeDecorAddOn::BeDecorAddOn(image_id id, const char* name)
130 	:
131 	DecorAddOn(id, name)
132 {
133 }
134 
135 
136 Decorator*
_AllocateDecorator(DesktopSettings & settings,BRect rect,Desktop * desktop)137 BeDecorAddOn::_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
BeDecorator(DesktopSettings & settings,BRect rect,Desktop * desktop)149 BeDecorator::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 
~BeDecorator()173 BeDecorator::~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 */
204 void
GetComponentColors(Component component,uint8 highlight,ComponentColors _colors,Decorator::Tab * _tab)205 BeDecorator::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 
331 void
_DrawFrame(BRect invalid)332 BeDecorator::_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 */
585 void
_DrawTab(Decorator::Tab * tab,BRect invalid)586 BeDecorator::_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 */
662 void
_DrawTitle(Decorator::Tab * _tab,BRect r)663 BeDecorator::_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 */
718 void
_DrawClose(Decorator::Tab * _tab,bool direct,BRect rect)719 BeDecorator::_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 */
747 void
_DrawZoom(Decorator::Tab * _tab,bool direct,BRect rect)748 BeDecorator::_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 
769 void
_DrawMinimize(Decorator::Tab * tab,bool direct,BRect rect)770 BeDecorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect)
771 {
772 	// This decorator doesn't have this button
773 }
774 
775 
776 void
_GetButtonSizeAndOffset(const BRect & tabRect,float * _offset,float * _size,float * _inset) const777 BeDecorator::_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 */
800 void
_DrawBevelRect(DrawingEngine * engine,const BRect rect,bool down,rgb_color light,rgb_color shadow)801 BeDecorator::_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 */
841 void
_DrawBlendedRect(DrawingEngine * engine,const BRect rect,bool down,rgb_color colorA,rgb_color colorB,rgb_color colorC,rgb_color colorD)842 BeDecorator::_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 
866 void
_DrawButtonBitmap(ServerBitmap * bitmap,bool direct,BRect rect)867 BeDecorator::_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 
882 ServerBitmap*
_GetBitmapForButton(Decorator::Tab * tab,Component item,bool down,int32 width,int32 height)883 BeDecorator::_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 				for (size_t i = 0; i < sizeof(kBigOuterShadowBits); i++) {
1208 					offset = i * 4;
1209 					if (kBigOuterShadowBits[i] == 0) {
1210 						// draw outer shadow
1211 						data[offset + 0] = buttonColorShadow1.blue;
1212 						data[offset + 1] = buttonColorShadow1.green;
1213 						data[offset + 2] = buttonColorShadow1.red;
1214 					} else if (kBigInnerShadowBits[i] == 0) {
1215 						// draw inner shadow
1216 						data[offset + 0] = buttonColor.blue;
1217 						data[offset + 1] = buttonColor.green;
1218 						data[offset + 2] = buttonColor.red;
1219 					} else {
1220 						// draw background color
1221 						data[offset + 0] = buttonColorLight1.blue;
1222 						data[offset + 1] = buttonColorLight1.green;
1223 						data[offset + 2] = buttonColorLight1.red;
1224 					}
1225 				}
1226 				// shadow is 7x7
1227 				const BRect rightBottom(BRect(bigRect.right - 6,
1228 					bigRect.bottom - 6, bigRect.right, bigRect.bottom));
1229 				sBitmapDrawingEngine->DrawBitmap(fBigZoomBitmap,
1230 					fBigZoomBitmap->Bounds(), rightBottom);
1231 			}
1232 
1233 			sBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
1234 
1235 			// draw small rect bevel
1236 			_DrawBevelRect(sBitmapDrawingEngine, smallRect, tab->zoomPressed,
1237 				buttonColorLight2, buttonColorShadow2);
1238 
1239 			if (!tab->zoomPressed) {
1240 				// undraw bottom left and top right corners
1241 				sBitmapDrawingEngine->StrokePoint(smallRect.LeftBottom(),
1242 					buttonColor);
1243 				sBitmapDrawingEngine->StrokePoint(smallRect.RightTop(),
1244 					buttonColor);
1245 			}
1246 
1247 			// inset past bevel
1248 			smallRect.InsetBy(2, 2);
1249 
1250 			// fill small rect bg
1251 			sBitmapDrawingEngine->FillRect(smallRect, buttonColorLight1);
1252 
1253 			// treat background color as transparent
1254 			sBitmapDrawingEngine->SetDrawingMode(B_OP_OVER);
1255 			sBitmapDrawingEngine->SetLowColor(buttonColorLight1);
1256 
1257 			// draw small bitmap
1258 			data = fSmallZoomBitmap->Bits();
1259 			size = sizeof(kSmallOuterShadowBits);
1260 			if (tab->zoomPressed) {
1261 				// draw combined inner and outer shadow
1262 				// Read the source bitmap in forward while writing the
1263 				// destination in reverse to rotate the bitmap by 180°.
1264 				for (size_t i = 0; i < size; i++) {
1265 					offset = (size - 1 - i) * 4;
1266 					if (kSmallOuterShadowBits[i] == 0) {
1267 						// draw outer shadow
1268 						data[offset + 0] = buttonColorShadow1.blue;
1269 						data[offset + 1] = buttonColorShadow1.green;
1270 						data[offset + 2] = buttonColorShadow1.red;
1271 					} else if (kSmallInnerShadowBits[i] == 0) {
1272 						// draw inner shadow
1273 						data[offset + 0] = buttonColor.blue;
1274 						data[offset + 1] = buttonColor.green;
1275 						data[offset + 2] = buttonColor.red;
1276 					} else {
1277 						// draw background color
1278 						data[offset + 0] = buttonColorLight1.blue;
1279 						data[offset + 1] = buttonColorLight1.green;
1280 						data[offset + 2] = buttonColorLight1.red;
1281 					}
1282 				}
1283 				// shadow is 5x5
1284 				const BRect smallLeftTop(BRect(smallRect.left,
1285 					smallRect.top, smallRect.left + 4, smallRect.top + 4));
1286 				sBitmapDrawingEngine->DrawBitmap(fSmallZoomBitmap,
1287 					fSmallZoomBitmap->Bounds(), smallLeftTop);
1288 			} else {
1289 				// draw combined inner and outer shadow
1290 				for (size_t i = 0; i < size; i++) {
1291 					offset = i * 4;
1292 					if (kSmallOuterShadowBits[i] == 0) {
1293 						// draw outer shadow
1294 						data[offset + 0] = buttonColorShadow1.blue;
1295 						data[offset + 1] = buttonColorShadow1.green;
1296 						data[offset + 2] = buttonColorShadow1.red;
1297 					} else if (kSmallInnerShadowBits[i] == 0) {
1298 						// draw inner shadow
1299 						data[offset + 0] = buttonColor.blue;
1300 						data[offset + 1] = buttonColor.green;
1301 						data[offset + 2] = buttonColor.red;
1302 					} else {
1303 						// draw background color
1304 						data[offset + 0] = buttonColorLight1.blue;
1305 						data[offset + 1] = buttonColorLight1.green;
1306 						data[offset + 2] = buttonColorLight1.red;
1307 					}
1308 				}
1309 				// shadow is 5x5
1310 				const BRect smallRightBottom(BRect(smallRect.right - 4,
1311 					smallRect.bottom - 4, smallRect.right, smallRect.bottom));
1312 				sBitmapDrawingEngine->DrawBitmap(fSmallZoomBitmap,
1313 					fSmallZoomBitmap->Bounds(), smallRightBottom);
1314 			}
1315 
1316 			// draw glint last (single pixel)
1317 			sBitmapDrawingEngine->StrokePoint(tab->zoomPressed
1318 					? smallRect.RightBottom() : smallRect.LeftTop(),
1319 				buttonColorLight2);
1320 
1321 			// restore drawing mode
1322 			sBitmapDrawingEngine->SetDrawingMode(B_OP_COPY);
1323 
1324 			break;
1325 		}
1326 
1327 		default:
1328 			break;
1329 	}
1330 
1331 	UtilityBitmap* bitmap = sBitmapDrawingEngine->ExportToBitmap(width, height,
1332 		B_RGB32);
1333 	if (bitmap == NULL)
1334 		return NULL;
1335 
1336 	// bitmap ready, put it into the list
1337 	decorator_bitmap* entry = new(std::nothrow) decorator_bitmap;
1338 	if (entry == NULL) {
1339 		delete bitmap;
1340 		return NULL;
1341 	}
1342 
1343 	entry->item = item;
1344 	entry->down = down;
1345 	entry->width = width;
1346 	entry->height = height;
1347 	entry->bitmap = bitmap;
1348 	entry->baseColor = colors[COLOR_BUTTON];
1349 	entry->lightColor = colors[COLOR_BUTTON_LIGHT];
1350 	entry->next = sBitmapList;
1351 	sBitmapList = entry;
1352 	return bitmap;
1353 }
1354 
1355 
1356 ServerBitmap*
_CreateTemporaryBitmap(BRect bounds) const1357 BeDecorator::_CreateTemporaryBitmap(BRect bounds) const
1358 {
1359 	UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(bounds,
1360 		B_RGB32, 0);
1361 	if (bitmap == NULL)
1362 		return NULL;
1363 
1364 	if (!bitmap->IsValid()) {
1365 		delete bitmap;
1366 		return NULL;
1367 	}
1368 
1369 	memset(bitmap->Bits(), 0, bitmap->BitsLength());
1370 		// background opacity is 0
1371 
1372 	return bitmap;
1373 }
1374 
1375 
1376 void
_GetComponentColors(Component component,ComponentColors _colors,Decorator::Tab * tab)1377 BeDecorator::_GetComponentColors(Component component,
1378 	ComponentColors _colors, Decorator::Tab* tab)
1379 {
1380 	// get the highlight for our component
1381 	Region region = REGION_NONE;
1382 	switch (component) {
1383 		case COMPONENT_TAB:
1384 			region = REGION_TAB;
1385 			break;
1386 		case COMPONENT_CLOSE_BUTTON:
1387 			region = REGION_CLOSE_BUTTON;
1388 			break;
1389 		case COMPONENT_ZOOM_BUTTON:
1390 			region = REGION_ZOOM_BUTTON;
1391 			break;
1392 		case COMPONENT_LEFT_BORDER:
1393 			region = REGION_LEFT_BORDER;
1394 			break;
1395 		case COMPONENT_RIGHT_BORDER:
1396 			region = REGION_RIGHT_BORDER;
1397 			break;
1398 		case COMPONENT_TOP_BORDER:
1399 			region = REGION_TOP_BORDER;
1400 			break;
1401 		case COMPONENT_BOTTOM_BORDER:
1402 			region = REGION_BOTTOM_BORDER;
1403 			break;
1404 		case COMPONENT_RESIZE_CORNER:
1405 			region = REGION_RIGHT_BOTTOM_CORNER;
1406 			break;
1407 	}
1408 
1409 	return GetComponentColors(component, RegionHighlight(region), _colors, tab);
1410 }
1411 
1412 
1413 extern "C" DecorAddOn* (instantiate_decor_addon)(image_id id, const char* name)
1414 {
1415 	return new (std::nothrow)BeDecorAddOn(id, name);
1416 }
1417