xref: /haiku/src/apps/activitymonitor/ActivityView.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
1 /*
2  * Copyright 2008-2009, Axel Dörfler, axeld@pinc-software.de.
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 #include <vector>
13 
14 #ifdef __HAIKU__
15 #	include <AbstractLayoutItem.h>
16 #	include <ControlLook.h>
17 #endif
18 #include <Application.h>
19 #include <Autolock.h>
20 #include <Bitmap.h>
21 #include <Dragger.h>
22 #include <fs_attr.h>
23 #include <MenuItem.h>
24 #include <MessageRunner.h>
25 #include <PopUpMenu.h>
26 #include <Shape.h>
27 #include <StorageKit.h>
28 #include <String.h>
29 
30 #include "ActivityMonitor.h"
31 #include "ActivityWindow.h"
32 #include "SettingsWindow.h"
33 #include "SystemInfo.h"
34 #include "SystemInfoHandler.h"
35 
36 
37 template<typename ObjectType>
38 class ListAddDeleter {
39 public:
40 	ListAddDeleter(BObjectList<ObjectType>& list, ObjectType* object,
41 			int32 spot)
42 		:
43 		fList(list),
44 		fObject(object)
45 	{
46 		if (fObject != NULL && !fList.AddItem(fObject, spot)) {
47 			delete fObject;
48 			fObject = NULL;
49 		}
50 	}
51 
52 	~ListAddDeleter()
53 	{
54 		if (fObject != NULL) {
55 			fList.RemoveItem(fObject);
56 			delete fObject;
57 		}
58 	}
59 
60 	bool Failed() const
61 	{
62 		return fObject == NULL;
63 	}
64 
65 	void Detach()
66 	{
67 		fObject = NULL;
68 	}
69 
70 private:
71 	BObjectList<ObjectType>&	fList;
72 	ObjectType*					fObject;
73 };
74 
75 
76 /*!	This class manages the scale of a history with a dynamic scale.
77 	Every history value will be input via Update(), and the minimum/maximum
78 	is computed from that.
79 */
80 class Scale {
81 public:
82 								Scale(scale_type type);
83 
84 			int64				MinimumValue() const { return fMinimumValue; }
85 			int64				MaximumValue() const { return fMaximumValue; }
86 
87 			void				Update(int64 value);
88 
89 private:
90 			scale_type			fType;
91 			int64				fMinimumValue;
92 			int64				fMaximumValue;
93 			bool				fInitialized;
94 };
95 
96 /*!	Stores the interpolated on screen view values. This is done so that the
97 	interpolation is fixed, and does not change when being scrolled.
98 
99 	We could also just do this by making sure we always ask for the same
100 	interval only, but this way we also save the interpolation.
101 */
102 class ViewHistory {
103 public:
104 								ViewHistory();
105 
106 			int64				ValueAt(int32 x);
107 
108 			int32				Start() const
109 									{ return fValues.Size()
110 										- fValues.CountItems(); }
111 
112 			void				Update(DataHistory* history, int32 width,
113 									int32 resolution, bigtime_t toTime,
114 									bigtime_t step, bigtime_t refresh);
115 
116 private:
117 			CircularBuffer<int64> fValues;
118 			int32				fResolution;
119 			bigtime_t			fRefresh;
120 			bigtime_t			fLastTime;
121 };
122 
123 struct data_item {
124 	bigtime_t	time;
125 	int64		value;
126 };
127 
128 #ifdef __HAIKU__
129 class ActivityView::HistoryLayoutItem : public BAbstractLayoutItem {
130 public:
131 							HistoryLayoutItem(ActivityView* parent);
132 
133 	virtual	bool			IsVisible();
134 	virtual	void			SetVisible(bool visible);
135 
136 	virtual	BRect			Frame();
137 	virtual	void			SetFrame(BRect frame);
138 
139 	virtual	BView*			View();
140 
141 	virtual	BSize			BasePreferredSize();
142 
143 private:
144 	ActivityView*			fParent;
145 	BRect					fFrame;
146 };
147 
148 class ActivityView::LegendLayoutItem : public BAbstractLayoutItem {
149 public:
150 							LegendLayoutItem(ActivityView* parent);
151 
152 	virtual	bool			IsVisible();
153 	virtual	void			SetVisible(bool visible);
154 
155 	virtual	BRect			Frame();
156 	virtual	void			SetFrame(BRect frame);
157 
158 	virtual	BView*			View();
159 
160 	virtual	BSize			BaseMinSize();
161 	virtual	BSize			BaseMaxSize();
162 	virtual	BSize			BasePreferredSize();
163 	virtual	BAlignment		BaseAlignment();
164 
165 private:
166 	ActivityView*			fParent;
167 	BRect					fFrame;
168 };
169 #endif
170 
171 const bigtime_t kInitialRefreshInterval = 250000LL;
172 
173 const uint32 kMsgToggleDataSource = 'tgds';
174 const uint32 kMsgToggleLegend = 'tglg';
175 const uint32 kMsgUpdateResolution = 'ures';
176 
177 extern const char* kSignature;
178 
179 const char* kDesktopAttrName = "be:bgndimginfo";
180 
181 Scale::Scale(scale_type type)
182 	:
183 	fType(type),
184 	fMinimumValue(0),
185 	fMaximumValue(0),
186 	fInitialized(false)
187 {
188 }
189 
190 
191 void
192 Scale::Update(int64 value)
193 {
194 	if (!fInitialized || fMinimumValue > value)
195 		fMinimumValue = value;
196 	if (!fInitialized || fMaximumValue < value)
197 		fMaximumValue = value;
198 
199 	fInitialized = true;
200 }
201 
202 
203 //	#pragma mark -
204 
205 
206 ViewHistory::ViewHistory()
207 	:
208 	fValues(1),
209 	fResolution(-1),
210 	fRefresh(-1),
211 	fLastTime(0)
212 {
213 }
214 
215 
216 int64
217 ViewHistory::ValueAt(int32 x)
218 {
219 	int64* value = fValues.ItemAt(x - Start());
220 	if (value != NULL)
221 		return *value;
222 
223 	return 0;
224 }
225 
226 
227 void
228 ViewHistory::Update(DataHistory* history, int32 width, int32 resolution,
229 	bigtime_t toTime, bigtime_t step, bigtime_t refresh)
230 {
231 	if (width > 16384) {
232 		// ignore this - it seems the view hasn't been layouted yet
233 		return;
234 	}
235 
236 	// Check if we need to invalidate the existing values
237 	if ((int32)fValues.Size() != width
238 		|| fResolution != resolution
239 		|| fRefresh != refresh) {
240 		fValues.SetSize(width);
241 		fResolution = resolution;
242 		fRefresh = refresh;
243 		fLastTime = 0;
244 	}
245 
246 	// Compute how many new values we need to retrieve
247 	if (fLastTime < history->Start())
248 		fLastTime = history->Start();
249 	if (fLastTime > history->End())
250 		return;
251 
252 	int32 updateWidth = int32((toTime - fLastTime) / step);
253 	if (updateWidth < 1)
254 		return;
255 
256 	if (updateWidth > (int32)fValues.Size()) {
257 		updateWidth = fValues.Size();
258 		fLastTime = toTime - updateWidth * step;
259 	}
260 
261 	for (int32 i = 0; i < updateWidth; i++) {
262 		int64 value = history->ValueAt(fLastTime += step);
263 
264 		if (step > refresh) {
265 			uint32 count = 1;
266 			for (bigtime_t offset = refresh; offset < step; offset += refresh) {
267 				// TODO: handle int64 overflow correctly!
268 				value += history->ValueAt(fLastTime + offset);
269 				count++;
270 			}
271 			value /= count;
272 		}
273 
274 		fValues.AddItem(value);
275 	}
276 }
277 
278 
279 //	#pragma mark -
280 
281 
282 DataHistory::DataHistory(bigtime_t memorize, bigtime_t interval)
283 	:
284 	fBuffer(10000),
285 	fMinimumValue(0),
286 	fMaximumValue(0),
287 	fRefreshInterval(interval),
288 	fLastIndex(-1),
289 	fScale(NULL)
290 {
291 }
292 
293 
294 DataHistory::~DataHistory()
295 {
296 }
297 
298 
299 void
300 DataHistory::AddValue(bigtime_t time, int64 value)
301 {
302 	if (fBuffer.IsEmpty() || fMaximumValue < value)
303 		fMaximumValue = value;
304 	if (fBuffer.IsEmpty() || fMinimumValue > value)
305 		fMinimumValue = value;
306 	if (fScale != NULL)
307 		fScale->Update(value);
308 
309 	data_item item = {time, value};
310 	fBuffer.AddItem(item);
311 }
312 
313 
314 int64
315 DataHistory::ValueAt(bigtime_t time)
316 {
317 	int32 left = 0;
318 	int32 right = fBuffer.CountItems() - 1;
319 	data_item* item = NULL;
320 
321 	while (left <= right) {
322 		int32 index = (left + right) / 2;
323 		item = fBuffer.ItemAt(index);
324 
325 		if (item->time > time) {
326 			// search in left part
327 			right = index - 1;
328 		} else {
329 			data_item* nextItem = fBuffer.ItemAt(index + 1);
330 			if (nextItem == NULL)
331 				return item->value;
332 			if (nextItem->time > time) {
333 				// found item
334 				int64 value = item->value;
335 				value += int64(double(nextItem->value - value)
336 					/ (nextItem->time - item->time) * (time - item->time));
337 				return value;
338 			}
339 
340 			// search in right part
341 			left = index + 1;
342 		}
343 	}
344 
345 	return 0;
346 }
347 
348 
349 int64
350 DataHistory::MaximumValue() const
351 {
352 	if (fScale != NULL)
353 		return fScale->MaximumValue();
354 
355 	return fMaximumValue;
356 }
357 
358 
359 int64
360 DataHistory::MinimumValue() const
361 {
362 	if (fScale != NULL)
363 		return fScale->MinimumValue();
364 
365 	return fMinimumValue;
366 }
367 
368 
369 bigtime_t
370 DataHistory::Start() const
371 {
372 	if (fBuffer.CountItems() == 0)
373 		return 0;
374 
375 	return fBuffer.ItemAt(0)->time;
376 }
377 
378 
379 bigtime_t
380 DataHistory::End() const
381 {
382 	if (fBuffer.CountItems() == 0)
383 		return 0;
384 
385 	return fBuffer.ItemAt(fBuffer.CountItems() - 1)->time;
386 }
387 
388 
389 void
390 DataHistory::SetRefreshInterval(bigtime_t interval)
391 {
392 	// TODO: adjust buffer size
393 }
394 
395 
396 void
397 DataHistory::SetScale(Scale* scale)
398 {
399 	fScale = scale;
400 }
401 
402 
403 //	#pragma mark -
404 
405 
406 #ifdef __HAIKU__
407 ActivityView::HistoryLayoutItem::HistoryLayoutItem(ActivityView* parent)
408 	:
409 	fParent(parent),
410 	fFrame()
411 {
412 }
413 
414 
415 bool
416 ActivityView::HistoryLayoutItem::IsVisible()
417 {
418 	return !fParent->IsHidden(fParent);
419 }
420 
421 
422 void
423 ActivityView::HistoryLayoutItem::SetVisible(bool visible)
424 {
425 	// not allowed
426 }
427 
428 
429 BRect
430 ActivityView::HistoryLayoutItem::Frame()
431 {
432 	return fFrame;
433 }
434 
435 
436 void
437 ActivityView::HistoryLayoutItem::SetFrame(BRect frame)
438 {
439 	fFrame = frame;
440 	fParent->_UpdateFrame();
441 }
442 
443 
444 BView*
445 ActivityView::HistoryLayoutItem::View()
446 {
447 	return fParent;
448 }
449 
450 
451 BSize
452 ActivityView::HistoryLayoutItem::BasePreferredSize()
453 {
454 	BSize size(BaseMaxSize());
455 	return size;
456 }
457 
458 
459 //	#pragma mark -
460 
461 
462 ActivityView::LegendLayoutItem::LegendLayoutItem(ActivityView* parent)
463 	:
464 	fParent(parent),
465 	fFrame()
466 {
467 }
468 
469 
470 bool
471 ActivityView::LegendLayoutItem::IsVisible()
472 {
473 	return !fParent->IsHidden(fParent);
474 }
475 
476 
477 void
478 ActivityView::LegendLayoutItem::SetVisible(bool visible)
479 {
480 	// not allowed
481 }
482 
483 
484 BRect
485 ActivityView::LegendLayoutItem::Frame()
486 {
487 	return fFrame;
488 }
489 
490 
491 void
492 ActivityView::LegendLayoutItem::SetFrame(BRect frame)
493 {
494 	fFrame = frame;
495 	fParent->_UpdateFrame();
496 }
497 
498 
499 BView*
500 ActivityView::LegendLayoutItem::View()
501 {
502 	return fParent;
503 }
504 
505 
506 BSize
507 ActivityView::LegendLayoutItem::BaseMinSize()
508 {
509 	// TODO: Cache the info. Might be too expensive for this call.
510 	BSize size;
511 	size.width = 80;
512 	size.height = fParent->_LegendHeight();
513 
514 	return size;
515 }
516 
517 
518 BSize
519 ActivityView::LegendLayoutItem::BaseMaxSize()
520 {
521 	BSize size(BaseMinSize());
522 	size.width = B_SIZE_UNLIMITED;
523 	return size;
524 }
525 
526 
527 BSize
528 ActivityView::LegendLayoutItem::BasePreferredSize()
529 {
530 	BSize size(BaseMinSize());
531 	return size;
532 }
533 
534 
535 BAlignment
536 ActivityView::LegendLayoutItem::BaseAlignment()
537 {
538 	return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
539 }
540 #endif
541 
542 
543 //	#pragma mark -
544 
545 
546 const rgb_color kWhite = (rgb_color){255, 255, 255, 255};
547 const rgb_color kBlack = (rgb_color){0, 0, 0, 255};
548 
549 
550 ActivityView::ActivityView(BRect frame, const char* name,
551 		const BMessage* settings, uint32 resizingMode)
552 	: BView(frame, name, resizingMode,
553 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
554 	fSourcesLock("data sources")
555 {
556 	_Init(settings);
557 
558 	BRect rect(Bounds());
559 	rect.top = rect.bottom - 7;
560 	rect.left = rect.right - 7;
561 	BDragger* dragger = new BDragger(rect, this,
562 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
563 	AddChild(dragger);
564 }
565 
566 
567 ActivityView::ActivityView(const char* name, const BMessage* settings)
568 #ifdef __HAIKU__
569 	: BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
570 #else
571 	: BView(BRect(0, 0, 300, 200), name, B_FOLLOW_NONE,
572 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
573 #endif
574 	fSourcesLock("data sources")
575 {
576 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
577 
578 	_Init(settings);
579 
580 	BRect rect(Bounds());
581 	rect.top = rect.bottom - 7;
582 	rect.left = rect.right - 7;
583 	BDragger* dragger = new BDragger(rect, this,
584 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
585 	AddChild(dragger);
586 }
587 
588 
589 ActivityView::ActivityView(BMessage* archive)
590 	: BView(archive)
591 {
592 	_Init(archive);
593 }
594 
595 
596 ActivityView::~ActivityView()
597 {
598 	stop_watching(this);
599 	delete fOffscreen;
600 	delete fSystemInfoHandler;
601 }
602 
603 
604 void
605 ActivityView::_Init(const BMessage* settings)
606 {
607 	fHistoryBackgroundColor = (rgb_color){255, 255, 240};
608 	fLegendBackgroundColor = LowColor();
609 		// the low color is restored by the BView unarchiving
610 	fOffscreen = NULL;
611 #ifdef __HAIKU__
612 	fHistoryLayoutItem = NULL;
613 	fLegendLayoutItem = NULL;
614 #endif
615 	SetViewColor(B_TRANSPARENT_COLOR);
616 
617 	fLastRefresh = 0;
618 	fDrawResolution = 1;
619 	fZooming = false;
620 
621 	fSystemInfoHandler = new SystemInfoHandler;
622 
623 	if (settings == NULL
624 		|| settings->FindInt64("refresh interval", &fRefreshInterval) != B_OK)
625 		fRefreshInterval = kInitialRefreshInterval;
626 
627 	if (settings == NULL
628 		|| settings->FindBool("show legend", &fShowLegend) != B_OK)
629 		fShowLegend = true;
630 
631 	if (settings == NULL)
632 		return;
633 
634 	ssize_t colorLength;
635 	rgb_color *color;
636 	if (settings->FindData("history background color", B_RGB_COLOR_TYPE,
637 			(const void **)&color, &colorLength) == B_OK
638 		&& colorLength == sizeof(rgb_color))
639 		fHistoryBackgroundColor = *color;
640 
641 	const char* name;
642 	for (int32 i = 0; settings->FindString("source", i, &name) == B_OK; i++) {
643 		AddDataSource(DataSource::FindSource(name), settings);
644 	}
645 }
646 
647 
648 status_t
649 ActivityView::Archive(BMessage* into, bool deep) const
650 {
651 	status_t status;
652 
653 	status = BView::Archive(into, deep);
654 	if (status < B_OK)
655 		return status;
656 
657 	status = into->AddString("add_on", kSignature);
658 	if (status < B_OK)
659 		return status;
660 
661 	status = SaveState(*into);
662 	if (status < B_OK)
663 		return status;
664 
665 	return B_OK;
666 }
667 
668 
669 BArchivable*
670 ActivityView::Instantiate(BMessage* archive)
671 {
672 	if (!validate_instantiation(archive, "ActivityView"))
673 		return NULL;
674 
675 	return new ActivityView(archive);
676 }
677 
678 
679 status_t
680 ActivityView::SaveState(BMessage& state) const
681 {
682 	status_t status = state.AddBool("show legend", fShowLegend);
683 	if (status != B_OK)
684 		return status;
685 
686 	status = state.AddInt64("refresh interval", fRefreshInterval);
687 	if (status != B_OK)
688 		return status;
689 
690 	status = state.AddData("history background color", B_RGB_COLOR_TYPE,
691 		&fHistoryBackgroundColor, sizeof(rgb_color));
692 	if (status != B_OK)
693 		return status;
694 
695 	for (int32 i = 0; i < fSources.CountItems(); i++) {
696 		DataSource* source = fSources.ItemAt(i);
697 
698 		if (!source->PerCPU() || source->CPU() == 0)
699 			status = state.AddString("source", source->Name());
700 		if (status != B_OK)
701 			return status;
702 
703 		BString name = source->Name();
704 		name << " color";
705 		rgb_color color = source->Color();
706 		state.AddData(name.String(), B_RGB_COLOR_TYPE, &color,
707 			sizeof(rgb_color));
708 	}
709 	return B_OK;
710 }
711 
712 
713 Scale*
714 ActivityView::_ScaleFor(scale_type type)
715 {
716 	if (type == kNoScale)
717 		return NULL;
718 
719 	std::map<scale_type, ::Scale*>::iterator iterator = fScales.find(type);
720 	if (iterator != fScales.end())
721 		return iterator->second;
722 
723 	// add new scale
724 	::Scale* scale = new ::Scale(type);
725 	fScales[type] = scale;
726 
727 	return scale;
728 }
729 
730 
731 #ifdef __HAIKU__
732 BLayoutItem*
733 ActivityView::CreateHistoryLayoutItem()
734 {
735 	if (fHistoryLayoutItem == NULL)
736 		fHistoryLayoutItem = new HistoryLayoutItem(this);
737 
738 	return fHistoryLayoutItem;
739 }
740 
741 
742 BLayoutItem*
743 ActivityView::CreateLegendLayoutItem()
744 {
745 	if (fLegendLayoutItem == NULL)
746 		fLegendLayoutItem = new LegendLayoutItem(this);
747 
748 	return fLegendLayoutItem;
749 }
750 #endif
751 
752 
753 DataSource*
754 ActivityView::FindDataSource(const DataSource* search)
755 {
756 	BAutolock _(fSourcesLock);
757 
758 	for (int32 i = fSources.CountItems(); i-- > 0;) {
759 		DataSource* source = fSources.ItemAt(i);
760 		if (!strcmp(source->Name(), search->Name()))
761 			return source;
762 	}
763 
764 	return NULL;
765 }
766 
767 
768 status_t
769 ActivityView::AddDataSource(const DataSource* source, const BMessage* state)
770 {
771 	if (source == NULL)
772 		return B_BAD_VALUE;
773 
774 	BAutolock _(fSourcesLock);
775 
776 	// Search for the correct insert spot to maintain the order of the sources
777 	int32 insert = DataSource::IndexOf(source);
778 	for (int32 i = 0; i < fSources.CountItems() && i < insert; i++) {
779 		DataSource* before = fSources.ItemAt(i);
780 		if (DataSource::IndexOf(before) > insert) {
781 			insert = i;
782 			break;
783 		}
784 	}
785 	if (insert > fSources.CountItems())
786 		insert = fSources.CountItems();
787 
788 	// Generate DataHistory and ViewHistory objects for the source
789 	// (one might need one history per CPU)
790 
791 	uint32 count = 1;
792 	if (source->PerCPU()) {
793 		SystemInfo info;
794 		count = info.CPUCount();
795 	}
796 
797 	for (uint32 i = 0; i < count; i++) {
798 		DataHistory* values = new(std::nothrow) DataHistory(10 * 60000000LL,
799 			RefreshInterval());
800 		ListAddDeleter<DataHistory> valuesDeleter(fValues, values, insert);
801 
802 		ViewHistory* viewValues = new(std::nothrow) ViewHistory;
803 		ListAddDeleter<ViewHistory> viewValuesDeleter(fViewValues, viewValues,
804 			insert);
805 
806 		if (valuesDeleter.Failed() || viewValuesDeleter.Failed())
807 			return B_NO_MEMORY;
808 
809 		values->SetScale(_ScaleFor(source->ScaleType()));
810 
811 		DataSource* copy;
812 		if (source->PerCPU())
813 			copy = source->CopyForCPU(i);
814 		else
815 			copy = source->Copy();
816 
817 		ListAddDeleter<DataSource> sourceDeleter(fSources, copy, insert);
818 		if (sourceDeleter.Failed())
819 			return B_NO_MEMORY;
820 
821 		BString colorName = source->Name();
822 		colorName << " color";
823 		if (state != NULL) {
824 			const rgb_color* color = NULL;
825 			ssize_t colorLength;
826 			if (state->FindData(colorName.String(), B_RGB_COLOR_TYPE, i,
827 					(const void**)&color, &colorLength) == B_OK
828 				&& colorLength == sizeof(rgb_color))
829 				copy->SetColor(*color);
830 		}
831 
832 		valuesDeleter.Detach();
833 		viewValuesDeleter.Detach();
834 		sourceDeleter.Detach();
835 	}
836 
837 #ifdef __HAIKU__
838 	InvalidateLayout();
839 #endif
840 	return B_OK;
841 }
842 
843 
844 status_t
845 ActivityView::RemoveDataSource(const DataSource* remove)
846 {
847 	bool removed = false;
848 
849 	BAutolock _(fSourcesLock);
850 
851 	while (true) {
852 		DataSource* source = FindDataSource(remove);
853 		if (source == NULL) {
854 			if (removed)
855 				break;
856 			return B_ENTRY_NOT_FOUND;
857 		}
858 
859 		int32 index = fSources.IndexOf(source);
860 		if (index < 0)
861 			return B_ENTRY_NOT_FOUND;
862 
863 		fSources.RemoveItemAt(index);
864 		delete source;
865 		DataHistory* values = fValues.RemoveItemAt(index);
866 		delete values;
867 		removed = true;
868 	}
869 
870 #ifdef __HAIKU__
871 	InvalidateLayout();
872 #endif
873 	return B_OK;
874 }
875 
876 
877 void
878 ActivityView::RemoveAllDataSources()
879 {
880 	BAutolock _(fSourcesLock);
881 
882 	fSources.MakeEmpty();
883 	fValues.MakeEmpty();
884 }
885 
886 
887 void
888 ActivityView::AttachedToWindow()
889 {
890 	if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0)
891 		_LoadBackgroundInfo(true);
892 
893 	Looper()->AddHandler(fSystemInfoHandler);
894 	fSystemInfoHandler->StartWatching();
895 
896 	fRefreshSem = create_sem(0, "refresh sem");
897 	fRefreshThread = spawn_thread(&_RefreshThread, "source refresh",
898 		B_URGENT_DISPLAY_PRIORITY, this);
899 	resume_thread(fRefreshThread);
900 
901 	FrameResized(Bounds().Width(), Bounds().Height());
902 }
903 
904 
905 void
906 ActivityView::DetachedFromWindow()
907 {
908 	fSystemInfoHandler->StopWatching();
909 	Looper()->RemoveHandler(fSystemInfoHandler);
910 
911 	delete_sem(fRefreshSem);
912 	wait_for_thread(fRefreshThread, NULL);
913 }
914 
915 
916 #ifdef __HAIKU__
917 BSize
918 ActivityView::MinSize()
919 {
920 	BSize size(32, 32);
921 	if (fShowLegend)
922 		size.height = _LegendHeight();
923 
924 	return size;
925 }
926 #endif
927 
928 
929 void
930 ActivityView::FrameResized(float /*width*/, float /*height*/)
931 {
932 	_UpdateOffscreenBitmap();
933 }
934 
935 
936 void
937 ActivityView::_UpdateOffscreenBitmap()
938 {
939 	BRect frame = _HistoryFrame();
940 	frame.OffsetTo(B_ORIGIN);
941 
942 	if (fOffscreen != NULL && frame == fOffscreen->Bounds())
943 		return;
944 
945 	delete fOffscreen;
946 
947 	// create offscreen bitmap
948 
949 	fOffscreen = new(std::nothrow) BBitmap(frame, B_BITMAP_ACCEPTS_VIEWS,
950 		B_RGB32);
951 	if (fOffscreen == NULL || fOffscreen->InitCheck() != B_OK) {
952 		delete fOffscreen;
953 		fOffscreen = NULL;
954 		return;
955 	}
956 
957 	BView* view = new BView(frame, NULL, B_FOLLOW_NONE, B_SUBPIXEL_PRECISE);
958 	view->SetViewColor(fHistoryBackgroundColor);
959 	view->SetLowColor(view->ViewColor());
960 	fOffscreen->AddChild(view);
961 }
962 
963 
964 BView*
965 ActivityView::_OffscreenView()
966 {
967 	if (fOffscreen == NULL)
968 		return NULL;
969 
970 	return fOffscreen->ChildAt(0);
971 }
972 
973 
974 void
975 ActivityView::MouseDown(BPoint where)
976 {
977 	int32 buttons = B_SECONDARY_MOUSE_BUTTON;
978 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
979 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
980 
981 	if (buttons == B_PRIMARY_MOUSE_BUTTON) {
982 		fZoomPoint = where;
983 		fOriginalResolution = fDrawResolution;
984 		fZooming = true;
985 		SetMouseEventMask(B_POINTER_EVENTS);
986 		return;
987 	}
988 
989 	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
990 	menu->SetFont(be_plain_font);
991 
992 	BMenu* additionalMenu = new BMenu("Additional items");
993 	additionalMenu->SetFont(be_plain_font);
994 
995 	SystemInfo info;
996 	BMenuItem* item;
997 
998 	for (int32 i = 0; i < DataSource::CountSources(); i++) {
999 		const DataSource* source = DataSource::SourceAt(i);
1000 
1001 		if (source->MultiCPUOnly() && info.CPUCount() == 1)
1002 			continue;
1003 
1004 		BMessage* message = new BMessage(kMsgToggleDataSource);
1005 		message->AddInt32("index", i);
1006 
1007 		item = new BMenuItem(source->Name(), message);
1008 		if (FindDataSource(source))
1009 			item->SetMarked(true);
1010 
1011 		if (source->Primary())
1012 			menu->AddItem(item);
1013 		else
1014 			additionalMenu->AddItem(item);
1015 	}
1016 
1017 	menu->AddItem(new BMenuItem(additionalMenu));
1018 	menu->AddSeparatorItem();
1019 	menu->AddItem(new BMenuItem(fShowLegend ? "Hide legend" : "Show legend",
1020 		new BMessage(kMsgToggleLegend)));
1021 
1022 	menu->SetTargetForItems(this);
1023 	additionalMenu->SetTargetForItems(this);
1024 
1025 	ActivityWindow* window = dynamic_cast<ActivityWindow*>(Window());
1026 	if (window != NULL && window->ActivityViewCount() > 1) {
1027 		menu->AddSeparatorItem();
1028 		BMessage* message = new BMessage(kMsgRemoveView);
1029 		message->AddPointer("view", this);
1030 		menu->AddItem(item = new BMenuItem("Remove graph", message));
1031 		item->SetTarget(window);
1032 	}
1033 
1034 	ConvertToScreen(&where);
1035 	menu->Go(where, true, false, true);
1036 }
1037 
1038 
1039 void
1040 ActivityView::MouseUp(BPoint where)
1041 {
1042 	fZooming = false;
1043 }
1044 
1045 
1046 void
1047 ActivityView::MouseMoved(BPoint where, uint32 transit,
1048 	const BMessage* dragMessage)
1049 {
1050 	if (!fZooming)
1051 		return;
1052 
1053 	int32 shift = int32(where.x - fZoomPoint.x) / 25;
1054 	int32 resolution;
1055 	if (shift > 0)
1056 		resolution = fOriginalResolution << shift;
1057 	else
1058 		resolution = fOriginalResolution >> -shift;
1059 
1060 	_UpdateResolution(resolution);
1061 }
1062 
1063 
1064 void
1065 ActivityView::MessageReceived(BMessage* message)
1066 {
1067 	// if a color is dropped, use it as background
1068 	if (message->WasDropped()) {
1069 		rgb_color* color;
1070 		ssize_t size;
1071 		if (message->FindData("RGBColor", B_RGB_COLOR_TYPE, 0,
1072 				(const void**)&color, &size) == B_OK
1073 			&& size == sizeof(rgb_color)) {
1074 			BPoint dropPoint = message->DropPoint();
1075 			ConvertFromScreen(&dropPoint);
1076 
1077 			if (_HistoryFrame().Contains(dropPoint)) {
1078 				fHistoryBackgroundColor = *color;
1079 				Invalidate(_HistoryFrame());
1080 			} else {
1081 				// check each legend color box
1082 				BAutolock _(fSourcesLock);
1083 
1084 				BRect legendFrame = _LegendFrame();
1085 				for (int32 i = 0; i < fSources.CountItems(); i++) {
1086 					BRect frame = _LegendColorFrameAt(legendFrame, i);
1087 					if (frame.Contains(dropPoint)) {
1088 						fSources.ItemAt(i)->SetColor(*color);
1089 						Invalidate(_HistoryFrame());
1090 						Invalidate(frame);
1091 						return;
1092 					}
1093 				}
1094 
1095 				if (dynamic_cast<ActivityMonitor*>(be_app) == NULL) {
1096 					// allow background color change in the replicant only
1097 					fLegendBackgroundColor = *color;
1098 					SetLowColor(fLegendBackgroundColor);
1099 					Invalidate(legendFrame);
1100 				}
1101 			}
1102 			return;
1103 		}
1104 	}
1105 
1106 	switch (message->what) {
1107 		case B_ABOUT_REQUESTED:
1108 			ActivityMonitor::ShowAbout();
1109 			break;
1110 
1111 		case B_NODE_MONITOR:
1112 		{
1113 			BString attrName;
1114 			if (message->FindString("attr", &attrName) == B_OK) {
1115 				if (attrName == kDesktopAttrName)
1116 					_LoadBackgroundInfo(false);
1117 			} else
1118 				_LoadBackgroundInfo(false);
1119 			break;
1120 		}
1121 
1122 		case kMsgUpdateResolution:
1123 		{
1124 			int32 resolution;
1125 			if (message->FindInt32("resolution", &resolution) != B_OK)
1126 				break;
1127 
1128 			_UpdateResolution(resolution, false);
1129 			break;
1130 		}
1131 
1132 		case kMsgTimeIntervalUpdated:
1133 			bigtime_t interval;
1134 			if (message->FindInt64("interval", &interval) != B_OK)
1135 				break;
1136 
1137 			if (interval < 10000)
1138 				interval = 10000;
1139 
1140 			atomic_set64(&fRefreshInterval, interval);
1141 			break;
1142 
1143 		case kMsgToggleDataSource:
1144 		{
1145 			int32 index;
1146 			if (message->FindInt32("index", &index) != B_OK)
1147 				break;
1148 
1149 			const DataSource* baseSource = DataSource::SourceAt(index);
1150 			if (baseSource == NULL)
1151 				break;
1152 
1153 			DataSource* source = FindDataSource(baseSource);
1154 			if (source == NULL)
1155 				AddDataSource(baseSource);
1156 			else
1157 				RemoveDataSource(baseSource);
1158 
1159 			Invalidate();
1160 			break;
1161 		}
1162 
1163 		case kMsgToggleLegend:
1164 			fShowLegend = !fShowLegend;
1165 			Invalidate();
1166 			break;
1167 
1168 		case B_MOUSE_WHEEL_CHANGED:
1169 		{
1170 			float deltaY = 0.0f;
1171 			if (message->FindFloat("be:wheel_delta_y", &deltaY) != B_OK
1172 				|| deltaY == 0.0f)
1173 				break;
1174 
1175 			int32 resolution = fDrawResolution;
1176 			if (deltaY > 0)
1177 				resolution *= 2;
1178 			else
1179 				resolution /= 2;
1180 
1181 			_UpdateResolution(resolution);
1182 			break;
1183 		}
1184 
1185 		default:
1186 			BView::MessageReceived(message);
1187 			break;
1188 	}
1189 }
1190 
1191 
1192 void
1193 ActivityView::_UpdateFrame()
1194 {
1195 #ifdef __HAIKU__
1196 	if (fLegendLayoutItem == NULL || fHistoryLayoutItem == NULL)
1197 		return;
1198 
1199 	BRect historyFrame = fHistoryLayoutItem->Frame();
1200 	BRect legendFrame = fLegendLayoutItem->Frame();
1201 #else
1202 	BRect historyFrame = Bounds();
1203 	BRect legendFrame = Bounds();
1204 	historyFrame.bottom -= 2 * Bounds().Height() / 3;
1205 	legendFrame.top += Bounds().Height() / 3;
1206 #endif
1207 	MoveTo(historyFrame.left, historyFrame.top);
1208 	ResizeTo(legendFrame.left + legendFrame.Width() - historyFrame.left,
1209 		legendFrame.top + legendFrame.Height() - historyFrame.top);
1210 }
1211 
1212 
1213 BRect
1214 ActivityView::_HistoryFrame() const
1215 {
1216 	BRect frame = Bounds();
1217 
1218 	if (fShowLegend) {
1219 		BRect legendFrame = _LegendFrame();
1220 		frame.bottom = legendFrame.top - 1;
1221 	}
1222 
1223 	frame.InsetBy(2, 2);
1224 
1225 	return frame;
1226 }
1227 
1228 
1229 float
1230 ActivityView::_LegendHeight() const
1231 {
1232 	font_height fontHeight;
1233 	GetFontHeight(&fontHeight);
1234 
1235 	BAutolock _(fSourcesLock);
1236 
1237 	int32 rows = (fSources.CountItems() + 1) / 2;
1238 
1239 	int32 boldMargin = Parent()
1240 		&& (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0 ? 2 : 0;
1241 
1242 	return rows * (4 + ceilf(fontHeight.ascent)
1243 		+ ceilf(fontHeight.descent) + ceilf(fontHeight.leading)) + boldMargin;
1244 }
1245 
1246 
1247 BRect
1248 ActivityView::_LegendFrame() const
1249 {
1250 	float height;
1251 #ifdef __HAIKU__
1252 	if (fLegendLayoutItem != NULL)
1253 		height = fLegendLayoutItem->Frame().Height();
1254 	else
1255 #endif
1256 		height = _LegendHeight();
1257 
1258 	BRect frame = Bounds();
1259 	frame.top = frame.bottom - height;
1260 
1261 	return frame;
1262 }
1263 
1264 
1265 BRect
1266 ActivityView::_LegendFrameAt(BRect frame, int32 index) const
1267 {
1268 	int32 column = index & 1;
1269 	int32 row = index / 2;
1270 	if (column == 0)
1271 		frame.right = frame.left + floorf(frame.Width() / 2) - 5;
1272 	else
1273 		frame.left = frame.right - floorf(frame.Width() / 2) + 5;
1274 
1275 	BAutolock _(fSourcesLock);
1276 
1277 	int32 rows = (fSources.CountItems() + 1) / 2;
1278 	float height = floorf((frame.Height() - 5) / rows);
1279 
1280 	frame.top = frame.top + 5 + row * height;
1281 	frame.bottom = frame.top + height - 1;
1282 
1283 	return frame;
1284 }
1285 
1286 
1287 BRect
1288 ActivityView::_LegendColorFrameAt(BRect frame, int32 index) const
1289 {
1290 	frame = _LegendFrameAt(frame, index);
1291 	frame.InsetBy(1, 1);
1292 	frame.right = frame.left + frame.Height();
1293 
1294 	return frame;
1295 }
1296 
1297 
1298 float
1299 ActivityView::_PositionForValue(DataSource* source, DataHistory* values,
1300 	int64 value)
1301 {
1302 	int64 min = source->Minimum();
1303 	int64 max = source->Maximum();
1304 	if (source->AdaptiveScale()) {
1305 		int64 adaptiveMax = int64(values->MaximumValue() * 1.2);
1306 		if (adaptiveMax < max)
1307 			max = adaptiveMax;
1308 	}
1309 
1310 	if (value > max)
1311 		value = max;
1312 	if (value < min)
1313 		value = min;
1314 
1315 	float height = _HistoryFrame().Height();
1316 	return height - (value - min) * height / (max - min);
1317 }
1318 
1319 
1320 void
1321 ActivityView::_DrawHistory(bool drawBackground)
1322 {
1323 	_UpdateOffscreenBitmap();
1324 
1325 	BView* view = this;
1326 	if (fOffscreen != NULL) {
1327 		fOffscreen->Lock();
1328 		view = _OffscreenView();
1329 	}
1330 
1331 	BRect frame = _HistoryFrame();
1332 	BRect outerFrame = frame.InsetByCopy(-2, -2);
1333 
1334 	// draw the outer frame
1335 	uint32 flags = 0;
1336 	if (!drawBackground)
1337 		flags |= BControlLook::B_BLEND_FRAME;
1338 	be_control_look->DrawTextControlBorder(this, outerFrame,
1339 		outerFrame, fLegendBackgroundColor, flags);
1340 
1341 	// convert to offscreen view if necessary
1342 	if (view != this)
1343 		frame.OffsetTo(B_ORIGIN);
1344 
1345 	view->SetLowColor(fHistoryBackgroundColor);
1346 	view->FillRect(frame, B_SOLID_LOW);
1347 
1348 	uint32 step = 2;
1349 	uint32 resolution = fDrawResolution;
1350 	if (fDrawResolution > 1) {
1351 		step = 1;
1352 		resolution--;
1353 	}
1354 
1355 	uint32 width = frame.IntegerWidth() - 10;
1356 	uint32 steps = width / step;
1357 	bigtime_t timeStep = RefreshInterval() * resolution;
1358 	bigtime_t now = system_time();
1359 
1360 	// Draw scale
1361 	// TODO: add second markers?
1362 
1363 	view->SetPenSize(1);
1364 
1365 	rgb_color scaleColor = view->LowColor();
1366 	uint32 average = (scaleColor.red + scaleColor.green + scaleColor.blue) / 3;
1367 	if (average < 96)
1368 		scaleColor = tint_color(scaleColor, B_LIGHTEN_2_TINT);
1369 	else
1370 		scaleColor = tint_color(scaleColor, B_DARKEN_2_TINT);
1371 
1372 	view->SetHighColor(scaleColor);
1373 	view->StrokeLine(BPoint(frame.left, frame.top + frame.Height() / 2),
1374 		BPoint(frame.right, frame.top + frame.Height() / 2));
1375 
1376 	// Draw values
1377 
1378 	view->SetPenSize(1.5);
1379 	BAutolock _(fSourcesLock);
1380 
1381 	for (uint32 i = fSources.CountItems(); i-- > 0;) {
1382 		ViewHistory* viewValues = fViewValues.ItemAt(i);
1383 		DataSource* source = fSources.ItemAt(i);
1384 		DataHistory* values = fValues.ItemAt(i);
1385 
1386 		viewValues->Update(values, steps, fDrawResolution, now, timeStep,
1387 			RefreshInterval());
1388 
1389 		uint32 x = viewValues->Start() * step;
1390 		BShape shape;
1391 		bool first = true;
1392 
1393 		for (uint32 i = viewValues->Start(); i < steps; x += step, i++) {
1394 			float y = _PositionForValue(source, values,
1395 				viewValues->ValueAt(i));
1396 
1397 			if (first) {
1398 				shape.MoveTo(BPoint(x, y));
1399 				first = false;
1400 			} else
1401 				shape.LineTo(BPoint(x, y));
1402 		}
1403 
1404 		view->SetHighColor(source->Color());
1405 		view->SetLineMode(B_BUTT_CAP, B_ROUND_JOIN);
1406 		view->MovePenTo(B_ORIGIN);
1407 		view->StrokeShape(&shape);
1408 	}
1409 
1410 	// TODO: add marks when an app started or quit
1411 	view->Sync();
1412 	if (fOffscreen != NULL) {
1413 		fOffscreen->Unlock();
1414 		DrawBitmap(fOffscreen, outerFrame.LeftTop());
1415 	}
1416 }
1417 
1418 
1419 void
1420 ActivityView::_UpdateResolution(int32 resolution, bool broadcast)
1421 {
1422 	if (resolution < 1)
1423 		resolution = 1;
1424 	if (resolution > 128)
1425 		resolution = 128;
1426 
1427 	if (resolution == fDrawResolution)
1428 		return;
1429 
1430 	ActivityWindow* window = dynamic_cast<ActivityWindow*>(Window());
1431 	if (broadcast && window != NULL) {
1432 		BMessage update(kMsgUpdateResolution);
1433 		update.AddInt32("resolution", resolution);
1434 		window->BroadcastToActivityViews(&update, this);
1435 	}
1436 
1437 	fDrawResolution = resolution;
1438 	Invalidate();
1439 }
1440 
1441 
1442 void
1443 ActivityView::_LoadBackgroundInfo(bool watch)
1444 {
1445 	fCachedOutline = false;
1446 	fCachedWorkspace = -1;
1447 	BPath path;
1448 	if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) {
1449 		BNode desktopNode = BNode(path.Path());
1450 
1451 		attr_info info;
1452 		if (desktopNode.GetAttrInfo(kDesktopAttrName, &info) != B_OK)
1453 			return;
1454 
1455 		char* buffer = new char[info.size];
1456 		if (desktopNode.ReadAttr(kDesktopAttrName, B_MESSAGE_TYPE, 0,
1457 			buffer, (size_t)info.size) == info.size) {
1458 				BMessage message;
1459 				if (message.Unflatten(buffer) == B_OK)
1460 					fBackgroundInfo = message;
1461 		}
1462 		delete[] buffer;
1463 
1464 		if (watch) {
1465 			node_ref nref;
1466 			desktopNode.GetNodeRef(&nref);
1467 			watch_node(&nref, B_WATCH_ATTR, this);
1468 		}
1469 	}
1470 }
1471 
1472 
1473 void
1474 ActivityView::Draw(BRect updateRect)
1475 {
1476 	bool drawBackground = true;
1477 	if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0)
1478 		drawBackground = false;
1479 
1480 	_DrawHistory(drawBackground);
1481 
1482 	if (!fShowLegend)
1483 		return;
1484 
1485 	// draw legend
1486 	BRect legendFrame = _LegendFrame();
1487 	SetLowColor(fLegendBackgroundColor);
1488 	if (drawBackground)
1489 		FillRect(legendFrame, B_SOLID_LOW);
1490 
1491 	BAutolock _(fSourcesLock);
1492 
1493 	font_height fontHeight;
1494 	GetFontHeight(&fontHeight);
1495 
1496 	for (int32 i = 0; i < fSources.CountItems(); i++) {
1497 		DataSource* source = fSources.ItemAt(i);
1498 		DataHistory* values = fValues.ItemAt(i);
1499 		BRect frame = _LegendFrameAt(legendFrame, i);
1500 
1501 		// draw color box
1502 		BRect colorBox = _LegendColorFrameAt(legendFrame, i);
1503 		BRect rect = colorBox;
1504 		uint32 flags = BControlLook::B_BLEND_FRAME;
1505 		be_control_look->DrawTextControlBorder(this, rect,
1506 			rect, fLegendBackgroundColor, flags);
1507 		SetHighColor(source->Color());
1508 		FillRect(rect);
1509 
1510 		// show current value and label
1511 		float y = frame.top + ceilf(fontHeight.ascent);
1512 		int64 value = values->ValueAt(values->End());
1513 		BString text;
1514 		source->Print(text, value);
1515 		float width = StringWidth(text.String());
1516 
1517 		BString label = source->Label();
1518 		float possibleLabelWidth = frame.right - colorBox.right - 12 - width;
1519 		// TODO: TruncateString() is broken... remove + 5 when fixed!
1520 		if (ceilf(StringWidth(label.String()) + 5) > possibleLabelWidth)
1521 			label = source->ShortLabel();
1522 		TruncateString(&label, B_TRUNCATE_MIDDLE, possibleLabelWidth);
1523 
1524 		if (drawBackground)
1525 			SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
1526 		else {
1527 			rgb_color c = Parent()->ViewColor();
1528 			rgb_color textColor = c.red + c.green * 1.5f + c.blue * 0.50f
1529 				>= 300 ? kBlack : kWhite;
1530 
1531 			int32 mask;
1532 			bool tmpOutline = false;
1533 			bool outline = fCachedOutline;
1534 			int8 indice = 0;
1535 
1536 			if (fCachedWorkspace != current_workspace()) {
1537 				while (fBackgroundInfo.FindInt32("be:bgndimginfoworkspaces",
1538 						indice, &mask) == B_OK
1539 					&& fBackgroundInfo.FindBool("be:bgndimginfoerasetext",
1540 						indice, &tmpOutline) == B_OK) {
1541 					if (((1 << current_workspace()) & mask) != 0) {
1542 						outline = tmpOutline;
1543 						fCachedWorkspace = current_workspace();
1544 						fCachedOutline = outline;
1545 						break;
1546 					}
1547 					indice++;
1548 				}
1549 			}
1550 
1551 			if (outline) {
1552 				SetDrawingMode(B_OP_ALPHA);
1553 				SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
1554 
1555 				BFont font;
1556 				GetFont(&font);
1557 				if (textColor == kBlack) {
1558 					// Black text with white halo/glow
1559 					rgb_color glowColor = kWhite;
1560 
1561 					font.SetFalseBoldWidth(2.0);
1562 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1563 
1564 					glowColor.alpha = 30;
1565 					SetHighColor(glowColor);
1566 					DrawString(label.String(), BPoint(6 + colorBox.right, y));
1567 					DrawString(text.String(), BPoint(frame.right - width, y));
1568 
1569 					font.SetFalseBoldWidth(1.0);
1570 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1571 
1572 					glowColor.alpha = 65;
1573 					SetHighColor(glowColor);
1574 					DrawString(label.String(), BPoint(6 + colorBox.right, y));
1575 					DrawString(text.String(), BPoint(frame.right - width, y));
1576 
1577 					font.SetFalseBoldWidth(0.0);
1578 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1579 				} else {
1580 					// white text with black outline
1581 					rgb_color outlineColor = kBlack;
1582 
1583 					font.SetFalseBoldWidth(1.0);
1584 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1585 
1586 					outlineColor.alpha = 30;
1587 					SetHighColor(outlineColor);
1588 					DrawString(label.String(), BPoint(6 + colorBox.right, y));
1589 					DrawString(text.String(), BPoint(frame.right - width, y));
1590 
1591 					font.SetFalseBoldWidth(0.0);
1592 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1593 
1594 					outlineColor.alpha = 200;
1595 					SetHighColor(outlineColor);
1596 					DrawString(label.String(), BPoint(6 + colorBox.right + 1,
1597 						y + 1));
1598 					DrawString(text.String(), BPoint(frame.right - width + 1,
1599 						y + 1));
1600 				}
1601 			}
1602 			SetDrawingMode(B_OP_OVER);
1603 			SetHighColor(textColor);
1604 		}
1605 		DrawString(label.String(), BPoint(6 + colorBox.right, y));
1606 		DrawString(text.String(), BPoint(frame.right - width, y));
1607 	}
1608 }
1609 
1610 
1611 void
1612 ActivityView::_Refresh()
1613 {
1614 	bigtime_t lastTimeout = system_time() - RefreshInterval();
1615 	BMessenger target(this);
1616 
1617 	while (true) {
1618 		status_t status = acquire_sem_etc(fRefreshSem, 1, B_ABSOLUTE_TIMEOUT,
1619 			lastTimeout + RefreshInterval());
1620 		if (status == B_OK || status == B_BAD_SEM_ID)
1621 			break;
1622 		if (status == B_INTERRUPTED)
1623 			continue;
1624 
1625 		SystemInfo info(fSystemInfoHandler);
1626 		lastTimeout += RefreshInterval();
1627 
1628 		fSourcesLock.Lock();
1629 
1630 		for (uint32 i = fSources.CountItems(); i-- > 0;) {
1631 			DataSource* source = fSources.ItemAt(i);
1632 			DataHistory* values = fValues.ItemAt(i);
1633 
1634 			int64 value = source->NextValue(info);
1635 			values->AddValue(info.Time(), value);
1636 		}
1637 
1638 		fSourcesLock.Unlock();
1639 
1640 		target.SendMessage(B_INVALIDATE);
1641 	}
1642 }
1643 
1644 
1645 /*static*/ status_t
1646 ActivityView::_RefreshThread(void* self)
1647 {
1648 	((ActivityView*)self)->_Refresh();
1649 	return B_OK;
1650 }
1651