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