1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2009, Stephan Aßmus, superstippi@gmx.de.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "HeaderView.h"
9
10#include <stdio.h>
11
12#include <algorithm>
13#include <new>
14
15#include <ControlLook.h>
16#include <LayoutUtils.h>
17#include <Looper.h>
18
19
20// #pragma mark - HeaderRenderer
21
22
23HeaderRenderer::~HeaderRenderer()
24{
25}
26
27
28void
29HeaderRenderer::DrawHeaderBackground(BView* view, BRect frame, BRect updateRect,
30	uint32 flags)
31{
32	BRect bgRect = frame;
33
34	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
35	view->SetHighColor(tint_color(base, B_DARKEN_2_TINT));
36	view->StrokeLine(bgRect.LeftBottom(), bgRect.RightBottom());
37
38	bgRect.bottom--;
39	bgRect.right--;
40
41//	if ((flags & CLICKED) != 0)
42//		base = tint_color(base, B_DARKEN_1_TINT);
43
44	be_control_look->DrawButtonBackground(view, bgRect, updateRect, base, 0,
45		BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER);
46
47	view->StrokeLine(frame.RightTop(), frame.RightBottom());
48}
49
50
51// #pragma mark - DefaultHeaderRenderer
52
53
54DefaultHeaderRenderer::DefaultHeaderRenderer()
55{
56}
57
58
59DefaultHeaderRenderer::~DefaultHeaderRenderer()
60{
61}
62
63
64float
65DefaultHeaderRenderer::HeaderHeight(BView* view, const Header* header)
66{
67	BVariant value;
68	if (!header->GetValue(value))
69		return 0;
70
71	if (value.Type() == B_STRING_TYPE) {
72		font_height fontHeight;
73		view->GetFontHeight(&fontHeight);
74		return ceilf((fontHeight.ascent + fontHeight.descent) * 1.2f) + 2.0f;
75	} else {
76		// TODO: Support more value types.
77		return 0;
78	}
79}
80
81
82float
83DefaultHeaderRenderer::PreferredHeaderWidth(BView* view, const Header* header)
84{
85	BVariant value;
86	if (!header->GetValue(value))
87		return 0;
88
89	if (value.Type() == B_STRING_TYPE) {
90		float width = view->StringWidth(value.ToString());
91		return width + be_control_look->DefaultLabelSpacing() * 2.0f;
92	} else {
93		// TODO: Support more value types.
94		return 0;
95	}
96}
97
98
99void
100DefaultHeaderRenderer::DrawHeader(BView* view, BRect frame, BRect updateRect,
101	const Header* header, uint32 flags)
102{
103	DrawHeaderBackground(view, frame, updateRect, flags);
104
105	BVariant value;
106	if (!header->GetValue(value))
107		return;
108
109	frame.InsetBy(be_control_look->DefaultLabelSpacing(), 0);
110
111	if (value.Type() == B_STRING_TYPE) {
112		be_control_look->DrawLabel(view, value.ToString(), frame, updateRect,
113			view->LowColor(), 0);
114	}
115}
116
117
118// #pragma mark - HeaderListener
119
120
121HeaderListener::~HeaderListener()
122{
123}
124
125
126void
127HeaderListener::HeaderWidthChanged(Header* header)
128{
129}
130
131
132void
133HeaderListener::HeaderWidthRestrictionsChanged(Header* header)
134{
135}
136
137
138void
139HeaderListener::HeaderValueChanged(Header* header)
140{
141}
142
143
144void
145HeaderListener::HeaderRendererChanged(Header* header)
146{
147}
148
149
150// #pragma mark - Header
151
152
153Header::Header(int32 modelIndex)
154	:
155	fWidth(100),
156	fMinWidth(0),
157	fMaxWidth(10000),
158	fPreferredWidth(100),
159	fValue(),
160	fRenderer(NULL),
161	fModelIndex(modelIndex),
162	fResizable(true)
163{
164}
165
166
167Header::Header(float width, float minWidth, float maxWidth,
168	float preferredWidth, int32 modelIndex)
169	:
170	fWidth(width),
171	fMinWidth(minWidth),
172	fMaxWidth(maxWidth),
173	fPreferredWidth(preferredWidth),
174	fValue(),
175	fRenderer(NULL),
176	fModelIndex(modelIndex),
177	fResizable(true)
178{
179}
180
181
182float
183Header::Width() const
184{
185	return fWidth;
186}
187
188
189float
190Header::MinWidth() const
191{
192	return fMinWidth;
193}
194
195
196float
197Header::MaxWidth() const
198{
199	return fMaxWidth;
200}
201
202
203float
204Header::PreferredWidth() const
205{
206	return fPreferredWidth;
207}
208
209
210void
211Header::SetWidth(float width)
212{
213	if (width != fWidth) {
214		fWidth = width;
215		NotifyWidthChanged();
216	}
217}
218
219
220void
221Header::SetMinWidth(float width)
222{
223	if (width != fMinWidth) {
224		fMinWidth = width;
225		NotifyWidthRestrictionsChanged();
226	}
227}
228
229
230void
231Header::SetMaxWidth(float width)
232{
233	if (width != fMaxWidth) {
234		fMaxWidth = width;
235		NotifyWidthRestrictionsChanged();
236	}
237}
238
239
240void
241Header::SetPreferredWidth(float width)
242{
243	if (width != fPreferredWidth) {
244		fPreferredWidth = width;
245		NotifyWidthRestrictionsChanged();
246	}
247}
248
249
250bool
251Header::IsResizable() const
252{
253	return fResizable;
254}
255
256
257void
258Header::SetResizable(bool resizable)
259{
260	if (resizable != fResizable) {
261		fResizable = resizable;
262		NotifyWidthRestrictionsChanged();
263	}
264}
265
266
267bool
268Header::GetValue(BVariant& _value) const
269{
270	_value = fValue;
271	return true;
272}
273
274
275void
276Header::SetValue(const BVariant& value)
277{
278	fValue = value;
279	NotifyValueChanged();
280}
281
282
283int32
284Header::ModelIndex() const
285{
286	return fModelIndex;
287}
288
289
290void
291Header::SetModelIndex(int32 index)
292{
293	fModelIndex = index;
294}
295
296
297HeaderRenderer*
298Header::GetHeaderRenderer() const
299{
300	return fRenderer;
301}
302
303
304void
305Header::SetHeaderRenderer(HeaderRenderer* renderer)
306{
307	if (renderer != fRenderer) {
308		fRenderer = renderer;
309		NotifyRendererChanged();
310	}
311}
312
313
314void
315Header::AddListener(HeaderListener* listener)
316{
317	fListeners.AddItem(listener);
318}
319
320
321void
322Header::RemoveListener(HeaderListener* listener)
323{
324	fListeners.RemoveItem(listener);
325}
326
327
328void
329Header::NotifyWidthChanged()
330{
331	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
332		HeaderListener* listener = fListeners.ItemAt(i);
333		listener->HeaderWidthChanged(this);
334	}
335}
336
337
338void
339Header::NotifyWidthRestrictionsChanged()
340{
341	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
342		HeaderListener* listener = fListeners.ItemAt(i);
343		listener->HeaderWidthRestrictionsChanged(this);
344	}
345}
346
347
348void
349Header::NotifyValueChanged()
350{
351	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
352		HeaderListener* listener = fListeners.ItemAt(i);
353		listener->HeaderValueChanged(this);
354	}
355}
356
357
358void
359Header::NotifyRendererChanged()
360{
361	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
362		HeaderListener* listener = fListeners.ItemAt(i);
363		listener->HeaderRendererChanged(this);
364	}
365}
366
367
368// #pragma mark - HeaderModelListener
369
370
371HeaderModelListener::~HeaderModelListener()
372{
373}
374
375
376void
377HeaderModelListener::HeaderAdded(HeaderModel* model, int32 index)
378{
379}
380
381
382void
383HeaderModelListener::HeaderRemoved(HeaderModel* model, int32 index)
384{
385}
386
387
388void
389HeaderModelListener::HeaderMoved(HeaderModel* model, int32 fromIndex,
390	int32 toIndex)
391{
392}
393
394
395// #pragma mark - HeaderModel
396
397
398HeaderModel::HeaderModel()
399{
400}
401
402
403HeaderModel::~HeaderModel()
404{
405}
406
407
408int32
409HeaderModel::CountHeaders() const
410{
411	return fHeaders.CountItems();
412}
413
414
415Header*
416HeaderModel::HeaderAt(int32 index) const
417{
418	return fHeaders.ItemAt(index);
419}
420
421
422int32
423HeaderModel::IndexOfHeader(Header* header) const
424{
425	return fHeaders.IndexOf(header);
426}
427
428
429bool
430HeaderModel::AddHeader(Header* header)
431{
432	if (!fHeaders.AddItem(header))
433		return false;
434
435	NotifyHeaderAdded(fHeaders.CountItems() - 1);
436
437	return true;
438}
439
440
441Header*
442HeaderModel::RemoveHeader(int32 index)
443{
444	Header* header = fHeaders.RemoveItemAt(index);
445	if (header != NULL)
446		return NULL;
447
448	NotifyHeaderRemoved(index);
449
450	return header;
451}
452
453
454void
455HeaderModel::RemoveHeader(Header* header)
456{
457	RemoveHeader(fHeaders.IndexOf(header));
458}
459
460
461bool
462HeaderModel::MoveHeader(int32 fromIndex, int32 toIndex)
463{
464	int32 headerCount = fHeaders.CountItems();
465	if (fromIndex < 0 || fromIndex >= headerCount
466		|| toIndex < 0 || toIndex >= headerCount) {
467		return false;
468	}
469
470	if (fromIndex == toIndex)
471		return true;
472
473	Header* header = fHeaders.RemoveItemAt(fromIndex);
474	fHeaders.AddItem(header, toIndex);
475		// TODO: Might fail.
476
477	NotifyHeaderMoved(fromIndex, toIndex);
478
479	return true;
480}
481
482
483void
484HeaderModel::AddListener(HeaderModelListener* listener)
485{
486	fListeners.AddItem(listener);
487}
488
489
490void
491HeaderModel::RemoveListener(HeaderModelListener* listener)
492{
493	fListeners.RemoveItem(listener);
494}
495
496
497void
498HeaderModel::NotifyHeaderAdded(int32 index)
499{
500	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
501		HeaderModelListener* listener = fListeners.ItemAt(i);
502		listener->HeaderAdded(this, index);
503	}
504}
505
506
507void
508HeaderModel::NotifyHeaderRemoved(int32 index)
509{
510	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
511		HeaderModelListener* listener = fListeners.ItemAt(i);
512		listener->HeaderRemoved(this, index);
513	}
514}
515
516
517void
518HeaderModel::NotifyHeaderMoved(int32 fromIndex, int32 toIndex)
519{
520	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
521		HeaderModelListener* listener = fListeners.ItemAt(i);
522		listener->HeaderMoved(this, fromIndex, toIndex);
523	}
524}
525
526
527// #pragma mark - HeaderEntry
528
529
530struct HeaderView::HeaderEntry {
531	Header*	header;
532	float	position;
533	float	width;
534
535	HeaderEntry(Header* header)
536		:
537		header(header)
538	{
539	}
540};
541
542
543// #pragma mark - States
544
545
546class HeaderView::State {
547public:
548	State(HeaderView* parent)
549		:
550		fParent(parent)
551	{
552	}
553
554	virtual ~State()
555	{
556	}
557
558	virtual void Entering(State* previousState)
559	{
560	}
561
562	virtual void Leaving(State* nextState)
563	{
564	}
565
566	virtual void MouseDown(BPoint where, uint32 buttons, uint32 modifiers)
567	{
568	}
569
570	virtual void MouseUp(BPoint where, uint32 buttons, uint32 modifiers)
571	{
572	}
573
574	virtual void MouseMoved(BPoint where, uint32 transit,
575		const BMessage* dragMessage)
576	{
577	}
578
579protected:
580	HeaderView*	fParent;
581};
582
583
584class HeaderView::DefaultState : public State {
585public:
586								DefaultState(HeaderView* parent);
587
588	virtual	void				MouseDown(BPoint where, uint32 buttons,
589									uint32 modifiers);
590	virtual	void				MouseMoved(BPoint where, uint32 transit,
591									const BMessage* dragMessage);
592};
593
594
595class HeaderView::ResizeState : public State {
596public:
597	virtual	void				Entering(State* previousState);
598	virtual	void				Leaving(State* nextState);
599
600								ResizeState(HeaderView* parent,
601									int32 headerIndex, BPoint startPoint);
602
603	virtual	void				MouseUp(BPoint where, uint32 buttons,
604									uint32 modifiers);
605	virtual	void				MouseMoved(BPoint where, uint32 transit,
606									const BMessage* dragMessage);
607
608private:
609			Header*				fHeader;
610			float				fStartX;
611			float				fStartWidth;
612};
613
614
615// #pragma mark - DefaultState
616
617
618HeaderView::DefaultState::DefaultState(HeaderView* parent)
619	:
620	State(parent)
621{
622}
623
624
625void
626HeaderView::DefaultState::MouseDown(BPoint where, uint32 buttons,
627	uint32 modifiers)
628{
629	HeaderModel* model = fParent->Model();
630	if (model == NULL)
631		return;
632
633	if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
634		return;
635
636	int32 headerIndex = fParent->HeaderIndexAt(where);
637	if (headerIndex < 0) {
638		int32 headerCount = model->CountHeaders();
639		if (headerCount == 0)
640			return;
641
642		headerIndex = headerCount - 1;
643	}
644
645	// Check whether the mouse is close to the left or the right side of the
646	// header.
647	BRect headerFrame = fParent->HeaderFrame(headerIndex);
648	if (fabs(headerFrame.left - where.x) <= 3) {
649		if (headerIndex == 0)
650			return;
651		headerIndex--;
652	} else if (fabs(headerFrame.right + 1 - where.x) > 3)
653		return;
654
655	// start resizing the header
656	fParent->_SwitchState(new ResizeState(fParent, headerIndex, where));
657}
658
659
660void
661HeaderView::DefaultState::MouseMoved(BPoint where, uint32 transit,
662	const BMessage* dragMessage)
663{
664}
665
666
667// #pragma mark - ResizeState
668
669
670void
671HeaderView::ResizeState::Entering(State* previousState)
672{
673	fParent->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
674}
675
676
677void
678HeaderView::ResizeState::Leaving(State* nextState)
679{
680	fParent->SetEventMask(0, 0);
681}
682
683
684HeaderView::ResizeState::ResizeState(HeaderView* parent, int32 headerIndex,
685	BPoint startPoint)
686	:
687	State(parent),
688	fHeader(parent->Model()->HeaderAt(headerIndex)),
689	fStartX(startPoint.x),
690	fStartWidth(fHeader->Width())
691{
692}
693
694
695void
696HeaderView::ResizeState::MouseUp(BPoint where, uint32 buttons,
697	uint32 modifiers)
698{
699	if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0)
700		fParent->_SwitchState(NULL);
701}
702
703
704void
705HeaderView::ResizeState::MouseMoved(BPoint where, uint32 transit,
706	const BMessage* dragMessage)
707{
708	float width = fStartWidth + where.x - fStartX;
709	width = std::max(width, fHeader->MinWidth());
710	width = std::min(width, fHeader->MaxWidth());
711	fHeader->SetWidth(width);
712}
713
714
715// #pragma mark - HeaderView
716
717
718HeaderView::HeaderView()
719 	:
720 	BView("header view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
721 	fModel(NULL),
722 	fHeaderEntries(10, true),
723 	fLayoutValid(false),
724 	fDefaultState(new DefaultState(this)),
725 	fState(fDefaultState)
726{
727 	HeaderModel* model = new(std::nothrow) HeaderModel;
728 	BReference<HeaderModel> modelReference(model, true);
729 	SetModel(model);
730
731	SetViewColor(B_TRANSPARENT_32_BIT);
732}
733
734
735HeaderView::~HeaderView()
736{
737	SetModel(NULL);
738
739	_SwitchState(NULL);
740	delete fDefaultState;
741}
742
743
744void
745HeaderView::Draw(BRect updateRect)
746{
747	if (fModel == NULL)
748		return;
749
750	_ValidateHeadersLayout();
751
752	DefaultHeaderRenderer defaultRenderer;
753	float bottom = Bounds().Height();
754	float left = 0.0f;
755
756	for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++) {
757		if (Header* header = fModel->HeaderAt(i)) {
758			HeaderRenderer* renderer = header->GetHeaderRenderer();
759			if (renderer == NULL)
760				renderer = &defaultRenderer;
761
762			BRect frame(entry->position, 0, entry->position + entry->width - 1,
763				bottom);
764			renderer->DrawHeader(this, frame, updateRect, header, 0);
765				// TODO: flags!
766
767			left = entry->position + entry->width;
768		}
769	}
770
771	// TODO: We are not using any custom renderer here.
772	defaultRenderer.DrawHeaderBackground(this,
773		BRect(left, 0, Bounds().right + 1, bottom), updateRect, 0);
774}
775
776
777BSize
778HeaderView::MinSize()
779{
780	_ValidateHeadersLayout();
781
782	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
783		BSize(100, fPreferredHeight - 1));
784}
785
786
787BSize
788HeaderView::MaxSize()
789{
790	_ValidateHeadersLayout();
791
792	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
793		BSize(B_SIZE_UNLIMITED, fPreferredHeight - 1));
794}
795
796
797BSize
798HeaderView::PreferredSize()
799{
800	_ValidateHeadersLayout();
801
802	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
803		BSize(fPreferredWidth - 1, fPreferredHeight - 1));
804}
805
806
807HeaderModel*
808HeaderView::Model() const
809{
810	return fModel;
811}
812
813
814void
815HeaderView::MouseDown(BPoint where)
816{
817	// get buttons and modifiers
818	BMessage* message = Looper()->CurrentMessage();
819	int32 buttons;
820	if (message == NULL || message->FindInt32("buttons", &buttons) != B_OK)
821		buttons = 0;
822	int32 modifiers;
823	if (message == NULL || message->FindInt32("modifiers", &modifiers) != B_OK)
824		modifiers = 0;
825
826	fState->MouseDown(where, buttons, modifiers);
827}
828
829
830void
831HeaderView::MouseUp(BPoint where)
832{
833	// get buttons and modifiers
834	BMessage* message = Looper()->CurrentMessage();
835	int32 buttons;
836	if (message == NULL || message->FindInt32("buttons", &buttons) != B_OK)
837		buttons = 0;
838	int32 modifiers;
839	if (message == NULL || message->FindInt32("modifiers", &modifiers) != B_OK)
840		modifiers = 0;
841
842	fState->MouseUp(where, buttons, modifiers);
843}
844
845
846void
847HeaderView::MouseMoved(BPoint where, uint32 transit,
848	const BMessage* dragMessage)
849{
850	fState->MouseMoved(where, transit, dragMessage);
851}
852
853
854status_t
855HeaderView::SetModel(HeaderModel* model)
856{
857	if (model == fModel)
858		return B_OK;
859
860	_SwitchState(NULL);
861
862	if (fModel != NULL) {
863		// remove all headers
864		for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++)
865			entry->header->RemoveListener(this);
866		fHeaderEntries.MakeEmpty();
867
868		fModel->RemoveListener(this);
869		fModel->ReleaseReference();
870	}
871
872	fModel = model;
873
874	if (fModel != NULL) {
875		fModel->AcquireReference();
876		fModel->AddListener(this);
877
878		// create header entries
879		int32 headerCount = fModel->CountHeaders();
880		for (int32 i = 0; i < headerCount; i++) {
881			HeaderEntry* entry = new(std::nothrow) HeaderEntry(
882				fModel->HeaderAt(i));
883			if (entry == NULL || !fHeaderEntries.AddItem(entry)) {
884				delete entry;
885				return B_NO_MEMORY;
886			}
887
888			entry->header->AddListener(this);
889		}
890	}
891
892	_InvalidateHeadersLayout(0);
893	Invalidate();
894
895	return B_OK;
896}
897
898
899BRect
900HeaderView::HeaderFrame(int32 index) const
901{
902	float bottom = Bounds().Height();
903
904	if (HeaderEntry* entry = fHeaderEntries.ItemAt(index)) {
905		return BRect(entry->position, 0, entry->position + entry->width - 1,
906			bottom);
907	}
908
909	return BRect();
910}
911
912
913int32
914HeaderView::HeaderIndexAt(BPoint point) const
915{
916	float x = point.x;
917
918	for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++) {
919		if (x >= entry->position && x < entry->position + entry->width)
920			return i;
921	}
922
923	return -1;
924}
925
926
927void
928HeaderView::AddListener(HeaderViewListener* listener)
929{
930	fListeners.AddItem(listener);
931}
932
933
934void
935HeaderView::RemoveListener(HeaderViewListener* listener)
936{
937	fListeners.RemoveItem(listener);
938}
939
940
941void
942HeaderView::HeaderAdded(HeaderModel* model, int32 index)
943{
944	if (Header* header = fModel->HeaderAt(index)) {
945		HeaderEntry* entry = new(std::nothrow) HeaderEntry(
946			fModel->HeaderAt(index));
947		if (entry == NULL || !fHeaderEntries.AddItem(entry)) {
948			delete entry;
949			return;
950		}
951
952		header->AddListener(this);
953		_InvalidateHeadersLayout(index);
954	}
955}
956
957
958void
959HeaderView::HeaderRemoved(HeaderModel* model, int32 index)
960{
961	if (HeaderEntry* entry = fHeaderEntries.RemoveItemAt(index)) {
962		entry->header->RemoveListener(this);
963		_InvalidateHeadersLayout(index);
964	}
965}
966
967
968void
969HeaderView::HeaderMoved(HeaderModel* model, int32 fromIndex, int32 toIndex)
970{
971	_InvalidateHeadersLayout(std::min(fromIndex, toIndex));
972}
973
974
975void
976HeaderView::HeaderWidthChanged(Header* header)
977{
978	_HeaderPropertiesChanged(header, true, true);
979}
980
981
982void
983HeaderView::HeaderWidthRestrictionsChanged(Header* header)
984{
985	// TODO:...
986}
987
988
989void
990HeaderView::HeaderValueChanged(Header* header)
991{
992	_HeaderPropertiesChanged(header, true, false);
993}
994
995
996void
997HeaderView::HeaderRendererChanged(Header* header)
998{
999	_HeaderPropertiesChanged(header, true, true);
1000}
1001
1002
1003void
1004HeaderView::_HeaderPropertiesChanged(Header* header, bool redrawNeeded,
1005	bool relayoutNeeded)
1006{
1007	if (!redrawNeeded && !relayoutNeeded)
1008		return;
1009
1010	int32 index = fModel->IndexOfHeader(header);
1011
1012	if (relayoutNeeded)
1013		_InvalidateHeadersLayout(index);
1014	else if (redrawNeeded)
1015		_InvalidateHeaders(index, index + 1);
1016}
1017
1018
1019void
1020HeaderView::_InvalidateHeadersLayout(int32 firstIndex)
1021{
1022	if (!fLayoutValid)
1023		return;
1024
1025	fLayoutValid = false;
1026	InvalidateLayout();
1027	Invalidate();
1028}
1029
1030
1031void
1032HeaderView::_InvalidateHeaders(int32 firstIndex, int32 endIndex)
1033{
1034	Invalidate();
1035		// TODO: Be less lazy!
1036}
1037
1038
1039void
1040HeaderView::_ValidateHeadersLayout()
1041{
1042	if (fLayoutValid)
1043		return;
1044
1045	DefaultHeaderRenderer defaultRenderer;
1046
1047	int32 headerCount = fHeaderEntries.CountItems();
1048	float position = 0;
1049	fPreferredWidth = 0;
1050	fPreferredHeight = 0;
1051
1052	for (int32 i = 0; i < headerCount; i++) {
1053		HeaderEntry* entry = fHeaderEntries.ItemAt(i);
1054		entry->position = position;
1055		if (Header* header = fModel->HeaderAt(i)) {
1056			entry->width = header->Width();
1057			fPreferredWidth += header->PreferredWidth();
1058		} else
1059			entry->width = 0;
1060
1061		position = entry->position + entry->width;
1062
1063		if (Header* header = fModel->HeaderAt(i)) {
1064			HeaderRenderer* renderer = header->GetHeaderRenderer();
1065			if (renderer == NULL)
1066				renderer = &defaultRenderer;
1067
1068			float height = renderer->HeaderHeight(this, header);
1069			if (height > fPreferredHeight)
1070				fPreferredHeight = height;
1071		}
1072	}
1073
1074	fLayoutValid = true;
1075}
1076
1077
1078void
1079HeaderView::_SwitchState(State* newState)
1080{
1081	if (newState == NULL)
1082		newState = fDefaultState;
1083
1084	fState->Leaving(newState);
1085	newState->Entering(fState);
1086
1087	if (fState != fDefaultState)
1088		delete fState;
1089
1090	fState = newState;
1091}
1092
1093
1094// #pragma mark - HeaderViewListener
1095
1096
1097HeaderViewListener::~HeaderViewListener()
1098{
1099}
1100