xref: /haiku/src/apps/activitymonitor/ActivityView.cpp (revision 425b1199b0cb2116ac84cd286d29569e62d86774)
1 /*
2  * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ActivityView.h"
8 
9 #include <new>
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 #ifdef __HAIKU__
14 #include <AbstractLayoutItem.h>
15 #endif
16 #include <Application.h>
17 #include <Bitmap.h>
18 #include <Dragger.h>
19 #include <MenuItem.h>
20 #include <MessageRunner.h>
21 #include <PopUpMenu.h>
22 #include <String.h>
23 
24 #include "ActivityMonitor.h"
25 #include "ActivityWindow.h"
26 #include "DataSource.h"
27 #include "SystemInfo.h"
28 #include "SystemInfoHandler.h"
29 
30 
31 struct data_item {
32 	bigtime_t	time;
33 	int64		value;
34 };
35 
36 #ifdef __HAIKU__
37 class ActivityView::HistoryLayoutItem : public BAbstractLayoutItem {
38 public:
39 							HistoryLayoutItem(ActivityView* parent);
40 
41 	virtual	bool			IsVisible();
42 	virtual	void			SetVisible(bool visible);
43 
44 	virtual	BRect			Frame();
45 	virtual	void			SetFrame(BRect frame);
46 
47 	virtual	BView*			View();
48 
49 	virtual	BSize			BasePreferredSize();
50 
51 private:
52 	ActivityView*			fParent;
53 	BRect					fFrame;
54 };
55 
56 class ActivityView::LegendLayoutItem : public BAbstractLayoutItem {
57 public:
58 							LegendLayoutItem(ActivityView* parent);
59 
60 	virtual	bool			IsVisible();
61 	virtual	void			SetVisible(bool visible);
62 
63 	virtual	BRect			Frame();
64 	virtual	void			SetFrame(BRect frame);
65 
66 	virtual	BView*			View();
67 
68 	virtual	BSize			BaseMinSize();
69 	virtual	BSize			BaseMaxSize();
70 	virtual	BSize			BasePreferredSize();
71 	virtual	BAlignment		BaseAlignment();
72 
73 private:
74 	ActivityView*			fParent;
75 	BRect					fFrame;
76 };
77 #endif
78 
79 const bigtime_t kInitialRefreshInterval = 500000LL;
80 
81 const uint32 kMsgRefresh = 'refr';
82 const uint32 kMsgToggleDataSource = 'tgds';
83 const uint32 kMsgToggleLegend = 'tglg';
84 
85 extern const char* kSignature;
86 
87 
88 DataHistory::DataHistory(bigtime_t memorize, bigtime_t interval)
89 	:
90 	fBuffer(10000),
91 	fMinimumValue(0),
92 	fMaximumValue(0),
93 	fRefreshInterval(interval),
94 	fLastIndex(-1)
95 {
96 }
97 
98 
99 DataHistory::~DataHistory()
100 {
101 }
102 
103 
104 void
105 DataHistory::AddValue(bigtime_t time, int64 value)
106 {
107 	if (fBuffer.IsEmpty() || fMaximumValue < value)
108 		fMaximumValue = value;
109 	if (fBuffer.IsEmpty() || fMinimumValue > value)
110 		fMinimumValue = value;
111 
112 	data_item item = {time, value};
113 	fBuffer.AddItem(item);
114 }
115 
116 
117 int64
118 DataHistory::ValueAt(bigtime_t time)
119 {
120 	// TODO: if the refresh rate changes, this computation won't work anymore!
121 	int32 index = (time - Start()) / fRefreshInterval;
122 	data_item* item = fBuffer.ItemAt(index);
123 	if (item != NULL)
124 		return item->value;
125 
126 	return 0;
127 }
128 
129 
130 int64
131 DataHistory::MaximumValue() const
132 {
133 	return fMaximumValue;
134 }
135 
136 
137 int64
138 DataHistory::MinimumValue() const
139 {
140 	return fMinimumValue;
141 }
142 
143 
144 bigtime_t
145 DataHistory::Start() const
146 {
147 	if (fBuffer.CountItems() == 0)
148 		return 0;
149 
150 	return fBuffer.ItemAt(0)->time;
151 }
152 
153 
154 bigtime_t
155 DataHistory::End() const
156 {
157 	if (fBuffer.CountItems() == 0)
158 		return 0;
159 
160 	return fBuffer.ItemAt(fBuffer.CountItems() - 1)->time;
161 }
162 
163 
164 void
165 DataHistory::SetRefreshInterval(bigtime_t interval)
166 {
167 	// TODO: adjust buffer size
168 }
169 
170 
171 //	#pragma mark -
172 
173 
174 #ifdef __HAIKU__
175 ActivityView::HistoryLayoutItem::HistoryLayoutItem(ActivityView* parent)
176 	:
177 	fParent(parent),
178 	fFrame()
179 {
180 }
181 
182 
183 bool
184 ActivityView::HistoryLayoutItem::IsVisible()
185 {
186 	return !fParent->IsHidden(fParent);
187 }
188 
189 
190 void
191 ActivityView::HistoryLayoutItem::SetVisible(bool visible)
192 {
193 	// not allowed
194 }
195 
196 
197 BRect
198 ActivityView::HistoryLayoutItem::Frame()
199 {
200 	return fFrame;
201 }
202 
203 
204 void
205 ActivityView::HistoryLayoutItem::SetFrame(BRect frame)
206 {
207 	fFrame = frame;
208 	fParent->_UpdateFrame();
209 }
210 
211 
212 BView*
213 ActivityView::HistoryLayoutItem::View()
214 {
215 	return fParent;
216 }
217 
218 
219 BSize
220 ActivityView::HistoryLayoutItem::BasePreferredSize()
221 {
222 	BSize size(BaseMaxSize());
223 	return size;
224 }
225 
226 
227 //	#pragma mark -
228 
229 
230 ActivityView::LegendLayoutItem::LegendLayoutItem(ActivityView* parent)
231 	:
232 	fParent(parent),
233 	fFrame()
234 {
235 }
236 
237 
238 bool
239 ActivityView::LegendLayoutItem::IsVisible()
240 {
241 	return !fParent->IsHidden(fParent);
242 }
243 
244 
245 void
246 ActivityView::LegendLayoutItem::SetVisible(bool visible)
247 {
248 	// not allowed
249 }
250 
251 
252 BRect
253 ActivityView::LegendLayoutItem::Frame()
254 {
255 	return fFrame;
256 }
257 
258 
259 void
260 ActivityView::LegendLayoutItem::SetFrame(BRect frame)
261 {
262 	fFrame = frame;
263 	fParent->_UpdateFrame();
264 }
265 
266 
267 BView*
268 ActivityView::LegendLayoutItem::View()
269 {
270 	return fParent;
271 }
272 
273 
274 BSize
275 ActivityView::LegendLayoutItem::BaseMinSize()
276 {
277 	// TODO: Cache the info. Might be too expensive for this call.
278 	BSize size;
279 	size.width = 80;
280 	size.height = fParent->_LegendHeight();
281 
282 	return size;
283 }
284 
285 
286 BSize
287 ActivityView::LegendLayoutItem::BaseMaxSize()
288 {
289 	BSize size(BaseMinSize());
290 	size.width = B_SIZE_UNLIMITED;
291 	return size;
292 }
293 
294 
295 BSize
296 ActivityView::LegendLayoutItem::BasePreferredSize()
297 {
298 	BSize size(BaseMinSize());
299 	return size;
300 }
301 
302 
303 BAlignment
304 ActivityView::LegendLayoutItem::BaseAlignment()
305 {
306 	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
307 }
308 #endif
309 
310 
311 //	#pragma mark -
312 
313 
314 ActivityView::ActivityView(BRect frame, const char* name,
315 		const BMessage* settings, uint32 resizingMode)
316 	: BView(frame, name, resizingMode,
317 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
318 {
319 	_Init(settings);
320 
321 	BRect rect(Bounds());
322 	rect.top = rect.bottom - 7;
323 	rect.left = rect.right - 7;
324 	BDragger* dragger = new BDragger(rect, this,
325 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
326 	AddChild(dragger);
327 }
328 
329 
330 ActivityView::ActivityView(const char* name, const BMessage* settings)
331 #ifdef __HAIKU__
332 	: BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
333 #else
334 	: BView(BRect(0,0,300,200), name, B_FOLLOW_NONE, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
335 #endif
336 {
337 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
338 
339 	_Init(settings);
340 
341 	BRect rect(Bounds());
342 	rect.top = rect.bottom - 7;
343 	rect.left = rect.right - 7;
344 	BDragger* dragger = new BDragger(rect, this,
345 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
346 	AddChild(dragger);
347 }
348 
349 
350 ActivityView::ActivityView(BMessage* archive)
351 	: BView(archive)
352 {
353 	_Init(archive);
354 }
355 
356 
357 ActivityView::~ActivityView()
358 {
359 	delete fOffscreen;
360 	delete fSystemInfoHandler;
361 }
362 
363 
364 void
365 ActivityView::_Init(const BMessage* settings)
366 {
367 	fHistoryBackgroundColor = (rgb_color){255, 255, 240};
368 	fLegendBackgroundColor = LowColor();
369 		// the low color is restored by the BView unarchiving
370 	fOffscreen = NULL;
371 #ifdef __HAIKU__
372 	fHistoryLayoutItem = NULL;
373 	fLegendLayoutItem = NULL;
374 #endif
375 	SetViewColor(B_TRANSPARENT_COLOR);
376 
377 	fRefreshInterval = kInitialRefreshInterval;
378 	fDrawInterval = kInitialRefreshInterval * 2;
379 	fLastRefresh = 0;
380 	fDrawResolution = 1;
381 
382 	fSystemInfoHandler = new SystemInfoHandler;
383 
384 	if (settings == NULL
385 		|| settings->FindBool("show legend", &fShowLegend) != B_OK)
386 		fShowLegend = true;
387 
388 	if (settings == NULL) {
389 		AddDataSource(new UsedMemoryDataSource());
390 		AddDataSource(new CachedMemoryDataSource());
391 		return;
392 	}
393 
394 	ssize_t colorLength;
395 	rgb_color *color;
396 	if (settings->FindData("history background color", B_RGB_COLOR_TYPE,
397 			(const void **)&color, &colorLength) == B_OK
398 		&& colorLength == sizeof(rgb_color))
399 		fHistoryBackgroundColor = *color;
400 
401 	const char* name;
402 	for (int32 i = 0; settings->FindString("source", i, &name) == B_OK; i++) {
403 		AddDataSource(DataSource::FindSource(name), settings);
404 	}
405 }
406 
407 
408 status_t
409 ActivityView::Archive(BMessage* into, bool deep) const
410 {
411 	status_t status;
412 
413 	status = BView::Archive(into, deep);
414 	if (status < B_OK)
415 		return status;
416 
417 	status = into->AddString("add_on", kSignature);
418 	if (status < B_OK)
419 		return status;
420 
421 	status = SaveState(*into);
422 	if (status < B_OK)
423 		return status;
424 
425 	return B_OK;
426 }
427 
428 
429 BArchivable*
430 ActivityView::Instantiate(BMessage* archive)
431 {
432 	if (!validate_instantiation(archive, "ActivityView"))
433 		return NULL;
434 
435 	return new ActivityView(archive);
436 }
437 
438 
439 status_t
440 ActivityView::SaveState(BMessage& state) const
441 {
442 	status_t status = state.AddBool("show legend", fShowLegend);
443 	if (status != B_OK)
444 		return status;
445 
446 	status = state.AddData("history background color", B_RGB_COLOR_TYPE,
447 		&fHistoryBackgroundColor, sizeof(rgb_color));
448 	if (status != B_OK)
449 		return status;
450 
451 	for (int32 i = 0; i < fSources.CountItems(); i++) {
452 		DataSource* source = fSources.ItemAt(i);
453 
454 		if (!source->PerCPU() || source->CPU() == 0)
455 			status = state.AddString("source", source->Name());
456 		if (status != B_OK)
457 			return status;
458 
459 		BString name = source->Name();
460 		name << " color";
461 		rgb_color color = source->Color();
462 		state.AddData(name.String(), B_RGB_COLOR_TYPE, &color,
463 			sizeof(rgb_color));
464 	}
465 	return B_OK;
466 }
467 
468 
469 #ifdef __HAIKU__
470 BLayoutItem*
471 ActivityView::CreateHistoryLayoutItem()
472 {
473 	if (fHistoryLayoutItem == NULL)
474 		fHistoryLayoutItem = new HistoryLayoutItem(this);
475 
476 	return fHistoryLayoutItem;
477 }
478 
479 
480 BLayoutItem*
481 ActivityView::CreateLegendLayoutItem()
482 {
483 	if (fLegendLayoutItem == NULL)
484 		fLegendLayoutItem = new LegendLayoutItem(this);
485 
486 	return fLegendLayoutItem;
487 }
488 #endif
489 
490 
491 DataSource*
492 ActivityView::FindDataSource(const DataSource* search)
493 {
494 	for (int32 i = fSources.CountItems(); i-- > 0;) {
495 		DataSource* source = fSources.ItemAt(i);
496 		if (!strcmp(source->Name(), search->Name()))
497 			return source;
498 	}
499 
500 	return NULL;
501 }
502 
503 
504 status_t
505 ActivityView::AddDataSource(const DataSource* source, const BMessage* state)
506 {
507 	if (source == NULL)
508 		return B_BAD_VALUE;
509 
510 	int32 insert = DataSource::IndexOf(source);
511 	for (int32 i = 0; i < fSources.CountItems() && i < insert; i++) {
512 		DataSource* before = fSources.ItemAt(i);
513 		if (DataSource::IndexOf(before) > insert) {
514 			insert = i;
515 			break;
516 		}
517 	}
518 	if (insert > fSources.CountItems())
519 		insert = fSources.CountItems();
520 
521 	uint32 count = 1;
522 	if (source->PerCPU()) {
523 		SystemInfo info;
524 		count = info.CPUCount();
525 	}
526 
527 	for (uint32 i = 0; i < count; i++) {
528 		DataHistory* values = new(std::nothrow) DataHistory(10 * 60000000LL,
529 			fRefreshInterval);
530 		if (values == NULL)
531 			return B_NO_MEMORY;
532 
533 		if (!fValues.AddItem(values, insert + i)) {
534 			delete values;
535 			return B_NO_MEMORY;
536 		}
537 
538 		DataSource* copy;
539 		if (source->PerCPU())
540 			copy = source->CopyForCPU(i);
541 		else
542 			copy = source->Copy();
543 
544 		BString colorName = source->Name();
545 		colorName << " color";
546 		if (state != NULL) {
547 			const rgb_color* color = NULL;
548 			ssize_t colorLength;
549 			if (state->FindData(colorName.String(), B_RGB_COLOR_TYPE, i,
550 					(const void**)&color, &colorLength) == B_OK
551 				&& colorLength == sizeof(rgb_color))
552 				copy->SetColor(*color);
553 		}
554 
555 		if (!fSources.AddItem(copy, insert + i)) {
556 			fValues.RemoveItem(values);
557 			delete values;
558 			return B_NO_MEMORY;
559 		}
560 	}
561 
562 #ifdef __HAIKU__
563 	InvalidateLayout();
564 #endif
565 	return B_OK;
566 }
567 
568 
569 status_t
570 ActivityView::RemoveDataSource(const DataSource* remove)
571 {
572 	bool removed = false;
573 
574 	while (true) {
575 		DataSource* source = FindDataSource(remove);
576 debug_printf("SEARCH %s, found %p\n", remove->Name(), source);
577 		if (source == NULL) {
578 			if (removed)
579 				break;
580 			return B_ENTRY_NOT_FOUND;
581 		}
582 
583 		int32 index = fSources.IndexOf(source);
584 		if (index < 0)
585 			return B_ENTRY_NOT_FOUND;
586 
587 		fSources.RemoveItemAt(index);
588 		delete source;
589 		DataHistory* values = fValues.RemoveItemAt(index);
590 		delete values;
591 		removed = true;
592 	}
593 
594 #ifdef __HAIKU__
595 	InvalidateLayout();
596 #endif
597 	return B_OK;
598 }
599 
600 
601 void
602 ActivityView::RemoveAllDataSources()
603 {
604 	fSources.MakeEmpty();
605 	fValues.MakeEmpty();
606 }
607 
608 
609 void
610 ActivityView::AttachedToWindow()
611 {
612 	Looper()->AddHandler(fSystemInfoHandler);
613 	fSystemInfoHandler->StartWatching();
614 
615 	BMessage refresh(kMsgRefresh);
616 	fRunner = new BMessageRunner(this, &refresh, fRefreshInterval);
617 
618 	FrameResized(Bounds().Width(), Bounds().Height());
619 }
620 
621 
622 void
623 ActivityView::DetachedFromWindow()
624 {
625 	fSystemInfoHandler->StopWatching();
626 	Looper()->RemoveHandler(fSystemInfoHandler);
627 
628 	delete fRunner;
629 }
630 
631 
632 #ifdef __HAIKU__
633 BSize
634 ActivityView::MinSize()
635 {
636 	BSize size(32, 32);
637 	if (fShowLegend)
638 		size.height = _LegendHeight();
639 
640 	return size;
641 }
642 #endif
643 
644 
645 void
646 ActivityView::FrameResized(float /*width*/, float /*height*/)
647 {
648 	_UpdateOffscreenBitmap();
649 }
650 
651 
652 void
653 ActivityView::_UpdateOffscreenBitmap()
654 {
655 	BRect frame = _HistoryFrame();
656 	if (fOffscreen != NULL && frame == fOffscreen->Bounds())
657 		return;
658 
659 	delete fOffscreen;
660 
661 	// create offscreen bitmap
662 
663 	fOffscreen = new(std::nothrow) BBitmap(frame, B_BITMAP_ACCEPTS_VIEWS,
664 		B_RGB32);
665 	if (fOffscreen == NULL || fOffscreen->InitCheck() != B_OK) {
666 		delete fOffscreen;
667 		fOffscreen = NULL;
668 		return;
669 	}
670 
671 	BView* view = new BView(frame, NULL, B_FOLLOW_NONE, B_SUBPIXEL_PRECISE);
672 	view->SetViewColor(fHistoryBackgroundColor);
673 	view->SetLowColor(view->ViewColor());
674 	fOffscreen->AddChild(view);
675 }
676 
677 
678 BView*
679 ActivityView::_OffscreenView()
680 {
681 	if (fOffscreen == NULL)
682 		return NULL;
683 
684 	return fOffscreen->ChildAt(0);
685 }
686 
687 
688 void
689 ActivityView::MouseDown(BPoint where)
690 {
691 #if 0
692 	int32 buttons = B_PRIMARY_MOUSE_BUTTON;
693 	int32 clicks = 1;
694 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL) {
695 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
696 		Looper()->CurrentMessage()->FindInt32("clicks", &clicks);
697 	}
698 #endif
699 
700 	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
701 	menu->SetFont(be_plain_font);
702 
703 	BMenu* additionalMenu = new BMenu("Additional Items");
704 	additionalMenu->SetFont(be_plain_font);
705 
706 	SystemInfo info;
707 	BMenuItem* item;
708 
709 	for (int32 i = 0; i < DataSource::CountSources(); i++) {
710 		const DataSource* source = DataSource::SourceAt(i);
711 
712 		if (source->MultiCPUOnly() && info.CPUCount() == 1)
713 			continue;
714 
715 		BMessage* message = new BMessage(kMsgToggleDataSource);
716 		message->AddInt32("index", i);
717 
718 		item = new BMenuItem(source->Name(), message);
719 		if (FindDataSource(source))
720 			item->SetMarked(true);
721 
722 		if (source->Primary())
723 			menu->AddItem(item);
724 		else
725 			additionalMenu->AddItem(item);
726 	}
727 
728 	menu->AddItem(new BMenuItem(additionalMenu));
729 	menu->AddSeparatorItem();
730 	menu->AddItem(new BMenuItem(fShowLegend ? "Hide Legend" : "Show Legend",
731 		new BMessage(kMsgToggleLegend)));
732 
733 	menu->SetTargetForItems(this);
734 	additionalMenu->SetTargetForItems(this);
735 
736 	ActivityWindow* window = dynamic_cast<ActivityWindow*>(Window());
737 	if (window != NULL && window->ActivityViewCount() > 1) {
738 		menu->AddSeparatorItem();
739 		BMessage* message = new BMessage(kMsgRemoveView);
740 		message->AddPointer("view", this);
741 		menu->AddItem(item = new BMenuItem("Remove View", message));
742 		item->SetTarget(window);
743 	}
744 
745 	ConvertToScreen(&where);
746 	menu->Go(where, true, false, true);
747 
748 }
749 
750 
751 void
752 ActivityView::MouseMoved(BPoint where, uint32 transit,
753 	const BMessage* dragMessage)
754 {
755 }
756 
757 
758 void
759 ActivityView::MessageReceived(BMessage* message)
760 {
761 	// if a color is dropped, use it as background
762 	if (message->WasDropped()) {
763 		rgb_color* color;
764 		ssize_t size;
765 		if (message->FindData("RGBColor", B_RGB_COLOR_TYPE, 0,
766 				(const void**)&color, &size) == B_OK
767 			&& size == sizeof(rgb_color)) {
768 			BPoint dropPoint = message->DropPoint();
769 			ConvertFromScreen(&dropPoint);
770 
771 			if (_HistoryFrame().Contains(dropPoint)) {
772 				fHistoryBackgroundColor = *color;
773 				Invalidate(_HistoryFrame());
774 			} else {
775 				// check each legend color box
776 				BRect legendFrame = _LegendFrame();
777 				for (int32 i = 0; i < fSources.CountItems(); i++) {
778 					BRect frame = _LegendColorFrameAt(legendFrame, i);
779 					if (frame.Contains(dropPoint)) {
780 						fSources.ItemAt(i)->SetColor(*color);
781 						Invalidate(_HistoryFrame());
782 						Invalidate(frame);
783 						return;
784 					}
785 				}
786 
787 				if (dynamic_cast<ActivityMonitor*>(be_app) == NULL) {
788 					// allow background color change in the replicant only
789 					fLegendBackgroundColor = *color;
790 					SetLowColor(fLegendBackgroundColor);
791 					Invalidate(legendFrame);
792 				}
793 			}
794 			return;
795 		}
796 	}
797 
798 	switch (message->what) {
799 		case B_ABOUT_REQUESTED:
800 			ActivityMonitor::ShowAbout();
801 			break;
802 
803 		case kMsgRefresh:
804 			_Refresh();
805 			break;
806 
807 		case kMsgToggleDataSource:
808 		{
809 			int32 index;
810 			if (message->FindInt32("index", &index) != B_OK)
811 				break;
812 
813 			const DataSource* baseSource = DataSource::SourceAt(index);
814 			if (baseSource == NULL)
815 				break;
816 
817 			DataSource* source = FindDataSource(baseSource);
818 			if (source == NULL)
819 				AddDataSource(baseSource);
820 			else
821 				RemoveDataSource(source);
822 
823 			Invalidate();
824 			break;
825 		}
826 
827 		case kMsgToggleLegend:
828 			fShowLegend = !fShowLegend;
829 			Invalidate();
830 			break;
831 
832 		case B_MOUSE_WHEEL_CHANGED:
833 		{
834 			float deltaY = 0.0f;
835 			if (message->FindFloat("be:wheel_delta_y", &deltaY) != B_OK
836 				|| deltaY == 0.0f)
837 				break;
838 
839 			if (deltaY > 0)
840 				fDrawResolution *= 2;
841 			else
842 				fDrawResolution /= 2;
843 
844 			if (fDrawResolution < 1)
845 				fDrawResolution = 1;
846 			if (fDrawResolution > 128)
847 				fDrawResolution = 128;
848 
849 			Invalidate();
850 			break;
851 		}
852 
853 		default:
854 			BView::MessageReceived(message);
855 			break;
856 	}
857 }
858 
859 
860 void
861 ActivityView::_UpdateFrame()
862 {
863 #ifdef __HAIKU__
864 	if (fLegendLayoutItem == NULL || fHistoryLayoutItem == NULL)
865 		return;
866 
867 	BRect historyFrame = fHistoryLayoutItem->Frame();
868 	BRect legendFrame = fLegendLayoutItem->Frame();
869 #else
870 	BRect historyFrame = Bounds();
871 	BRect legendFrame = Bounds();
872 	historyFrame.bottom -= 2 * Bounds().Height() / 3;
873 	legendFrame.top += Bounds().Height() / 3;
874 #endif
875 	MoveTo(historyFrame.left, historyFrame.top);
876 	ResizeTo(legendFrame.left + legendFrame.Width() - historyFrame.left,
877 		legendFrame.top + legendFrame.Height() - historyFrame.top);
878 }
879 
880 
881 BRect
882 ActivityView::_HistoryFrame() const
883 {
884 	if (!fShowLegend)
885 		return Bounds();
886 
887 	BRect frame = Bounds();
888 	BRect legendFrame = _LegendFrame();
889 
890 	frame.bottom = legendFrame.top - 1;
891 
892 	return frame;
893 }
894 
895 
896 float
897 ActivityView::_LegendHeight() const
898 {
899 	font_height fontHeight;
900 	GetFontHeight(&fontHeight);
901 
902 	int32 rows = (fSources.CountItems() + 1) / 2;
903 	return rows * (4 + ceilf(fontHeight.ascent)
904 		+ ceilf(fontHeight.descent) + ceilf(fontHeight.leading));
905 }
906 
907 
908 BRect
909 ActivityView::_LegendFrame() const
910 {
911 	float height;
912 #ifdef __HAIKU__
913 	if (fLegendLayoutItem != NULL)
914 		height = fLegendLayoutItem->Frame().Height();
915 	else
916 #endif
917 		height = _LegendHeight();
918 
919 	BRect frame = Bounds();
920 	frame.top = frame.bottom - height;
921 
922 	return frame;
923 }
924 
925 
926 BRect
927 ActivityView::_LegendFrameAt(BRect frame, int32 index) const
928 {
929 	int32 column = index & 1;
930 	int32 row = index / 2;
931 	if (column == 0)
932 		frame.right = frame.left + floorf(frame.Width() / 2) - 5;
933 	else
934 		frame.left = frame.right - floorf(frame.Width() / 2) + 5;
935 
936 	int32 rows = (fSources.CountItems() + 1) / 2;
937 	float height = floorf((frame.Height() - 5) / rows);
938 
939 	frame.top = frame.top + 5 + row * height;
940 	frame.bottom = frame.top + height - 1;
941 
942 	return frame;
943 }
944 
945 
946 BRect
947 ActivityView::_LegendColorFrameAt(BRect frame, int32 index) const
948 {
949 	frame = _LegendFrameAt(frame, index);
950 	frame.InsetBy(1, 1);
951 	frame.right = frame.left + frame.Height();
952 
953 	return frame;
954 }
955 
956 
957 float
958 ActivityView::_PositionForValue(DataSource* source, DataHistory* values,
959 	int64 value)
960 {
961 	int64 min = source->Minimum();
962 	int64 max = source->Maximum();
963 	if (source->AdaptiveScale()) {
964 		int64 adaptiveMax = int64(values->MaximumValue() * 1.2);
965 		if (adaptiveMax < max)
966 			max = adaptiveMax;
967 	}
968 
969 	if (value > max)
970 		value = max;
971 	if (value < min)
972 		value = min;
973 
974 	float height = _HistoryFrame().Height();
975 	return height - (value - min) * height / (max - min);
976 }
977 
978 
979 void
980 ActivityView::_DrawHistory()
981 {
982 	_UpdateOffscreenBitmap();
983 
984 	BView* view = this;
985 	if (fOffscreen != NULL) {
986 		fOffscreen->Lock();
987 		view = _OffscreenView();
988 	}
989 
990 	BRect frame = _HistoryFrame();
991 	view->SetLowColor(fHistoryBackgroundColor);
992 	view->FillRect(frame, B_SOLID_LOW);
993 
994 	uint32 step = 2;
995 	uint32 resolution = fDrawResolution;
996 	if (fDrawResolution > 1) {
997 		step = 1;
998 		resolution--;
999 	}
1000 
1001 	uint32 width = frame.IntegerWidth() - 10;
1002 	uint32 steps = width / step;
1003 	bigtime_t timeStep = fRefreshInterval * resolution;
1004 	bigtime_t now = system_time();
1005 
1006 	// Draw scale
1007 	// TODO: add second markers?
1008 
1009 	view->SetPenSize(1);
1010 
1011 	rgb_color scaleColor = view->LowColor();
1012 	uint32 average = (scaleColor.red + scaleColor.green + scaleColor.blue) / 3;
1013 	if (average < 96)
1014 		scaleColor = tint_color(scaleColor, B_LIGHTEN_2_TINT);
1015 	else
1016 		scaleColor = tint_color(scaleColor, B_DARKEN_2_TINT);
1017 
1018 	view->SetHighColor(scaleColor);
1019 	view->StrokeRect(frame);
1020 	view->StrokeLine(BPoint(frame.left, frame.top + frame.Height() / 2),
1021 		BPoint(frame.right, frame.top + frame.Height() / 2));
1022 
1023 	// Draw values
1024 
1025 	view->SetPenSize(2);
1026 
1027 	for (uint32 i = fSources.CountItems(); i-- > 0;) {
1028 		DataSource* source = fSources.ItemAt(i);
1029 		DataHistory* values = fValues.ItemAt(i);
1030 		bigtime_t time = now - steps * timeStep;
1031 			// for now steps pixels per second
1032 
1033 		view->BeginLineArray(steps);
1034 		view->SetHighColor(source->Color());
1035 
1036 		float lastY = FLT_MIN;
1037 		uint32 lastX = 0;
1038 
1039 		for (uint32 x = 0; x < width; x += step, time += timeStep) {
1040 			// TODO: compute starting point instead!
1041 			if (values->Start() > time || values->End() < time)
1042 				continue;
1043 
1044 			int64 value = values->ValueAt(time);
1045 			if (timeStep > fRefreshInterval) {
1046 				// TODO: always start with the same index, so that it always
1047 				// uses the same values for computation (currently it jumps)
1048 				uint32 count = 1;
1049 				for (bigtime_t offset = fRefreshInterval; offset < timeStep;
1050 						offset += fRefreshInterval) {
1051 					// TODO: handle int64 overflow correctly!
1052 					value += values->ValueAt(time + offset);
1053 					count++;
1054 				}
1055 				value /= count;
1056 			}
1057 
1058 			float y = _PositionForValue(source, values, value);
1059 			if (lastY != FLT_MIN) {
1060 				view->AddLine(BPoint(lastX, lastY), BPoint(x, y),
1061 					source->Color());
1062 			}
1063 
1064 			lastX = x;
1065 			lastY = y;
1066 		}
1067 
1068 		view->EndLineArray();
1069 	}
1070 
1071 	// TODO: add marks when an app started or quit
1072 	view->Sync();
1073 	if (fOffscreen != NULL) {
1074 		fOffscreen->Unlock();
1075 		DrawBitmap(fOffscreen);
1076 	}
1077 }
1078 
1079 
1080 void
1081 ActivityView::Draw(BRect /*updateRect*/)
1082 {
1083 	_DrawHistory();
1084 
1085 	if (!fShowLegend)
1086 		return;
1087 
1088 	// draw legend
1089 
1090 	BRect legendFrame = _LegendFrame();
1091 
1092 	SetLowColor(fLegendBackgroundColor);
1093 	FillRect(legendFrame, B_SOLID_LOW);
1094 
1095 	font_height fontHeight;
1096 	GetFontHeight(&fontHeight);
1097 
1098 	for (int32 i = 0; i < fSources.CountItems(); i++) {
1099 		DataSource* source = fSources.ItemAt(i);
1100 		DataHistory* values = fValues.ItemAt(i);
1101 		BRect frame = _LegendFrameAt(legendFrame, i);
1102 
1103 		// draw color box
1104 		BRect colorBox = _LegendColorFrameAt(legendFrame, i);
1105 		SetHighColor(tint_color(source->Color(), B_DARKEN_1_TINT));
1106 		StrokeRect(colorBox);
1107 		SetHighColor(source->Color());
1108 		colorBox.InsetBy(1, 1);
1109 		FillRect(colorBox);
1110 
1111 		// show current value and label
1112 		float y = frame.top + ceilf(fontHeight.ascent);
1113 		int64 value = values->ValueAt(values->End());
1114 		BString text;
1115 		source->Print(text, value);
1116 		float width = StringWidth(text.String());
1117 
1118 		BString label = source->Label();
1119 		TruncateString(&label, B_TRUNCATE_MIDDLE,
1120 			frame.right - colorBox.right - 12 - width);
1121 
1122 		SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
1123 		DrawString(label.String(), BPoint(6 + colorBox.right, y));
1124 		DrawString(text.String(), BPoint(frame.right - width, y));
1125 	}
1126 }
1127 
1128 
1129 void
1130 ActivityView::_Refresh()
1131 {
1132 	SystemInfo info(fSystemInfoHandler);
1133 
1134 	// TODO: run refresh in another thread to decouple it from the UI!
1135 
1136 	for (uint32 i = fSources.CountItems(); i-- > 0;) {
1137 		DataSource* source = fSources.ItemAt(i);
1138 		DataHistory* values = fValues.ItemAt(i);
1139 
1140 		int64 value = source->NextValue(info);
1141 		values->AddValue(info.Time(), value);
1142 	}
1143 
1144 	bigtime_t now = info.Time();
1145 	if (fLastRefresh + fDrawInterval <= now) {
1146 		Invalidate();
1147 		fLastRefresh = now;
1148 	}
1149 }
1150 
1151