1/*
2 * Copyright 2001-2011, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		Stephan A��mus <superstippi@gmx.de>
8 *		Clemens Zeidler <haiku@clemens-zeidler.de>
9 *		Ingo Weinhold <ingo_weinhold@gmx.de>
10 */
11
12
13/*!	Base class for window decorators */
14
15
16#include "Decorator.h"
17
18#include <stdio.h>
19
20#include <Region.h>
21
22#include "DrawingEngine.h"
23
24
25Decorator::Tab::Tab()
26	:
27	zoomRect(),
28	closeRect(),
29	minimizeRect(),
30
31	closePressed(false),
32	zoomPressed(false),
33	minimizePressed(false),
34
35	look(B_TITLED_WINDOW_LOOK),
36	flags(0),
37	isFocused(false),
38	title("")
39{
40
41}
42
43
44/*!	\brief Constructor
45
46	Does general initialization of internal data members and creates a colorset
47	object.
48
49	\param rect Size of client area
50	\param wlook style of window look. See Window.h
51	\param wfeel style of window feel. See Window.h
52	\param wflags various window flags. See Window.h
53*/
54Decorator::Decorator(DesktopSettings& settings, BRect rect)
55	:
56	fDrawingEngine(NULL),
57	fDrawState(),
58
59	fTitleBarRect(),
60	fFrame(rect),
61	fResizeRect(),
62	fBorderRect(),
63
64	fTopTab(NULL),
65
66	fFootprintValid(false)
67{
68	memset(&fRegionHighlights, HIGHLIGHT_NONE, sizeof(fRegionHighlights));
69}
70
71
72/*!
73	\brief Destructor
74
75	Frees the color set and the title string
76*/
77Decorator::~Decorator()
78{
79}
80
81
82Decorator::Tab*
83Decorator::AddTab(DesktopSettings& settings, const char* title,
84	window_look look, uint32 flags, int32 index, BRegion* updateRegion)
85{
86	Decorator::Tab* tab = _AllocateNewTab();
87	if (tab == NULL)
88		return NULL;
89	tab->title = title;
90	tab->look = look;
91	tab->flags = flags;
92
93	bool ok = false;
94	if (index >= 0) {
95		if (fTabList.AddItem(tab, index) == true)
96			ok = true;
97	} else if (fTabList.AddItem(tab) == true)
98		ok = true;
99
100	if (ok == false) {
101		delete tab;
102		return NULL;
103	}
104
105	Decorator::Tab* oldTop = fTopTab;
106	fTopTab = tab;
107	if (_AddTab(settings, index, updateRegion) == false) {
108		fTabList.RemoveItem(tab);
109		delete tab;
110		fTopTab = oldTop;
111		return NULL;
112	}
113
114	_InvalidateFootprint();
115	return tab;
116}
117
118
119bool
120Decorator::RemoveTab(int32 index, BRegion* updateRegion)
121{
122	Decorator::Tab* tab = fTabList.RemoveItemAt(index);
123	if (tab == NULL)
124		return false;
125
126	_RemoveTab(index, updateRegion);
127
128	delete tab;
129	_InvalidateFootprint();
130	return true;
131}
132
133
134bool
135Decorator::MoveTab(int32 from, int32 to, bool isMoving, BRegion* updateRegion)
136{
137	if (_MoveTab(from, to, isMoving, updateRegion) == false)
138		return false;
139	if (fTabList.MoveItem(from, to) == false) {
140		// move the tab back
141		_MoveTab(from, to, isMoving, updateRegion);
142		return false;
143	}
144	return true;
145}
146
147
148int32
149Decorator::TabAt(const BPoint& where) const
150{
151	for (int32 i = 0; i < fTabList.CountItems(); i++) {
152		Decorator::Tab* tab = fTabList.ItemAt(i);
153		if (tab->tabRect.Contains(where))
154			return i;
155	}
156
157	return -1;
158}
159
160
161void
162Decorator::SetTopTab(int32 tab)
163{
164	fTopTab = fTabList.ItemAt(tab);
165}
166
167
168/*!	\brief Assigns a display driver to the decorator
169	\param driver A valid DrawingEngine object
170*/
171void
172Decorator::SetDrawingEngine(DrawingEngine* engine)
173{
174	fDrawingEngine = engine;
175	// lots of subclasses will depend on the driver for text support, so call
176	// _DoLayout() after we have it
177	if (fDrawingEngine)
178		_DoLayout();
179}
180
181
182/*!	\brief Sets the decorator's window flags
183
184	While this call will not update the screen, it will affect how future
185	updates work and immediately affects input handling.
186
187	\param flags New value for the flags
188*/
189void
190Decorator::SetFlags(int32 tab, uint32 flags, BRegion* updateRegion)
191{
192	// we're nice to our subclasses - we make sure B_NOT_{H|V|}_RESIZABLE
193	// are in sync (it's only a semantical simplification, not a necessity)
194	if ((flags & (B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE))
195			== (B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE))
196		flags |= B_NOT_RESIZABLE;
197	if (flags & B_NOT_RESIZABLE)
198		flags |= B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE;
199
200	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
201	if (decoratorTab == NULL)
202		return;
203	_SetFlags(decoratorTab, flags, updateRegion);
204	_InvalidateFootprint();
205		// the border might have changed (smaller/larger tab)
206}
207
208
209/*!	\brief Called whenever the system fonts are changed.
210*/
211void
212Decorator::FontsChanged(DesktopSettings& settings, BRegion* updateRegion)
213{
214	_FontsChanged(settings, updateRegion);
215	_InvalidateFootprint();
216}
217
218
219/*!	\brief Sets the decorator's window look
220	\param look New value for the look
221*/
222void
223Decorator::SetLook(int32 tab, DesktopSettings& settings, window_look look,
224	BRegion* updateRect)
225{
226	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
227	if (decoratorTab == NULL)
228		return;
229	_SetLook(decoratorTab, settings, look, updateRect);
230	_InvalidateFootprint();
231		// the border very likely changed
232}
233
234
235/*!	\brief Returns the decorator's window look
236	\return the decorator's window look
237*/
238window_look
239Decorator::Look(int32 tab) const
240{
241	return TabAt(tab)->look;
242}
243
244
245/*!	\brief Returns the decorator's window flags
246	\return the decorator's window flags
247*/
248uint32
249Decorator::Flags(int32 tab) const
250{
251	return TabAt(tab)->flags;
252}
253
254
255/*!	\brief Returns the decorator's border rectangle
256	\return the decorator's border rectangle
257*/
258BRect
259Decorator::BorderRect() const
260{
261	return fBorderRect;
262}
263
264
265BRect
266Decorator::TitleBarRect() const
267{
268	return fTitleBarRect;
269}
270
271
272/*!	\brief Returns the decorator's tab rectangle
273	\return the decorator's tab rectangle
274*/
275BRect
276Decorator::TabRect(int32 tab) const
277{
278	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
279	if (decoratorTab == NULL)
280		return BRect();
281	return decoratorTab->tabRect;
282}
283
284
285BRect
286Decorator::TabRect(Decorator::Tab* tab) const
287{
288	return tab->tabRect;
289}
290
291
292/*!	\brief Sets the close button's value.
293
294	Note that this does not update the button's look - it just updates the
295	internal button value
296
297	\param is_down Whether the button is down or not
298*/
299void
300Decorator::SetClose(int32 tab, bool pressed)
301{
302	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
303	if (decoratorTab == NULL)
304		return;
305
306	if (pressed != decoratorTab->closePressed) {
307		decoratorTab->closePressed = pressed;
308		DrawClose(tab);
309	}
310}
311
312/*!	\brief Sets the minimize button's value.
313
314	Note that this does not update the button's look - it just updates the
315	internal button value
316
317	\param is_down Whether the button is down or not
318*/
319void
320Decorator::SetMinimize(int32 tab, bool pressed)
321{
322	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
323	if (decoratorTab == NULL)
324		return;
325
326	if (pressed != decoratorTab->minimizePressed) {
327		decoratorTab->minimizePressed = pressed;
328		DrawMinimize(tab);
329	}
330}
331
332/*!	\brief Sets the zoom button's value.
333
334	Note that this does not update the button's look - it just updates the
335	internal button value
336
337	\param is_down Whether the button is down or not
338*/
339void
340Decorator::SetZoom(int32 tab, bool pressed)
341{
342	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
343	if (decoratorTab == NULL)
344		return;
345
346	if (pressed != decoratorTab->zoomPressed) {
347		decoratorTab->zoomPressed = pressed;
348		DrawZoom(tab);
349	}
350}
351
352
353/*!	\brief Updates the value of the decorator title
354	\param string New title value
355*/
356void
357Decorator::SetTitle(int32 tab, const char* string, BRegion* updateRegion)
358{
359	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
360	if (decoratorTab == NULL)
361		return;
362
363	decoratorTab->title.SetTo(string);
364	_SetTitle(decoratorTab, string, updateRegion);
365
366	_InvalidateFootprint();
367		// the border very likely changed
368
369	// TODO: redraw?
370}
371
372
373/*!	\brief Returns the decorator's title
374	\return the decorator's title
375*/
376const char*
377Decorator::Title(int32 tab) const
378{
379	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
380	if (decoratorTab == NULL)
381		return "";
382	return decoratorTab->title;
383}
384
385
386const char*
387Decorator::Title(Decorator::Tab* tab) const
388{
389	return tab->title;
390}
391
392
393bool
394Decorator::SetTabLocation(int32 tab, float location, bool isShifting,
395	BRegion* updateRegion)
396{
397	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
398	if (decoratorTab == NULL)
399		return false;
400	if (_SetTabLocation(decoratorTab, location, isShifting, updateRegion)) {
401		_InvalidateFootprint();
402		return true;
403	}
404	return false;
405}
406
407
408
409/*!	\brief Changes the focus value of the decorator
410
411	While this call will not update the screen, it will affect how future
412	updates work.
413
414	\param active True if active, false if not
415*/
416void
417Decorator::SetFocus(int32 tab, bool active)
418{
419	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
420	if (decoratorTab == NULL)
421		return;
422	decoratorTab->isFocused = active;
423	_SetFocus(decoratorTab);
424	// TODO: maybe it would be cleaner to handle the redraw here.
425}
426
427
428bool
429Decorator::IsFocus(int32 tab) const
430{
431	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
432	if (decoratorTab == NULL)
433		return false;
434	return decoratorTab->isFocused;
435};
436
437
438bool
439Decorator::IsFocus(Decorator::Tab* tab) const
440{
441	return tab->isFocused;
442}
443
444
445void
446Decorator::GetSizeLimits(int32* minWidth, int32* minHeight, int32* maxWidth,
447	int32* maxHeight) const
448{
449}
450
451
452//	#pragma mark - virtual methods
453
454
455/*!	\brief Returns a cached footprint if available otherwise recalculate it
456*/
457const BRegion&
458Decorator::GetFootprint()
459{
460	if (!fFootprintValid) {
461		_GetFootprint(&fFootprint);
462		fFootprintValid = true;
463	}
464	return fFootprint;
465}
466
467
468/*!	\brief Performs hit-testing for the decorator.
469
470	The base class provides a basic implementation, recognizing only button and
471	tab hits. Derived classes must override/enhance it to handle borders and
472	corners correctly.
473
474	\param where The point to be tested.
475	\return Either of the following, depending on what was hit:
476		- \c REGION_NONE: None of the decorator regions.
477		- \c REGION_TAB: The window tab (but none of the buttons embedded).
478		- \c REGION_CLOSE_BUTTON: The close button.
479		- \c REGION_ZOOM_BUTTON: The zoom button.
480		- \c REGION_MINIMIZE_BUTTON: The minimize button.
481		- \c REGION_LEFT_BORDER: The left border.
482		- \c REGION_RIGHT_BORDER: The right border.
483		- \c REGION_TOP_BORDER: The top border.
484		- \c REGION_BOTTOM_BORDER: The bottom border.
485		- \c REGION_LEFT_TOP_CORNER: The left-top corner.
486		- \c REGION_LEFT_BOTTOM_CORNER: The left-bottom corner.
487		- \c REGION_RIGHT_TOP_CORNER: The right-top corner.
488		- \c REGION_RIGHT_BOTTOM_CORNER The right-bottom corner.
489*/
490Decorator::Region
491Decorator::RegionAt(BPoint where, int32& tabIndex) const
492{
493	tabIndex = -1;
494
495	for (int32 i = 0; i < fTabList.CountItems(); i++) {
496		Decorator::Tab* tab = fTabList.ItemAt(i);
497		if (tab->closeRect.Contains(where)) {
498			tabIndex = i;
499			return REGION_CLOSE_BUTTON;
500		}
501		if (tab->zoomRect.Contains(where)) {
502			tabIndex = i;
503			return REGION_ZOOM_BUTTON;
504		}
505		if (tab->tabRect.Contains(where)) {
506			tabIndex = i;
507			return REGION_TAB;
508		}
509	}
510
511	return REGION_NONE;
512}
513
514
515/*!	\brief Moves the decorator frame and all default rectangles
516
517	If a subclass implements this method, be sure to call Decorator::MoveBy
518	to ensure that internal members are also updated. All members of the
519	Decorator class are automatically moved in this method
520
521	\param x X Offset
522	\param y y Offset
523*/
524void
525Decorator::MoveBy(float x, float y)
526{
527	MoveBy(BPoint(x, y));
528}
529
530
531/*!	\brief Moves the decorator frame and all default rectangles
532
533	If a subclass implements this method, be sure to call Decorator::MoveBy
534	to ensure that internal members are also updated. All members of the
535	Decorator class are automatically moved in this method
536
537	\param offset BPoint containing the offsets
538*/
539void
540Decorator::MoveBy(BPoint offset)
541{
542	if (fFootprintValid)
543		fFootprint.OffsetBy(offset.x, offset.y);
544
545	_MoveBy(offset);
546}
547
548
549/*!	\brief Resizes the decorator frame
550
551	This is a required function for subclasses to implement - the default does
552	nothing. Note that window resize flags should be followed and fFrame should
553	be resized accordingly. It would also be a wise idea to ensure that the
554	window's rectangles are not inverted.
555
556	\param x x offset
557	\param y y offset
558*/
559void
560Decorator::ResizeBy(float x, float y, BRegion* dirty)
561{
562	ResizeBy(BPoint(x, y), dirty);
563}
564
565
566void
567Decorator::ResizeBy(BPoint offset, BRegion* dirty)
568{
569	_ResizeBy(offset, dirty);
570	_InvalidateFootprint();
571}
572
573
574/*!	\brief Sets a specific highlight for a decorator region.
575
576	Can be overridden by derived classes, but the base class version must be
577	called, if the highlight shall be applied.
578
579	\param region The decorator region.
580	\param highlight The value identifying the kind of highlight.
581	\param dirty The dirty region to be extended, if the highlight changes. Can
582		be \c NULL.
583	\return \c true, if the highlight could be applied.
584*/
585bool
586Decorator::SetRegionHighlight(Region region, uint8 highlight, BRegion* dirty,
587	int32 tab)
588{
589	int32 index = (int32)region - 1;
590	if (index < 0 || index >= REGION_COUNT - 1)
591		return false;
592
593	if (fRegionHighlights[index] == highlight)
594		return true;
595	fRegionHighlights[index] = highlight;
596
597	if (dirty != NULL)
598		ExtendDirtyRegion(region, *dirty);
599
600	return true;
601}
602
603
604bool
605Decorator::SetSettings(const BMessage& settings, BRegion* updateRegion)
606{
607	if (_SetSettings(settings, updateRegion)) {
608		_InvalidateFootprint();
609		return true;
610	}
611	return false;
612}
613
614
615bool
616Decorator::GetSettings(BMessage* settings) const
617{
618	return false;
619}
620
621
622/*!	\brief Updates the decorator's look in the area \a rect
623
624	The default version updates all areas which intersect the frame and tab.
625
626	\param rect The area to update.
627*/
628void
629Decorator::Draw(BRect rect)
630{
631	_DrawFrame(rect & fFrame);
632	_DrawTabs(rect & fTitleBarRect);
633}
634
635
636//! Forces a complete decorator update
637void
638Decorator::Draw()
639{
640	_DrawFrame(fFrame);
641	_DrawTabs(fTitleBarRect);
642}
643
644
645//! draws the frame
646void
647Decorator::DrawFrame()
648{
649	_DrawFrame(fFrame);
650}
651
652
653//! draws the tab, title, and buttons
654void
655Decorator::DrawTab(int32 tabIndex)
656{
657	Decorator::Tab* tab = fTabList.ItemAt(tabIndex);
658	if (tab == NULL)
659		return;
660
661	_DrawTab(tab, tab->tabRect);
662	_DrawZoom(tab, false, tab->zoomRect);
663	_DrawMinimize(tab, false, tab->minimizeRect);
664	_DrawTitle(tab, tab->tabRect);
665	_DrawClose(tab, false, tab->closeRect);
666}
667
668
669//! Draws the close button
670void
671Decorator::DrawClose(int32 tab)
672{
673	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
674	if (decoratorTab == NULL)
675		return;
676	_DrawClose(decoratorTab, true, decoratorTab->closeRect);
677}
678
679
680//! draws the minimize button
681void
682Decorator::DrawMinimize(int32 tab)
683{
684	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
685	if (decoratorTab == NULL)
686		return;
687	_DrawTab(decoratorTab, decoratorTab->minimizeRect);
688}
689
690
691//! draws the title
692void
693Decorator::DrawTitle(int32 tab)
694{
695	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
696	if (decoratorTab == NULL)
697		return;
698	_DrawTitle(decoratorTab, decoratorTab->tabRect);
699}
700
701
702//! draws the zoom button
703void
704Decorator::DrawZoom(int32 tab)
705{
706	Decorator::Tab* decoratorTab = fTabList.ItemAt(tab);
707	if (decoratorTab == NULL)
708		return;
709	_DrawZoom(decoratorTab, true, decoratorTab->zoomRect);
710}
711
712
713rgb_color
714Decorator::UIColor(color_which which)
715{
716	// TODO: for now - calling ui_color() from within the app_server
717	//	will always return the default colors (as there is no be_app)
718	return ui_color(which);
719}
720
721
722/*!	\brief Extends a dirty region by a decorator region.
723
724	Must be implemented by derived classes.
725
726	\param region The decorator region.
727	\param dirty The dirty region to be extended.
728*/
729void
730Decorator::ExtendDirtyRegion(Region region, BRegion& dirty)
731{
732}
733
734
735//! Function for calculating layout for the decorator
736void
737Decorator::_DoLayout()
738{
739}
740
741
742/*!	\brief Actually draws the frame
743	\param rect Area of the frame to update
744*/
745void
746Decorator::_DrawFrame(BRect rect)
747{
748}
749
750
751
752void
753Decorator::_DrawTabs(BRect rect)
754{
755	Decorator::Tab* focusTab = NULL;
756	for (int32 i = 0; i < fTabList.CountItems(); i++) {
757		Decorator::Tab* tab = fTabList.ItemAt(i);
758		if (tab->isFocused) {
759			focusTab = tab;
760			continue;
761		}
762		_DrawTab(tab, rect);
763	}
764	if (focusTab != NULL)
765		_DrawTab(focusTab, rect);
766}
767
768
769/*!	\brief Actually draws the tab
770
771	This function is called when the tab itself needs drawn. Other items,
772	like the window title or buttons, should not be drawn here.
773
774	\param rect Area of the tab to update
775*/
776void
777Decorator::_DrawTab(Decorator::Tab* tab, BRect rect)
778{
779}
780
781
782/*!	\brief Actually draws the close button
783
784	Unless a subclass has a particularly large button, it is probably
785	unnecessary to check the update rectangle.
786
787	\param rect Area of the button to update
788*/
789void
790Decorator::_DrawClose(Decorator::Tab* tab, bool direct, BRect rect)
791{
792}
793
794
795/*!	\brief Actually draws the title
796
797	The main tasks for this function are to ensure that the decorator draws
798	the title only in its own area and drawing the title itself.
799	Using B_OP_COPY for drawing the title is recommended because of the marked
800	performance hit of the other drawing modes, but it is not a requirement.
801
802	\param rect area of the title to update
803*/
804void
805Decorator::_DrawTitle(Decorator::Tab* tab, BRect rect)
806{
807}
808
809
810/*!	\brief Actually draws the zoom button
811
812	Unless a subclass has a particularly large button, it is probably
813	unnecessary to check the update rectangle.
814
815	\param rect Area of the button to update
816*/
817void
818Decorator::_DrawZoom(Decorator::Tab* tab, bool direct, BRect rect)
819{
820}
821
822/*!
823	\brief Actually draws the minimize button
824
825	Unless a subclass has a particularly large button, it is probably
826	unnecessary to check the update rectangle.
827
828	\param rect Area of the button to update
829*/
830void
831Decorator::_DrawMinimize(Decorator::Tab* tab, bool direct, BRect rect)
832{
833}
834
835
836bool
837Decorator::_SetTabLocation(Decorator::Tab* tab, float location, bool isShifting,
838	BRegion* /*updateRegion*/)
839{
840	return false;
841}
842
843
844//! Hook function called when the decorator changes focus
845void
846Decorator::_SetFocus(Decorator::Tab* tab)
847{
848}
849
850
851void
852Decorator::_FontsChanged(DesktopSettings& settings, BRegion* updateRegion)
853{
854}
855
856
857void
858Decorator::_SetLook(Decorator::Tab* tab, DesktopSettings& settings,
859	window_look look, BRegion* updateRect)
860{
861	tab->look = look;
862}
863
864
865void
866Decorator::_SetFlags(Decorator::Tab* tab, uint32 flags, BRegion* updateRegion)
867{
868	tab->flags = flags;
869}
870
871
872void
873Decorator::_MoveBy(BPoint offset)
874{
875	for (int32 i = 0; i < fTabList.CountItems(); i++) {
876		Decorator::Tab* tab = fTabList.ItemAt(i);
877
878		tab->zoomRect.OffsetBy(offset);
879		tab->closeRect.OffsetBy(offset);
880		tab->minimizeRect.OffsetBy(offset);
881		tab->tabRect.OffsetBy(offset);
882	}
883	fTitleBarRect.OffsetBy(offset);
884	fFrame.OffsetBy(offset);
885	fResizeRect.OffsetBy(offset);
886	fBorderRect.OffsetBy(offset);
887}
888
889
890bool
891Decorator::_SetSettings(const BMessage& settings, BRegion* updateRegion)
892{
893	return false;
894}
895
896
897/*!	\brief Returns the "footprint" of the entire window, including decorator
898
899	This function is required by all subclasses.
900
901	\param region Region to be changed to represent the window's screen
902		footprint
903*/
904void
905Decorator::_GetFootprint(BRegion *region)
906{
907}
908
909
910void
911Decorator::_InvalidateFootprint()
912{
913	fFootprintValid = false;
914}
915