1/*
2 * Copyright 2009-2010, Haiku.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7/*! Decorator looking like Windows 95 */
8
9
10#include "WinDecorator.h"
11
12#include <new>
13#include <stdio.h>
14
15#include <Point.h>
16#include <View.h>
17
18#include "DesktopSettings.h"
19#include "DrawingEngine.h"
20#include "PatternHandler.h"
21#include "RGBColor.h"
22
23
24//#define DEBUG_DECORATOR
25#ifdef DEBUG_DECORATOR
26#	define STRACE(x) printf x
27#else
28#	define STRACE(x) ;
29#endif
30
31
32WinDecorAddOn::WinDecorAddOn(image_id id, const char* name)
33	:
34	DecorAddOn(id, name)
35{
36
37}
38
39
40Decorator*
41WinDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect,
42	window_look look, uint32 flags)
43{
44	return new (std::nothrow)WinDecorator(settings, rect, look, flags);
45}
46
47
48WinDecorator::WinDecorator(DesktopSettings& settings, BRect rect,
49		window_look look, uint32 flags)
50	:
51	Decorator(settings, rect, look, flags)
52{
53	_UpdateFont(settings);
54
55	// common colors to both focus and non focus state
56	frame_highcol = (rgb_color){ 255, 255, 255, 255 };
57	frame_midcol = (rgb_color){ 216, 216, 216, 255 };
58	frame_lowcol = (rgb_color){ 110, 110, 110, 255 };
59	frame_lowercol = (rgb_color){ 0, 0, 0, 255 };
60
61	// state based colors
62	fFocusTabColor = settings.UIColor(B_WINDOW_TAB_COLOR);
63	fFocusTextColor = settings.UIColor(B_WINDOW_TEXT_COLOR);
64	fNonFocusTabColor = settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR);
65	fNonFocusTextColor = settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR);
66
67	// Set appropriate colors based on the current focus value. In this case,
68	// each decorator defaults to not having the focus.
69	_SetFocus();
70
71	// Do initial decorator setup
72	_DoLayout();
73
74	textoffset=5;
75
76	STRACE(("WinDecorator()\n"));
77}
78
79
80WinDecorator::~WinDecorator()
81{
82	STRACE(("~WinDecorator()\n"));
83}
84
85
86// TODO : Add GetSettings
87
88
89void
90WinDecorator::Draw(BRect update)
91{
92	STRACE(("WinDecorator::Draw(): ")); update.PrintToStream();
93
94	fDrawingEngine->SetDrawState(&fDrawState);
95
96	_DrawFrame(update);
97	_DrawTab(update);
98}
99
100
101void
102WinDecorator::Draw()
103{
104	STRACE(("WinDecorator::Draw()\n"));
105	fDrawingEngine->SetDrawState(&fDrawState);
106
107	_DrawFrame(fBorderRect);
108	_DrawTab(fTabRect);
109}
110
111
112// TODO : add GetSizeLimits
113
114
115Decorator::Region
116WinDecorator::RegionAt(BPoint where) const
117{
118	// Let the base class version identify hits of the buttons and the tab.
119	Region region = Decorator::RegionAt(where);
120	if (region != REGION_NONE)
121		return region;
122
123	// check the resize corner
124	if (fLook == B_DOCUMENT_WINDOW_LOOK && fResizeRect.Contains(where))
125		return REGION_RIGHT_BOTTOM_CORNER;
126
127	// hit-test the borders
128	if (!(fFlags & B_NOT_RESIZABLE)
129		&& (fLook == B_TITLED_WINDOW_LOOK
130			|| fLook == B_FLOATING_WINDOW_LOOK
131			|| fLook == B_MODAL_WINDOW_LOOK)
132		&& fBorderRect.Contains(where) && !fFrame.Contains(where)) {
133		return REGION_BOTTOM_BORDER;
134			// TODO: Determine the actual border!
135	}
136
137	return REGION_NONE;
138}
139
140
141void
142WinDecorator::_DoLayout()
143{
144	STRACE(("WinDecorator()::_DoLayout()\n"));
145
146	bool hasTab = false;
147
148	fBorderRect=fFrame;
149	fTabRect=fFrame;
150
151	switch (Look()) {
152		case B_MODAL_WINDOW_LOOK:
153			fBorderRect.InsetBy(-4, -4);
154			break;
155
156		case B_TITLED_WINDOW_LOOK:
157		case B_DOCUMENT_WINDOW_LOOK:
158			hasTab = true;
159			fBorderRect.InsetBy(-4, -4);
160			break;
161		case B_FLOATING_WINDOW_LOOK:
162			hasTab = true;
163			break;
164
165		case B_BORDERED_WINDOW_LOOK:
166			fBorderRect.InsetBy(-1, -1);
167			break;
168
169		default:
170			break;
171	}
172
173	if (hasTab) {
174		fBorderRect.top -= 19;
175
176		fTabRect.top -= 19;
177		fTabRect.bottom=fTabRect.top+19;
178
179		fZoomRect=fTabRect;
180		fZoomRect.top+=3;
181		fZoomRect.right-=3;
182		fZoomRect.bottom-=3;
183		fZoomRect.left=fZoomRect.right-15;
184
185		fCloseRect=fZoomRect;
186		fZoomRect.OffsetBy(0-fZoomRect.Width()-3,0);
187
188		fMinimizeRect=fZoomRect;
189		fMinimizeRect.OffsetBy(0-fZoomRect.Width()-1,0);
190	} else {
191		fTabRect.Set(0.0, 0.0, -1.0, -1.0);
192		fCloseRect.Set(0.0, 0.0, -1.0, -1.0);
193		fZoomRect.Set(0.0, 0.0, -1.0, -1.0);
194		fMinimizeRect.Set(0.0, 0.0, -1.0, -1.0);
195	}
196}
197
198
199void
200WinDecorator::_DrawFrame(BRect rect)
201{
202	if (fLook == B_NO_BORDER_WINDOW_LOOK)
203		return;
204
205	if (fBorderRect == fFrame)
206		return;
207
208	BRect r = fBorderRect;
209
210	fDrawingEngine->SetHighColor(frame_lowercol);
211	fDrawingEngine->StrokeRect(r);
212
213	if (fLook == B_BORDERED_WINDOW_LOOK)
214		return;
215
216	BPoint pt;
217
218	pt=r.RightTop();
219	pt.x--;
220	fDrawingEngine->StrokeLine(r.LeftTop(),pt,frame_midcol);
221	pt=r.LeftBottom();
222	pt.y--;
223	fDrawingEngine->StrokeLine(r.LeftTop(),pt,frame_midcol);
224
225	fDrawingEngine->StrokeLine(r.RightTop(),r.RightBottom(),frame_lowercol);
226	fDrawingEngine->StrokeLine(r.LeftBottom(),r.RightBottom(),frame_lowercol);
227
228	r.InsetBy(1,1);
229	pt=r.RightTop();
230	pt.x--;
231	fDrawingEngine->StrokeLine(r.LeftTop(),pt,frame_highcol);
232	pt=r.LeftBottom();
233	pt.y--;
234	fDrawingEngine->StrokeLine(r.LeftTop(),pt,frame_highcol);
235
236	fDrawingEngine->StrokeLine(r.RightTop(),r.RightBottom(),frame_lowcol);
237	fDrawingEngine->StrokeLine(r.LeftBottom(),r.RightBottom(),frame_lowcol);
238
239	r.InsetBy(1,1);
240	fDrawingEngine->StrokeRect(r,frame_midcol);
241	r.InsetBy(1,1);
242	fDrawingEngine->StrokeRect(r,frame_midcol);
243}
244
245
246void
247WinDecorator::_DrawTab(BRect invalid)
248{
249	// If a window has a tab, this will draw it and any buttons which are
250	// in it.
251	if (!fTabRect.IsValid() || !invalid.Intersects(fTabRect) || fLook==B_NO_BORDER_WINDOW_LOOK)
252		return;
253
254	fDrawingEngine->FillRect(fTabRect,tab_highcol);
255
256	_DrawTitle(fTabRect);
257
258	// Draw the buttons if we're supposed to
259	// TODO : we should still draw the buttons if they are disabled, but grey them out
260	if (!(fFlags & B_NOT_CLOSABLE) && invalid.Intersects(fCloseRect))
261		_DrawClose(fCloseRect);
262	if (!(fFlags & B_NOT_ZOOMABLE) && invalid.Intersects(fZoomRect))
263		_DrawZoom(fZoomRect);
264}
265
266
267void
268WinDecorator::_DrawClose(BRect r)
269{
270	// Just like DrawZoom, but for a close button
271	_DrawBeveledRect(r,GetClose());
272
273	// Draw the X
274
275	BRect rect(r);
276	rect.InsetBy(4,4);
277	rect.right--;
278	rect.top--;
279
280	if (GetClose())
281		rect.OffsetBy(1,1);
282
283	fDrawingEngine->SetHighColor(RGBColor(0,0,0));
284	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.RightBottom());
285	fDrawingEngine->StrokeLine(rect.RightTop(),rect.LeftBottom());
286	rect.OffsetBy(1,0);
287	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.RightBottom());
288	fDrawingEngine->StrokeLine(rect.RightTop(),rect.LeftBottom());
289}
290
291
292void
293WinDecorator::_DrawTitle(BRect r)
294{
295	//fDrawingEngine->SetDrawingMode(B_OP_OVER);
296	fDrawingEngine->SetHighColor(textcol);
297	fDrawingEngine->SetLowColor(IsFocus()?fFocusTabColor:fNonFocusTabColor);
298
299	fTruncatedTitle = Title();
300	fDrawState.Font().TruncateString(&fTruncatedTitle, B_TRUNCATE_END,
301		((fZoomRect.IsValid() ? fZoomRect.left :
302			fCloseRect.IsValid() ? fCloseRect.left : fTabRect.right) - 5)
303		- (fTabRect.left + textoffset));
304	fTruncatedTitleLength = fTruncatedTitle.Length();
305	fDrawingEngine->SetFont(fDrawState.Font());
306
307	fDrawingEngine->DrawString(fTruncatedTitle,fTruncatedTitleLength,
308		BPoint(fTabRect.left+textoffset,fCloseRect.bottom-1));
309
310	//fDrawingEngine->SetDrawingMode(B_OP_COPY);
311}
312
313
314void
315WinDecorator::_DrawZoom(BRect r)
316{
317	_DrawBeveledRect(r,GetZoom());
318
319	// Draw the Zoom box
320
321	BRect rect(r);
322	rect.InsetBy(2,2);
323	rect.InsetBy(1,0);
324	rect.bottom--;
325	rect.right--;
326
327	if (GetZoom())
328		rect.OffsetBy(1,1);
329
330	fDrawingEngine->SetHighColor(RGBColor(0,0,0));
331	fDrawingEngine->StrokeRect(rect);
332	rect.InsetBy(1,1);
333	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.RightTop());
334
335}
336
337
338void
339WinDecorator::_DrawMinimize(BRect r)
340{
341	// Just like DrawZoom, but for a Minimize button
342	_DrawBeveledRect(r,GetMinimize());
343
344	fDrawingEngine->SetHighColor(textcol);
345	BRect rect(r.left+5,r.bottom-4,r.right-5,r.bottom-3);
346	if(GetMinimize())
347		rect.OffsetBy(1,1);
348
349	fDrawingEngine->SetHighColor(RGBColor(0,0,0));
350	fDrawingEngine->StrokeRect(rect);
351}
352
353
354void
355WinDecorator::_SetTitle(const char* string, BRegion* updateRegion)
356{
357	// TODO: we could be much smarter about the update region
358
359	BRect rect = TabRect();
360
361	if (updateRegion == NULL)
362		return;
363
364	BRect updatedRect = TabRect();
365	if (rect.left > updatedRect.left)
366		rect.left = updatedRect.left;
367	if (rect.right < updatedRect.right)
368		rect.right = updatedRect.right;
369
370	updateRegion->Include(rect);
371}
372
373
374void
375WinDecorator::_FontsChanged(DesktopSettings& settings,
376	BRegion* updateRegion)
377{
378	// get previous extent
379	if (updateRegion != NULL)
380		updateRegion->Include(&GetFootprint());
381
382	_UpdateFont(settings);
383	_DoLayout();
384
385	_InvalidateFootprint();
386	if (updateRegion != NULL)
387		updateRegion->Include(&GetFootprint());
388}
389
390
391void
392WinDecorator::_SetLook(DesktopSettings& settings, window_look look,
393	BRegion* updateRegion)
394{
395	// TODO: we could be much smarter about the update region
396
397	// get previous extent
398	if (updateRegion != NULL)
399		updateRegion->Include(&GetFootprint());
400
401	fLook = look;
402
403	_UpdateFont(settings);
404	_DoLayout();
405
406	_InvalidateFootprint();
407	if (updateRegion != NULL)
408		updateRegion->Include(&GetFootprint());
409}
410
411
412void
413WinDecorator::_SetFlags(uint32 flags, BRegion* updateRegion)
414{
415	// TODO: we could be much smarter about the update region
416
417	// get previous extent
418	if (updateRegion != NULL)
419		updateRegion->Include(&GetFootprint());
420
421	_DoLayout();
422
423	_InvalidateFootprint();
424	if (updateRegion != NULL)
425		updateRegion->Include(&GetFootprint());
426}
427
428
429void
430WinDecorator::_SetFocus(void)
431{
432	// SetFocus() performs necessary duties for color swapping and
433	// other things when a window is deactivated or activated.
434
435	if (IsFocus()) {
436//		tab_highcol.SetColor(100,100,255);
437//		tab_lowcol.SetColor(40,0,255);
438		tab_highcol=fFocusTabColor;
439		textcol=fFocusTextColor;
440	} else {
441//		tab_highcol.SetColor(220,220,220);
442//		tab_lowcol.SetColor(128,128,128);
443		tab_highcol=fNonFocusTabColor;
444		textcol=fNonFocusTextColor;
445	}
446}
447
448
449void
450WinDecorator::_MoveBy(BPoint pt)
451{
452	// Move all internal rectangles the appropriate amount
453	fFrame.OffsetBy(pt);
454	fCloseRect.OffsetBy(pt);
455	fTabRect.OffsetBy(pt);
456	fBorderRect.OffsetBy(pt);
457	fZoomRect.OffsetBy(pt);
458	fMinimizeRect.OffsetBy(pt);
459}
460
461
462void
463WinDecorator::_ResizeBy(BPoint offset, BRegion* dirty)
464{
465	// Move all internal rectangles the appropriate amount
466	fFrame.right += offset.x;
467	fFrame.bottom += offset.y;
468
469	fTabRect.right += offset.x;
470	fBorderRect.right += offset.x;
471	fBorderRect.bottom += offset.y;
472	// fZoomRect.OffsetBy(offset.x, 0);
473	// fMinimizeRect.OffsetBy(offset.x, 0);
474	if (dirty) {
475		dirty->Include(fTabRect);
476		dirty->Include(fBorderRect);
477	}
478
479
480	// TODO probably some other layouting stuff here
481	_DoLayout();
482}
483
484
485// TODO : _SetSettings
486
487
488void
489WinDecorator::_GetFootprint(BRegion* region)
490{
491	// This function calculates the decorator's footprint in coordinates
492	// relative to the view. This is most often used to set a Window
493	// object's visible region.
494	if (!region)
495		return;
496
497	region->MakeEmpty();
498
499	if (fLook == B_NO_BORDER_WINDOW_LOOK)
500		return;
501
502	region->Set(fBorderRect);
503	region->Include(fTabRect);
504	region->Exclude(fFrame);
505}
506
507
508void
509WinDecorator::_UpdateFont(DesktopSettings& settings)
510{
511	ServerFont font;
512	if (fLook == B_FLOATING_WINDOW_LOOK)
513		settings.GetDefaultPlainFont(font);
514	else
515		settings.GetDefaultBoldFont(font);
516
517	font.SetFlags(B_FORCE_ANTIALIASING);
518	font.SetSpacing(B_STRING_SPACING);
519	fDrawState.SetFont(font);
520}
521
522
523void
524WinDecorator::_DrawBeveledRect(BRect r, bool down)
525{
526	RGBColor higher;
527	RGBColor high;
528	RGBColor mid;
529	RGBColor low;
530	RGBColor lower;
531
532	if (down) {
533		lower.SetColor(255,255,255);
534		low.SetColor(216,216,216);
535		mid.SetColor(192,192,192);
536		high.SetColor(128,128,128);
537		higher.SetColor(0,0,0);
538	} else {
539		higher.SetColor(255,255,255);
540		high.SetColor(216,216,216);
541		mid.SetColor(192,192,192);
542		low.SetColor(128,128,128);
543		lower.SetColor(0,0,0);
544	}
545
546	BRect rect(r);
547	BPoint pt;
548
549	// Top highlight
550	fDrawingEngine->SetHighColor(higher);
551	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.RightTop());
552
553	// Left highlight
554	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.LeftBottom());
555
556	// Right shading
557	pt=rect.RightTop();
558	pt.y++;
559	fDrawingEngine->StrokeLine(pt,rect.RightBottom(),lower);
560
561	// Bottom shading
562	pt=rect.LeftBottom();
563	pt.x++;
564	fDrawingEngine->StrokeLine(pt,rect.RightBottom(),lower);
565
566	rect.InsetBy(1,1);
567
568	// Top inside highlight
569	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.RightTop());
570
571	// Left inside highlight
572	fDrawingEngine->StrokeLine(rect.LeftTop(),rect.LeftBottom());
573
574	// Right inside shading
575	pt=rect.RightTop();
576	pt.y++;
577	fDrawingEngine->StrokeLine(pt,rect.RightBottom(),lower);
578
579	// Bottom inside shading
580	pt=rect.LeftBottom();
581	pt.x++;
582	fDrawingEngine->StrokeLine(pt,rect.RightBottom(),lower);
583
584	rect.InsetBy(1,1);
585
586	fDrawingEngine->FillRect(rect,mid);
587}
588
589
590// #pragma mark -
591
592
593extern "C" DecorAddOn*
594instantiate_decor_addon(image_id id, const char* name)
595{
596	return new (std::nothrow)WinDecorAddOn(id, name);
597}
598