xref: /haiku/src/apps/debuganalyzer/gui/HeaderView.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
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 
18 
19 // #pragma mark - HeaderRenderer
20 
21 
22 HeaderRenderer::~HeaderRenderer()
23 {
24 }
25 
26 
27 void
28 HeaderRenderer::DrawHeaderBackground(BView* view, BRect frame, BRect updateRect,
29 	uint32 flags)
30 {
31 	BRect bgRect = frame;
32 
33 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
34 	view->SetHighColor(tint_color(base, B_DARKEN_2_TINT));
35 	view->StrokeLine(bgRect.LeftBottom(), bgRect.RightBottom());
36 
37 	bgRect.bottom--;
38 	bgRect.right--;
39 
40 //	if ((flags & CLICKED) != 0)
41 //		base = tint_color(base, B_DARKEN_1_TINT);
42 
43 	be_control_look->DrawButtonBackground(view, bgRect, updateRect, base, 0,
44 		BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER);
45 
46 	view->StrokeLine(frame.RightTop(), frame.RightBottom());
47 }
48 
49 
50 // #pragma mark - DefaultHeaderRenderer
51 
52 
53 DefaultHeaderRenderer::DefaultHeaderRenderer()
54 {
55 }
56 
57 
58 DefaultHeaderRenderer::~DefaultHeaderRenderer()
59 {
60 }
61 
62 
63 float
64 DefaultHeaderRenderer::HeaderHeight(BView* view, const Header* header)
65 {
66 	BVariant value;
67 	if (!header->GetValue(value))
68 		return 0;
69 
70 	if (value.Type() == B_STRING_TYPE) {
71 		font_height fontHeight;
72 		view->GetFontHeight(&fontHeight);
73 		return ceilf((fontHeight.ascent + fontHeight.descent) * 1.2f) + 2.0f;
74 	} else {
75 		// TODO: Support more value types.
76 		return 0;
77 	}
78 }
79 
80 
81 float
82 DefaultHeaderRenderer::PreferredHeaderWidth(BView* view, const Header* header)
83 {
84 	BVariant value;
85 	if (!header->GetValue(value))
86 		return 0;
87 
88 	if (value.Type() == B_STRING_TYPE) {
89 		float width = view->StringWidth(value.ToString());
90 		return width + be_control_look->DefaultLabelSpacing() * 2.0f;
91 	} else {
92 		// TODO: Support more value types.
93 		return 0;
94 	}
95 }
96 
97 
98 void
99 DefaultHeaderRenderer::DrawHeader(BView* view, BRect frame, BRect updateRect,
100 	const Header* header, uint32 flags)
101 {
102 	DrawHeaderBackground(view, frame, updateRect, flags);
103 
104 	BVariant value;
105 	if (!header->GetValue(value))
106 		return;
107 
108 	frame.InsetBy(be_control_look->DefaultLabelSpacing(), 0);
109 
110 	if (value.Type() == B_STRING_TYPE) {
111 		be_control_look->DrawLabel(view, value.ToString(), frame, updateRect,
112 			view->LowColor(), 0);
113 	}
114 }
115 
116 
117 // #pragma mark - HeaderListener
118 
119 
120 HeaderListener::~HeaderListener()
121 {
122 }
123 
124 
125 void
126 HeaderListener::HeaderWidthChanged(Header* header)
127 {
128 }
129 
130 
131 void
132 HeaderListener::HeaderWidthRestrictionsChanged(Header* header)
133 {
134 }
135 
136 
137 void
138 HeaderListener::HeaderValueChanged(Header* header)
139 {
140 }
141 
142 
143 void
144 HeaderListener::HeaderRendererChanged(Header* header)
145 {
146 }
147 
148 
149 // #pragma mark - Header
150 
151 
152 Header::Header(int32 modelIndex)
153 	:
154 	fWidth(100),
155 	fMinWidth(0),
156 	fMaxWidth(10000),
157 	fPreferredWidth(100),
158 	fValue(),
159 	fRenderer(NULL),
160 	fModelIndex(modelIndex),
161 	fResizable(true)
162 {
163 }
164 
165 
166 Header::Header(float width, float minWidth, float maxWidth,
167 	float preferredWidth, int32 modelIndex)
168 	:
169 	fWidth(width),
170 	fMinWidth(minWidth),
171 	fMaxWidth(maxWidth),
172 	fPreferredWidth(preferredWidth),
173 	fValue(),
174 	fRenderer(NULL),
175 	fModelIndex(modelIndex),
176 	fResizable(true)
177 {
178 }
179 
180 
181 float
182 Header::Width() const
183 {
184 	return fWidth;
185 }
186 
187 
188 float
189 Header::MinWidth() const
190 {
191 	return fMinWidth;
192 }
193 
194 
195 float
196 Header::MaxWidth() const
197 {
198 	return fMaxWidth;
199 }
200 
201 
202 float
203 Header::PreferredWidth() const
204 {
205 	return fPreferredWidth;
206 }
207 
208 
209 void
210 Header::SetWidth(float width)
211 {
212 	if (width != fWidth) {
213 		fWidth = width;
214 		NotifyWidthChanged();
215 	}
216 }
217 
218 
219 void
220 Header::SetMinWidth(float width)
221 {
222 	if (width != fMinWidth) {
223 		fMinWidth = width;
224 		NotifyWidthRestrictionsChanged();
225 	}
226 }
227 
228 
229 void
230 Header::SetMaxWidth(float width)
231 {
232 	if (width != fMaxWidth) {
233 		fMaxWidth = width;
234 		NotifyWidthRestrictionsChanged();
235 	}
236 }
237 
238 
239 void
240 Header::SetPreferredWidth(float width)
241 {
242 	if (width != fPreferredWidth) {
243 		fPreferredWidth = width;
244 		NotifyWidthRestrictionsChanged();
245 	}
246 }
247 
248 
249 bool
250 Header::IsResizable() const
251 {
252 	return fResizable;
253 }
254 
255 
256 void
257 Header::SetResizable(bool resizable)
258 {
259 	if (resizable != fResizable) {
260 		fResizable = resizable;
261 		NotifyWidthRestrictionsChanged();
262 	}
263 }
264 
265 
266 bool
267 Header::GetValue(BVariant& _value) const
268 {
269 	_value = fValue;
270 	return true;
271 }
272 
273 
274 void
275 Header::SetValue(const BVariant& value)
276 {
277 	fValue = value;
278 	NotifyValueChanged();
279 }
280 
281 
282 int32
283 Header::ModelIndex() const
284 {
285 	return fModelIndex;
286 }
287 
288 
289 void
290 Header::SetModelIndex(int32 index)
291 {
292 	fModelIndex = index;
293 }
294 
295 
296 HeaderRenderer*
297 Header::GetHeaderRenderer() const
298 {
299 	return fRenderer;
300 }
301 
302 
303 void
304 Header::SetHeaderRenderer(HeaderRenderer* renderer)
305 {
306 	if (renderer != fRenderer) {
307 		fRenderer = renderer;
308 		NotifyRendererChanged();
309 	}
310 }
311 
312 
313 void
314 Header::AddListener(HeaderListener* listener)
315 {
316 	fListeners.AddItem(listener);
317 }
318 
319 
320 void
321 Header::RemoveListener(HeaderListener* listener)
322 {
323 	fListeners.RemoveItem(listener);
324 }
325 
326 
327 void
328 Header::NotifyWidthChanged()
329 {
330 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
331 		HeaderListener* listener = fListeners.ItemAt(i);
332 		listener->HeaderWidthChanged(this);
333 	}
334 }
335 
336 
337 void
338 Header::NotifyWidthRestrictionsChanged()
339 {
340 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
341 		HeaderListener* listener = fListeners.ItemAt(i);
342 		listener->HeaderWidthRestrictionsChanged(this);
343 	}
344 }
345 
346 
347 void
348 Header::NotifyValueChanged()
349 {
350 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
351 		HeaderListener* listener = fListeners.ItemAt(i);
352 		listener->HeaderValueChanged(this);
353 	}
354 }
355 
356 
357 void
358 Header::NotifyRendererChanged()
359 {
360 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
361 		HeaderListener* listener = fListeners.ItemAt(i);
362 		listener->HeaderRendererChanged(this);
363 	}
364 }
365 
366 
367 // #pragma mark - HeaderModelListener
368 
369 
370 HeaderModelListener::~HeaderModelListener()
371 {
372 }
373 
374 
375 void
376 HeaderModelListener::HeaderAdded(HeaderModel* model, int32 index)
377 {
378 }
379 
380 
381 void
382 HeaderModelListener::HeaderRemoved(HeaderModel* model, int32 index)
383 {
384 }
385 
386 
387 void
388 HeaderModelListener::HeaderMoved(HeaderModel* model, int32 fromIndex,
389 	int32 toIndex)
390 {
391 }
392 
393 
394 // #pragma mark - HeaderModel
395 
396 
397 HeaderModel::HeaderModel()
398 {
399 }
400 
401 
402 HeaderModel::~HeaderModel()
403 {
404 }
405 
406 
407 int32
408 HeaderModel::CountHeaders() const
409 {
410 	return fHeaders.CountItems();
411 }
412 
413 
414 Header*
415 HeaderModel::HeaderAt(int32 index) const
416 {
417 	return fHeaders.ItemAt(index);
418 }
419 
420 
421 int32
422 HeaderModel::IndexOfHeader(Header* header) const
423 {
424 	return fHeaders.IndexOf(header);
425 }
426 
427 
428 bool
429 HeaderModel::AddHeader(Header* header)
430 {
431 	if (!fHeaders.AddItem(header))
432 		return false;
433 
434 	NotifyHeaderAdded(fHeaders.CountItems() - 1);
435 
436 	return true;
437 }
438 
439 
440 Header*
441 HeaderModel::RemoveHeader(int32 index)
442 {
443 	Header* header = fHeaders.RemoveItemAt(index);
444 	if (header != NULL)
445 		return NULL;
446 
447 	NotifyHeaderRemoved(index);
448 
449 	return header;
450 }
451 
452 
453 void
454 HeaderModel::RemoveHeader(Header* header)
455 {
456 	RemoveHeader(fHeaders.IndexOf(header));
457 }
458 
459 
460 bool
461 HeaderModel::MoveHeader(int32 fromIndex, int32 toIndex)
462 {
463 	int32 headerCount = fHeaders.CountItems();
464 	if (fromIndex < 0 || fromIndex >= headerCount
465 		|| toIndex < 0 || toIndex >= headerCount) {
466 		return false;
467 	}
468 
469 	if (fromIndex == toIndex)
470 		return true;
471 
472 	Header* header = fHeaders.RemoveItemAt(fromIndex);
473 	fHeaders.AddItem(header, toIndex);
474 		// TODO: Might fail.
475 
476 	NotifyHeaderMoved(fromIndex, toIndex);
477 
478 	return true;
479 }
480 
481 
482 void
483 HeaderModel::AddListener(HeaderModelListener* listener)
484 {
485 	fListeners.AddItem(listener);
486 }
487 
488 
489 void
490 HeaderModel::RemoveListener(HeaderModelListener* listener)
491 {
492 	fListeners.RemoveItem(listener);
493 }
494 
495 
496 void
497 HeaderModel::NotifyHeaderAdded(int32 index)
498 {
499 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
500 		HeaderModelListener* listener = fListeners.ItemAt(i);
501 		listener->HeaderAdded(this, index);
502 	}
503 }
504 
505 
506 void
507 HeaderModel::NotifyHeaderRemoved(int32 index)
508 {
509 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
510 		HeaderModelListener* listener = fListeners.ItemAt(i);
511 		listener->HeaderRemoved(this, index);
512 	}
513 }
514 
515 
516 void
517 HeaderModel::NotifyHeaderMoved(int32 fromIndex, int32 toIndex)
518 {
519 	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) {
520 		HeaderModelListener* listener = fListeners.ItemAt(i);
521 		listener->HeaderMoved(this, fromIndex, toIndex);
522 	}
523 }
524 
525 
526 // #pragma mark - HeaderEntry
527 
528 
529 struct HeaderView::HeaderEntry {
530 	Header*	header;
531 	float	position;
532 	float	width;
533 
534 	HeaderEntry(Header* header)
535 		:
536 		header(header)
537 	{
538 	}
539 };
540 
541 
542 // #pragma mark - HeaderEntry
543 
544 
545 class HeaderView::DragState {
546 public:
547 	virtual ~DragState()
548 	{
549 	}
550 
551 	virtual void MouseDown(BPoint where) = 0;
552 	virtual	void DragTo(BPoint where) = 0;
553 	virtual void MouseUp();
554 
555 };
556 
557 
558 // #pragma mark - HeaderView
559 
560 
561 HeaderView::HeaderView()
562  	:
563  	BView("header view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
564  	fModel(NULL),
565  	fHeaderEntries(10, true),
566  	fLayoutValid(false)
567 {
568  	HeaderModel* model = new(std::nothrow) HeaderModel;
569  	Reference<HeaderModel> modelReference(model, true);
570  	SetModel(model);
571 }
572 
573 
574 HeaderView::~HeaderView()
575 {
576 	SetModel(NULL);
577 }
578 
579 
580 void
581 HeaderView::Draw(BRect updateRect)
582 {
583 	if (fModel == NULL)
584 		return;
585 
586 	_ValidateHeadersLayout();
587 
588 	DefaultHeaderRenderer defaultRenderer;
589 	float bottom = Bounds().Height();
590 	float left = 0.0f;
591 
592 	for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++) {
593 		if (Header* header = fModel->HeaderAt(i)) {
594 			HeaderRenderer* renderer = header->GetHeaderRenderer();
595 			if (renderer == NULL)
596 				renderer = &defaultRenderer;
597 
598 			BRect frame(entry->position, 0, entry->position + entry->width - 1,
599 				bottom);
600 			renderer->DrawHeader(this, frame, updateRect, header, 0);
601 				// TODO: flags!
602 
603 			left = entry->position + entry->width;
604 		}
605 	}
606 
607 	// TODO: We are not using any custom renderer here.
608 	defaultRenderer.DrawHeaderBackground(this,
609 		BRect(left, 0, Bounds().right + 1, bottom), updateRect, 0);
610 }
611 
612 
613 BSize
614 HeaderView::MinSize()
615 {
616 	_ValidateHeadersLayout();
617 
618 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
619 		BSize(100, fPreferredHeight - 1));
620 }
621 
622 
623 BSize
624 HeaderView::MaxSize()
625 {
626 	_ValidateHeadersLayout();
627 
628 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
629 		BSize(B_SIZE_UNLIMITED, fPreferredHeight - 1));
630 }
631 
632 
633 BSize
634 HeaderView::PreferredSize()
635 {
636 	_ValidateHeadersLayout();
637 
638 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
639 		BSize(fPreferredWidth - 1, fPreferredHeight - 1));
640 }
641 
642 
643 HeaderModel*
644 HeaderView::Model() const
645 {
646 	return fModel;
647 }
648 
649 
650 void
651 HeaderView::MouseDown(BPoint where)
652 {
653 }
654 
655 
656 void
657 HeaderView::MouseUp(BPoint where)
658 {
659 }
660 
661 
662 void
663 HeaderView::MouseMoved(BPoint where, uint32 transit,
664 	const BMessage* dragMessage)
665 {
666 }
667 
668 
669 status_t
670 HeaderView::SetModel(HeaderModel* model)
671 {
672 	if (model == fModel)
673 		return B_OK;
674 
675 	if (fModel != NULL) {
676 		// remove all headers
677 		for (int32 i = 0; HeaderEntry* entry = fHeaderEntries.ItemAt(i); i++)
678 			entry->header->RemoveListener(this);
679 		fHeaderEntries.MakeEmpty();
680 
681 		fModel->RemoveListener(this);
682 		fModel->ReleaseReference();
683 	}
684 
685 	fModel = model;
686 
687 	if (fModel != NULL) {
688 		fModel->AcquireReference();
689 		fModel->AddListener(this);
690 
691 		// create header entries
692 		int32 headerCount = fModel->CountHeaders();
693 		for (int32 i = 0; i < headerCount; i++) {
694 			HeaderEntry* entry = new(std::nothrow) HeaderEntry(
695 				fModel->HeaderAt(i));
696 			if (entry == NULL || !fHeaderEntries.AddItem(entry)) {
697 				delete entry;
698 				return B_NO_MEMORY;
699 			}
700 
701 			entry->header->AddListener(this);
702 		}
703 	}
704 
705 	_InvalidateHeadersLayout(0);
706 	Invalidate();
707 
708 	return B_OK;
709 }
710 
711 
712 void
713 HeaderView::AddListener(HeaderViewListener* listener)
714 {
715 	fListeners.AddItem(listener);
716 }
717 
718 
719 void
720 HeaderView::RemoveListener(HeaderViewListener* listener)
721 {
722 	fListeners.RemoveItem(listener);
723 }
724 
725 
726 void
727 HeaderView::HeaderAdded(HeaderModel* model, int32 index)
728 {
729 	if (Header* header = fModel->HeaderAt(index)) {
730 		HeaderEntry* entry = new(std::nothrow) HeaderEntry(
731 			fModel->HeaderAt(index));
732 printf("HeaderView::HeaderAdded(%p, %ld): header: %p, entry: %p\n", model, index, entry->header, entry);
733 		if (entry == NULL || !fHeaderEntries.AddItem(entry)) {
734 			delete entry;
735 			return;
736 		}
737 
738 		header->AddListener(this);
739 		_InvalidateHeadersLayout(index);
740 	}
741 }
742 
743 
744 void
745 HeaderView::HeaderRemoved(HeaderModel* model, int32 index)
746 {
747 	if (HeaderEntry* entry = fHeaderEntries.RemoveItemAt(index)) {
748 		entry->header->RemoveListener(this);
749 		_InvalidateHeadersLayout(index);
750 	}
751 }
752 
753 
754 void
755 HeaderView::HeaderMoved(HeaderModel* model, int32 fromIndex, int32 toIndex)
756 {
757 	_InvalidateHeadersLayout(std::min(fromIndex, toIndex));
758 }
759 
760 
761 void
762 HeaderView::HeaderWidthChanged(Header* header)
763 {
764 	_HeaderPropertiesChanged(header, true, true);
765 }
766 
767 
768 void
769 HeaderView::HeaderWidthRestrictionsChanged(Header* header)
770 {
771 	// TODO:...
772 }
773 
774 
775 void
776 HeaderView::HeaderValueChanged(Header* header)
777 {
778 	_HeaderPropertiesChanged(header, true, false);
779 }
780 
781 
782 void
783 HeaderView::HeaderRendererChanged(Header* header)
784 {
785 	_HeaderPropertiesChanged(header, true, true);
786 }
787 
788 
789 void
790 HeaderView::_HeaderPropertiesChanged(Header* header, bool redrawNeeded,
791 	bool relayoutNeeded)
792 {
793 	if (!redrawNeeded && !relayoutNeeded)
794 		return;
795 
796 	int32 index = fModel->IndexOfHeader(header);
797 
798 	if (relayoutNeeded)
799 		_InvalidateHeadersLayout(index);
800 	else if (redrawNeeded)
801 		_InvalidateHeaders(index, index + 1);
802 }
803 
804 
805 void
806 HeaderView::_InvalidateHeadersLayout(int32 firstIndex)
807 {
808 	if (!fLayoutValid)
809 		return;
810 
811 	fLayoutValid = false;
812 	InvalidateLayout();
813 	Invalidate();
814 }
815 
816 
817 void
818 HeaderView::_InvalidateHeaders(int32 firstIndex, int32 endIndex)
819 {
820 	Invalidate();
821 		// TODO: Be less lazy!
822 }
823 
824 
825 void
826 HeaderView::_ValidateHeadersLayout()
827 {
828 	if (fLayoutValid)
829 		return;
830 
831 	DefaultHeaderRenderer defaultRenderer;
832 
833 	int32 headerCount = fHeaderEntries.CountItems();
834 	float position = 0;
835 	fPreferredWidth = 0;
836 	fPreferredHeight = 0;
837 
838 	for (int32 i = 0; i < headerCount; i++) {
839 		HeaderEntry* entry = fHeaderEntries.ItemAt(i);
840 		entry->position = position;
841 		if (Header* header = fModel->HeaderAt(i)) {
842 			entry->width = header->Width();
843 			fPreferredWidth += header->PreferredWidth();
844 		} else
845 			entry->width = 0;
846 
847 		position = entry->position + entry->width;
848 
849 		if (Header* header = fModel->HeaderAt(i)) {
850 			HeaderRenderer* renderer = header->GetHeaderRenderer();
851 			if (renderer == NULL)
852 				renderer = &defaultRenderer;
853 
854 			float height = renderer->HeaderHeight(this, header);
855 			if (height > fPreferredHeight)
856 				fPreferredHeight = height;
857 		}
858 	}
859 
860 	fLayoutValid = true;
861 }
862 
863 
864 // #pragma mark - HeaderViewListener
865 
866 
867 HeaderViewListener::~HeaderViewListener()
868 {
869 }
870