xref: /haiku/src/apps/activitymonitor/ActivityView.cpp (revision c90684742e7361651849be4116d0e5de3a817194)
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 const float kDraggerSize = 7;
549 
550 
551 ActivityView::ActivityView(BRect frame, const char* name,
552 		const BMessage* settings, uint32 resizingMode)
553 	: BView(frame, name, resizingMode,
554 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
555 	fSourcesLock("data sources")
556 {
557 	_Init(settings);
558 
559 	BRect rect(Bounds());
560 	rect.top = rect.bottom - kDraggerSize;
561 	rect.left = rect.right - kDraggerSize;
562 	BDragger* dragger = new BDragger(rect, this,
563 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
564 	AddChild(dragger);
565 }
566 
567 
568 ActivityView::ActivityView(const char* name, const BMessage* settings)
569 #ifdef __HAIKU__
570 	: BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
571 #else
572 	: BView(BRect(0, 0, 300, 200), name, B_FOLLOW_NONE,
573 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
574 #endif
575 	fSourcesLock("data sources")
576 {
577 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
578 
579 	_Init(settings);
580 
581 	BRect rect(Bounds());
582 	rect.top = rect.bottom - kDraggerSize;
583 	rect.left = rect.right - kDraggerSize;
584 	BDragger* dragger = new BDragger(rect, this,
585 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
586 	AddChild(dragger);
587 }
588 
589 
590 ActivityView::ActivityView(BMessage* archive)
591 	: BView(archive)
592 {
593 	_Init(archive);
594 }
595 
596 
597 ActivityView::~ActivityView()
598 {
599 	stop_watching(this);
600 	delete fOffscreen;
601 	delete fSystemInfoHandler;
602 }
603 
604 
605 void
606 ActivityView::_Init(const BMessage* settings)
607 {
608 	fHistoryBackgroundColor = (rgb_color){255, 255, 240};
609 	fLegendBackgroundColor = LowColor();
610 		// the low color is restored by the BView unarchiving
611 	fOffscreen = NULL;
612 #ifdef __HAIKU__
613 	fHistoryLayoutItem = NULL;
614 	fLegendLayoutItem = NULL;
615 #endif
616 	SetViewColor(B_TRANSPARENT_COLOR);
617 
618 	fLastRefresh = 0;
619 	fDrawResolution = 1;
620 	fZooming = false;
621 
622 	fSystemInfoHandler = new SystemInfoHandler;
623 
624 	if (settings == NULL
625 		|| settings->FindInt64("refresh interval", &fRefreshInterval) != B_OK)
626 		fRefreshInterval = kInitialRefreshInterval;
627 
628 	if (settings == NULL
629 		|| settings->FindBool("show legend", &fShowLegend) != B_OK)
630 		fShowLegend = true;
631 
632 	if (settings == NULL)
633 		return;
634 
635 	ssize_t colorLength;
636 	rgb_color *color;
637 	if (settings->FindData("history background color", B_RGB_COLOR_TYPE,
638 			(const void **)&color, &colorLength) == B_OK
639 		&& colorLength == sizeof(rgb_color))
640 		fHistoryBackgroundColor = *color;
641 
642 	const char* name;
643 	for (int32 i = 0; settings->FindString("source", i, &name) == B_OK; i++) {
644 		AddDataSource(DataSource::FindSource(name), settings);
645 	}
646 }
647 
648 
649 status_t
650 ActivityView::Archive(BMessage* into, bool deep) const
651 {
652 	status_t status;
653 
654 	status = BView::Archive(into, deep);
655 	if (status < B_OK)
656 		return status;
657 
658 	status = into->AddString("add_on", kSignature);
659 	if (status < B_OK)
660 		return status;
661 
662 	status = SaveState(*into);
663 	if (status < B_OK)
664 		return status;
665 
666 	return B_OK;
667 }
668 
669 
670 BArchivable*
671 ActivityView::Instantiate(BMessage* archive)
672 {
673 	if (!validate_instantiation(archive, "ActivityView"))
674 		return NULL;
675 
676 	return new ActivityView(archive);
677 }
678 
679 
680 status_t
681 ActivityView::SaveState(BMessage& state) const
682 {
683 	status_t status = state.AddBool("show legend", fShowLegend);
684 	if (status != B_OK)
685 		return status;
686 
687 	status = state.AddInt64("refresh interval", fRefreshInterval);
688 	if (status != B_OK)
689 		return status;
690 
691 	status = state.AddData("history background color", B_RGB_COLOR_TYPE,
692 		&fHistoryBackgroundColor, sizeof(rgb_color));
693 	if (status != B_OK)
694 		return status;
695 
696 	for (int32 i = 0; i < fSources.CountItems(); i++) {
697 		DataSource* source = fSources.ItemAt(i);
698 
699 		if (!source->PerCPU() || source->CPU() == 0)
700 			status = state.AddString("source", source->Name());
701 		if (status != B_OK)
702 			return status;
703 
704 		BString name = source->Name();
705 		name << " color";
706 		rgb_color color = source->Color();
707 		state.AddData(name.String(), B_RGB_COLOR_TYPE, &color,
708 			sizeof(rgb_color));
709 	}
710 	return B_OK;
711 }
712 
713 
714 Scale*
715 ActivityView::_ScaleFor(scale_type type)
716 {
717 	if (type == kNoScale)
718 		return NULL;
719 
720 	std::map<scale_type, ::Scale*>::iterator iterator = fScales.find(type);
721 	if (iterator != fScales.end())
722 		return iterator->second;
723 
724 	// add new scale
725 	::Scale* scale = new ::Scale(type);
726 	fScales[type] = scale;
727 
728 	return scale;
729 }
730 
731 
732 #ifdef __HAIKU__
733 BLayoutItem*
734 ActivityView::CreateHistoryLayoutItem()
735 {
736 	if (fHistoryLayoutItem == NULL)
737 		fHistoryLayoutItem = new HistoryLayoutItem(this);
738 
739 	return fHistoryLayoutItem;
740 }
741 
742 
743 BLayoutItem*
744 ActivityView::CreateLegendLayoutItem()
745 {
746 	if (fLegendLayoutItem == NULL)
747 		fLegendLayoutItem = new LegendLayoutItem(this);
748 
749 	return fLegendLayoutItem;
750 }
751 #endif
752 
753 
754 DataSource*
755 ActivityView::FindDataSource(const DataSource* search)
756 {
757 	BAutolock _(fSourcesLock);
758 
759 	for (int32 i = fSources.CountItems(); i-- > 0;) {
760 		DataSource* source = fSources.ItemAt(i);
761 		if (!strcmp(source->Name(), search->Name()))
762 			return source;
763 	}
764 
765 	return NULL;
766 }
767 
768 
769 status_t
770 ActivityView::AddDataSource(const DataSource* source, const BMessage* state)
771 {
772 	if (source == NULL)
773 		return B_BAD_VALUE;
774 
775 	BAutolock _(fSourcesLock);
776 
777 	// Search for the correct insert spot to maintain the order of the sources
778 	int32 insert = DataSource::IndexOf(source);
779 	for (int32 i = 0; i < fSources.CountItems() && i < insert; i++) {
780 		DataSource* before = fSources.ItemAt(i);
781 		if (DataSource::IndexOf(before) > insert) {
782 			insert = i;
783 			break;
784 		}
785 	}
786 	if (insert > fSources.CountItems())
787 		insert = fSources.CountItems();
788 
789 	// Generate DataHistory and ViewHistory objects for the source
790 	// (one might need one history per CPU)
791 
792 	uint32 count = 1;
793 	if (source->PerCPU()) {
794 		SystemInfo info;
795 		count = info.CPUCount();
796 	}
797 
798 	for (uint32 i = 0; i < count; i++) {
799 		DataHistory* values = new(std::nothrow) DataHistory(10 * 60000000LL,
800 			RefreshInterval());
801 		ListAddDeleter<DataHistory> valuesDeleter(fValues, values, insert);
802 
803 		ViewHistory* viewValues = new(std::nothrow) ViewHistory;
804 		ListAddDeleter<ViewHistory> viewValuesDeleter(fViewValues, viewValues,
805 			insert);
806 
807 		if (valuesDeleter.Failed() || viewValuesDeleter.Failed())
808 			return B_NO_MEMORY;
809 
810 		values->SetScale(_ScaleFor(source->ScaleType()));
811 
812 		DataSource* copy;
813 		if (source->PerCPU())
814 			copy = source->CopyForCPU(i);
815 		else
816 			copy = source->Copy();
817 
818 		ListAddDeleter<DataSource> sourceDeleter(fSources, copy, insert);
819 		if (sourceDeleter.Failed())
820 			return B_NO_MEMORY;
821 
822 		BString colorName = source->Name();
823 		colorName << " color";
824 		if (state != NULL) {
825 			const rgb_color* color = NULL;
826 			ssize_t colorLength;
827 			if (state->FindData(colorName.String(), B_RGB_COLOR_TYPE, i,
828 					(const void**)&color, &colorLength) == B_OK
829 				&& colorLength == sizeof(rgb_color))
830 				copy->SetColor(*color);
831 		}
832 
833 		valuesDeleter.Detach();
834 		viewValuesDeleter.Detach();
835 		sourceDeleter.Detach();
836 	}
837 
838 #ifdef __HAIKU__
839 	InvalidateLayout();
840 #endif
841 	return B_OK;
842 }
843 
844 
845 status_t
846 ActivityView::RemoveDataSource(const DataSource* remove)
847 {
848 	bool removed = false;
849 
850 	BAutolock _(fSourcesLock);
851 
852 	while (true) {
853 		DataSource* source = FindDataSource(remove);
854 		if (source == NULL) {
855 			if (removed)
856 				break;
857 			return B_ENTRY_NOT_FOUND;
858 		}
859 
860 		int32 index = fSources.IndexOf(source);
861 		if (index < 0)
862 			return B_ENTRY_NOT_FOUND;
863 
864 		fSources.RemoveItemAt(index);
865 		delete source;
866 		DataHistory* values = fValues.RemoveItemAt(index);
867 		delete values;
868 		removed = true;
869 	}
870 
871 #ifdef __HAIKU__
872 	InvalidateLayout();
873 #endif
874 	return B_OK;
875 }
876 
877 
878 void
879 ActivityView::RemoveAllDataSources()
880 {
881 	BAutolock _(fSourcesLock);
882 
883 	fSources.MakeEmpty();
884 	fValues.MakeEmpty();
885 }
886 
887 
888 void
889 ActivityView::AttachedToWindow()
890 {
891 	if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0)
892 		_LoadBackgroundInfo(true);
893 
894 	Looper()->AddHandler(fSystemInfoHandler);
895 	fSystemInfoHandler->StartWatching();
896 
897 	fRefreshSem = create_sem(0, "refresh sem");
898 	fRefreshThread = spawn_thread(&_RefreshThread, "source refresh",
899 		B_URGENT_DISPLAY_PRIORITY, this);
900 	resume_thread(fRefreshThread);
901 
902 	FrameResized(Bounds().Width(), Bounds().Height());
903 }
904 
905 
906 void
907 ActivityView::DetachedFromWindow()
908 {
909 	fSystemInfoHandler->StopWatching();
910 	Looper()->RemoveHandler(fSystemInfoHandler);
911 
912 	delete_sem(fRefreshSem);
913 	wait_for_thread(fRefreshThread, NULL);
914 }
915 
916 
917 #ifdef __HAIKU__
918 BSize
919 ActivityView::MinSize()
920 {
921 	BSize size(32, 32);
922 	if (fShowLegend)
923 		size.height = _LegendHeight();
924 
925 	return size;
926 }
927 #endif
928 
929 
930 void
931 ActivityView::FrameResized(float /*width*/, float /*height*/)
932 {
933 	_UpdateOffscreenBitmap();
934 }
935 
936 
937 void
938 ActivityView::_UpdateOffscreenBitmap()
939 {
940 	BRect frame = _HistoryFrame();
941 	frame.OffsetTo(B_ORIGIN);
942 
943 	if (fOffscreen != NULL && frame == fOffscreen->Bounds())
944 		return;
945 
946 	delete fOffscreen;
947 
948 	// create offscreen bitmap
949 
950 	fOffscreen = new(std::nothrow) BBitmap(frame, B_BITMAP_ACCEPTS_VIEWS,
951 		B_RGB32);
952 	if (fOffscreen == NULL || fOffscreen->InitCheck() != B_OK) {
953 		delete fOffscreen;
954 		fOffscreen = NULL;
955 		return;
956 	}
957 
958 	BView* view = new BView(frame, NULL, B_FOLLOW_NONE, B_SUBPIXEL_PRECISE);
959 	view->SetViewColor(fHistoryBackgroundColor);
960 	view->SetLowColor(view->ViewColor());
961 	fOffscreen->AddChild(view);
962 }
963 
964 
965 BView*
966 ActivityView::_OffscreenView()
967 {
968 	if (fOffscreen == NULL)
969 		return NULL;
970 
971 	return fOffscreen->ChildAt(0);
972 }
973 
974 
975 void
976 ActivityView::MouseDown(BPoint where)
977 {
978 	int32 buttons = B_SECONDARY_MOUSE_BUTTON;
979 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
980 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
981 
982 	if (buttons == B_PRIMARY_MOUSE_BUTTON) {
983 		fZoomPoint = where;
984 		fOriginalResolution = fDrawResolution;
985 		fZooming = true;
986 		SetMouseEventMask(B_POINTER_EVENTS);
987 		return;
988 	}
989 
990 	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
991 	menu->SetFont(be_plain_font);
992 
993 	BMenu* additionalMenu = new BMenu("Additional items");
994 	additionalMenu->SetFont(be_plain_font);
995 
996 	SystemInfo info;
997 	BMenuItem* item;
998 
999 	for (int32 i = 0; i < DataSource::CountSources(); i++) {
1000 		const DataSource* source = DataSource::SourceAt(i);
1001 
1002 		if (source->MultiCPUOnly() && info.CPUCount() == 1)
1003 			continue;
1004 
1005 		BMessage* message = new BMessage(kMsgToggleDataSource);
1006 		message->AddInt32("index", i);
1007 
1008 		item = new BMenuItem(source->Name(), message);
1009 		if (FindDataSource(source))
1010 			item->SetMarked(true);
1011 
1012 		if (source->Primary())
1013 			menu->AddItem(item);
1014 		else
1015 			additionalMenu->AddItem(item);
1016 	}
1017 
1018 	menu->AddItem(new BMenuItem(additionalMenu));
1019 	menu->AddSeparatorItem();
1020 	menu->AddItem(new BMenuItem(fShowLegend ? "Hide legend" : "Show legend",
1021 		new BMessage(kMsgToggleLegend)));
1022 
1023 	menu->SetTargetForItems(this);
1024 	additionalMenu->SetTargetForItems(this);
1025 
1026 	ActivityWindow* window = dynamic_cast<ActivityWindow*>(Window());
1027 	if (window != NULL && window->ActivityViewCount() > 1) {
1028 		menu->AddSeparatorItem();
1029 		BMessage* message = new BMessage(kMsgRemoveView);
1030 		message->AddPointer("view", this);
1031 		menu->AddItem(item = new BMenuItem("Remove graph", message));
1032 		item->SetTarget(window);
1033 	}
1034 
1035 	ConvertToScreen(&where);
1036 	menu->Go(where, true, false, true);
1037 }
1038 
1039 
1040 void
1041 ActivityView::MouseUp(BPoint where)
1042 {
1043 	fZooming = false;
1044 }
1045 
1046 
1047 void
1048 ActivityView::MouseMoved(BPoint where, uint32 transit,
1049 	const BMessage* dragMessage)
1050 {
1051 	if (!fZooming)
1052 		return;
1053 
1054 	int32 shift = int32(where.x - fZoomPoint.x) / 25;
1055 	int32 resolution;
1056 	if (shift > 0)
1057 		resolution = fOriginalResolution << shift;
1058 	else
1059 		resolution = fOriginalResolution >> -shift;
1060 
1061 	_UpdateResolution(resolution);
1062 }
1063 
1064 
1065 void
1066 ActivityView::MessageReceived(BMessage* message)
1067 {
1068 	// if a color is dropped, use it as background
1069 	if (message->WasDropped()) {
1070 		rgb_color* color;
1071 		ssize_t size;
1072 		if (message->FindData("RGBColor", B_RGB_COLOR_TYPE, 0,
1073 				(const void**)&color, &size) == B_OK
1074 			&& size == sizeof(rgb_color)) {
1075 			BPoint dropPoint = message->DropPoint();
1076 			ConvertFromScreen(&dropPoint);
1077 
1078 			if (_HistoryFrame().Contains(dropPoint)) {
1079 				fHistoryBackgroundColor = *color;
1080 				Invalidate(_HistoryFrame());
1081 			} else {
1082 				// check each legend color box
1083 				BAutolock _(fSourcesLock);
1084 
1085 				BRect legendFrame = _LegendFrame();
1086 				for (int32 i = 0; i < fSources.CountItems(); i++) {
1087 					BRect frame = _LegendColorFrameAt(legendFrame, i);
1088 					if (frame.Contains(dropPoint)) {
1089 						fSources.ItemAt(i)->SetColor(*color);
1090 						Invalidate(_HistoryFrame());
1091 						Invalidate(frame);
1092 						return;
1093 					}
1094 				}
1095 
1096 				if (dynamic_cast<ActivityMonitor*>(be_app) == NULL) {
1097 					// allow background color change in the replicant only
1098 					fLegendBackgroundColor = *color;
1099 					SetLowColor(fLegendBackgroundColor);
1100 					Invalidate(legendFrame);
1101 				}
1102 			}
1103 			return;
1104 		}
1105 	}
1106 
1107 	switch (message->what) {
1108 		case B_ABOUT_REQUESTED:
1109 			ActivityMonitor::ShowAbout();
1110 			break;
1111 
1112 		case B_NODE_MONITOR:
1113 		{
1114 			BString attrName;
1115 			if (message->FindString("attr", &attrName) == B_OK) {
1116 				if (attrName == kDesktopAttrName)
1117 					_LoadBackgroundInfo(false);
1118 			} else
1119 				_LoadBackgroundInfo(false);
1120 			break;
1121 		}
1122 
1123 		case kMsgUpdateResolution:
1124 		{
1125 			int32 resolution;
1126 			if (message->FindInt32("resolution", &resolution) != B_OK)
1127 				break;
1128 
1129 			_UpdateResolution(resolution, false);
1130 			break;
1131 		}
1132 
1133 		case kMsgTimeIntervalUpdated:
1134 			bigtime_t interval;
1135 			if (message->FindInt64("interval", &interval) != B_OK)
1136 				break;
1137 
1138 			if (interval < 10000)
1139 				interval = 10000;
1140 
1141 			atomic_set64(&fRefreshInterval, interval);
1142 			break;
1143 
1144 		case kMsgToggleDataSource:
1145 		{
1146 			int32 index;
1147 			if (message->FindInt32("index", &index) != B_OK)
1148 				break;
1149 
1150 			const DataSource* baseSource = DataSource::SourceAt(index);
1151 			if (baseSource == NULL)
1152 				break;
1153 
1154 			DataSource* source = FindDataSource(baseSource);
1155 			if (source == NULL)
1156 				AddDataSource(baseSource);
1157 			else
1158 				RemoveDataSource(baseSource);
1159 
1160 			Invalidate();
1161 			break;
1162 		}
1163 
1164 		case kMsgToggleLegend:
1165 			fShowLegend = !fShowLegend;
1166 			Invalidate();
1167 			break;
1168 
1169 		case B_MOUSE_WHEEL_CHANGED:
1170 		{
1171 			float deltaY = 0.0f;
1172 			if (message->FindFloat("be:wheel_delta_y", &deltaY) != B_OK
1173 				|| deltaY == 0.0f)
1174 				break;
1175 
1176 			int32 resolution = fDrawResolution;
1177 			if (deltaY > 0)
1178 				resolution *= 2;
1179 			else
1180 				resolution /= 2;
1181 
1182 			_UpdateResolution(resolution);
1183 			break;
1184 		}
1185 
1186 		default:
1187 			BView::MessageReceived(message);
1188 			break;
1189 	}
1190 }
1191 
1192 
1193 void
1194 ActivityView::_UpdateFrame()
1195 {
1196 #ifdef __HAIKU__
1197 	if (fLegendLayoutItem == NULL || fHistoryLayoutItem == NULL)
1198 		return;
1199 
1200 	BRect historyFrame = fHistoryLayoutItem->Frame();
1201 	BRect legendFrame = fLegendLayoutItem->Frame();
1202 #else
1203 	BRect historyFrame = Bounds();
1204 	BRect legendFrame = Bounds();
1205 	historyFrame.bottom -= 2 * Bounds().Height() / 3;
1206 	legendFrame.top += Bounds().Height() / 3;
1207 #endif
1208 	MoveTo(historyFrame.left, historyFrame.top);
1209 	ResizeTo(legendFrame.left + legendFrame.Width() - historyFrame.left,
1210 		legendFrame.top + legendFrame.Height() - historyFrame.top);
1211 }
1212 
1213 
1214 BRect
1215 ActivityView::_HistoryFrame() const
1216 {
1217 	BRect frame = Bounds();
1218 
1219 	if (fShowLegend) {
1220 		BRect legendFrame = _LegendFrame();
1221 		frame.bottom = legendFrame.top - 1;
1222 	}
1223 
1224 	frame.InsetBy(2, 2);
1225 
1226 	return frame;
1227 }
1228 
1229 
1230 float
1231 ActivityView::_LegendHeight() const
1232 {
1233 	font_height fontHeight;
1234 	GetFontHeight(&fontHeight);
1235 
1236 	BAutolock _(fSourcesLock);
1237 
1238 	int32 rows = (fSources.CountItems() + 1) / 2;
1239 
1240 	int32 boldMargin = Parent()
1241 		&& (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0 ? 2 : 0;
1242 
1243 	return rows * (4 + ceilf(fontHeight.ascent)
1244 		+ ceilf(fontHeight.descent) + ceilf(fontHeight.leading)) + boldMargin;
1245 }
1246 
1247 
1248 BRect
1249 ActivityView::_LegendFrame() const
1250 {
1251 	float height;
1252 #ifdef __HAIKU__
1253 	if (fLegendLayoutItem != NULL)
1254 		height = fLegendLayoutItem->Frame().Height();
1255 	else
1256 #endif
1257 		height = _LegendHeight();
1258 
1259 	BRect frame = Bounds();
1260 	frame.bottom -= kDraggerSize;
1261 	frame.top = frame.bottom - height;
1262 
1263 	return frame;
1264 }
1265 
1266 
1267 BRect
1268 ActivityView::_LegendFrameAt(BRect frame, int32 index) const
1269 {
1270 	int32 column = index & 1;
1271 	int32 row = index / 2;
1272 	if (column == 0)
1273 		frame.right = frame.left + floorf(frame.Width() / 2) - 5;
1274 	else
1275 		frame.left = frame.right - floorf(frame.Width() / 2) + 5;
1276 
1277 	BAutolock _(fSourcesLock);
1278 
1279 	int32 rows = (fSources.CountItems() + 1) / 2;
1280 	float height = floorf((frame.Height() - 5) / rows);
1281 
1282 	frame.top = frame.top + 5 + row * height;
1283 	frame.bottom = frame.top + height - 1;
1284 
1285 	return frame;
1286 }
1287 
1288 
1289 BRect
1290 ActivityView::_LegendColorFrameAt(BRect frame, int32 index) const
1291 {
1292 	frame = _LegendFrameAt(frame, index);
1293 	frame.InsetBy(1, 1);
1294 	frame.right = frame.left + frame.Height();
1295 
1296 	return frame;
1297 }
1298 
1299 
1300 float
1301 ActivityView::_PositionForValue(DataSource* source, DataHistory* values,
1302 	int64 value)
1303 {
1304 	int64 min = source->Minimum();
1305 	int64 max = source->Maximum();
1306 	if (source->AdaptiveScale()) {
1307 		int64 adaptiveMax = int64(values->MaximumValue() * 1.2);
1308 		if (adaptiveMax < max)
1309 			max = adaptiveMax;
1310 	}
1311 
1312 	if (value > max)
1313 		value = max;
1314 	if (value < min)
1315 		value = min;
1316 
1317 	float height = _HistoryFrame().Height();
1318 	return height - (value - min) * height / (max - min);
1319 }
1320 
1321 
1322 void
1323 ActivityView::_DrawHistory(bool drawBackground)
1324 {
1325 	_UpdateOffscreenBitmap();
1326 
1327 	BView* view = this;
1328 	if (fOffscreen != NULL) {
1329 		fOffscreen->Lock();
1330 		view = _OffscreenView();
1331 	}
1332 
1333 	BRect frame = _HistoryFrame();
1334 	BRect outerFrame = frame.InsetByCopy(-2, -2);
1335 
1336 	// draw the outer frame
1337 	uint32 flags = 0;
1338 	if (!drawBackground)
1339 		flags |= BControlLook::B_BLEND_FRAME;
1340 	be_control_look->DrawTextControlBorder(this, outerFrame,
1341 		outerFrame, fLegendBackgroundColor, flags);
1342 
1343 	// convert to offscreen view if necessary
1344 	if (view != this)
1345 		frame.OffsetTo(B_ORIGIN);
1346 
1347 	view->SetLowColor(fHistoryBackgroundColor);
1348 	view->FillRect(frame, B_SOLID_LOW);
1349 
1350 	uint32 step = 2;
1351 	uint32 resolution = fDrawResolution;
1352 	if (fDrawResolution > 1) {
1353 		step = 1;
1354 		resolution--;
1355 	}
1356 
1357 	uint32 width = frame.IntegerWidth() - 10;
1358 	uint32 steps = width / step;
1359 	bigtime_t timeStep = RefreshInterval() * resolution;
1360 	bigtime_t now = system_time();
1361 
1362 	// Draw scale
1363 	// TODO: add second markers?
1364 
1365 	view->SetPenSize(1);
1366 
1367 	rgb_color scaleColor = view->LowColor();
1368 	uint32 average = (scaleColor.red + scaleColor.green + scaleColor.blue) / 3;
1369 	if (average < 96)
1370 		scaleColor = tint_color(scaleColor, B_LIGHTEN_2_TINT);
1371 	else
1372 		scaleColor = tint_color(scaleColor, B_DARKEN_2_TINT);
1373 
1374 	view->SetHighColor(scaleColor);
1375 	view->StrokeLine(BPoint(frame.left, frame.top + frame.Height() / 2),
1376 		BPoint(frame.right, frame.top + frame.Height() / 2));
1377 
1378 	// Draw values
1379 
1380 	view->SetPenSize(1.5);
1381 	BAutolock _(fSourcesLock);
1382 
1383 	for (uint32 i = fSources.CountItems(); i-- > 0;) {
1384 		ViewHistory* viewValues = fViewValues.ItemAt(i);
1385 		DataSource* source = fSources.ItemAt(i);
1386 		DataHistory* values = fValues.ItemAt(i);
1387 
1388 		viewValues->Update(values, steps, fDrawResolution, now, timeStep,
1389 			RefreshInterval());
1390 
1391 		uint32 x = viewValues->Start() * step;
1392 		BShape shape;
1393 		bool first = true;
1394 
1395 		for (uint32 i = viewValues->Start(); i < steps; x += step, i++) {
1396 			float y = _PositionForValue(source, values,
1397 				viewValues->ValueAt(i));
1398 
1399 			if (first) {
1400 				shape.MoveTo(BPoint(x, y));
1401 				first = false;
1402 			} else
1403 				shape.LineTo(BPoint(x, y));
1404 		}
1405 
1406 		view->SetHighColor(source->Color());
1407 		view->SetLineMode(B_BUTT_CAP, B_ROUND_JOIN);
1408 		view->MovePenTo(B_ORIGIN);
1409 		view->StrokeShape(&shape);
1410 	}
1411 
1412 	// TODO: add marks when an app started or quit
1413 	view->Sync();
1414 	if (fOffscreen != NULL) {
1415 		fOffscreen->Unlock();
1416 		DrawBitmap(fOffscreen, outerFrame.LeftTop());
1417 	}
1418 }
1419 
1420 
1421 void
1422 ActivityView::_UpdateResolution(int32 resolution, bool broadcast)
1423 {
1424 	if (resolution < 1)
1425 		resolution = 1;
1426 	if (resolution > 128)
1427 		resolution = 128;
1428 
1429 	if (resolution == fDrawResolution)
1430 		return;
1431 
1432 	ActivityWindow* window = dynamic_cast<ActivityWindow*>(Window());
1433 	if (broadcast && window != NULL) {
1434 		BMessage update(kMsgUpdateResolution);
1435 		update.AddInt32("resolution", resolution);
1436 		window->BroadcastToActivityViews(&update, this);
1437 	}
1438 
1439 	fDrawResolution = resolution;
1440 	Invalidate();
1441 }
1442 
1443 
1444 void
1445 ActivityView::_LoadBackgroundInfo(bool watch)
1446 {
1447 	fCachedOutline = false;
1448 	fCachedWorkspace = -1;
1449 	BPath path;
1450 	if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) {
1451 		BNode desktopNode = BNode(path.Path());
1452 
1453 		attr_info info;
1454 		if (desktopNode.GetAttrInfo(kDesktopAttrName, &info) != B_OK)
1455 			return;
1456 
1457 		char* buffer = new char[info.size];
1458 		if (desktopNode.ReadAttr(kDesktopAttrName, B_MESSAGE_TYPE, 0,
1459 			buffer, (size_t)info.size) == info.size) {
1460 				BMessage message;
1461 				if (message.Unflatten(buffer) == B_OK)
1462 					fBackgroundInfo = message;
1463 		}
1464 		delete[] buffer;
1465 
1466 		if (watch) {
1467 			node_ref nref;
1468 			desktopNode.GetNodeRef(&nref);
1469 			watch_node(&nref, B_WATCH_ATTR, this);
1470 		}
1471 	}
1472 }
1473 
1474 
1475 void
1476 ActivityView::Draw(BRect updateRect)
1477 {
1478 	bool drawBackground = true;
1479 	if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN) != 0)
1480 		drawBackground = false;
1481 
1482 	_DrawHistory(drawBackground);
1483 
1484 	if (!fShowLegend)
1485 		return;
1486 
1487 	// draw legend
1488 	BRect legendFrame = _LegendFrame();
1489 	SetLowColor(fLegendBackgroundColor);
1490 	if (drawBackground) {
1491 		BRect backgroundFrame(legendFrame);
1492 		backgroundFrame.bottom += kDraggerSize;
1493 		FillRect(backgroundFrame, B_SOLID_LOW);
1494 	}
1495 
1496 	BAutolock _(fSourcesLock);
1497 
1498 	font_height fontHeight;
1499 	GetFontHeight(&fontHeight);
1500 
1501 	for (int32 i = 0; i < fSources.CountItems(); i++) {
1502 		DataSource* source = fSources.ItemAt(i);
1503 		DataHistory* values = fValues.ItemAt(i);
1504 		BRect frame = _LegendFrameAt(legendFrame, i);
1505 
1506 		// draw color box
1507 		BRect colorBox = _LegendColorFrameAt(legendFrame, i);
1508 		BRect rect = colorBox;
1509 		uint32 flags = BControlLook::B_BLEND_FRAME;
1510 		be_control_look->DrawTextControlBorder(this, rect,
1511 			rect, fLegendBackgroundColor, flags);
1512 		SetHighColor(source->Color());
1513 		FillRect(rect);
1514 
1515 		// show current value and label
1516 		float y = frame.top + ceilf(fontHeight.ascent);
1517 		int64 value = values->ValueAt(values->End());
1518 		BString text;
1519 		source->Print(text, value);
1520 		float width = StringWidth(text.String());
1521 
1522 		BString label = source->Label();
1523 		float possibleLabelWidth = frame.right - colorBox.right - 12 - width;
1524 		// TODO: TruncateString() is broken... remove + 5 when fixed!
1525 		if (ceilf(StringWidth(label.String()) + 5) > possibleLabelWidth)
1526 			label = source->ShortLabel();
1527 		TruncateString(&label, B_TRUNCATE_MIDDLE, possibleLabelWidth);
1528 
1529 		if (drawBackground)
1530 			SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
1531 		else {
1532 			rgb_color c = Parent()->ViewColor();
1533 			rgb_color textColor = c.red + c.green * 1.5f + c.blue * 0.50f
1534 				>= 300 ? kBlack : kWhite;
1535 
1536 			int32 mask;
1537 			bool tmpOutline = false;
1538 			bool outline = fCachedOutline;
1539 			int8 indice = 0;
1540 
1541 			if (fCachedWorkspace != current_workspace()) {
1542 				while (fBackgroundInfo.FindInt32("be:bgndimginfoworkspaces",
1543 						indice, &mask) == B_OK
1544 					&& fBackgroundInfo.FindBool("be:bgndimginfoerasetext",
1545 						indice, &tmpOutline) == B_OK) {
1546 					if (((1 << current_workspace()) & mask) != 0) {
1547 						outline = tmpOutline;
1548 						fCachedWorkspace = current_workspace();
1549 						fCachedOutline = outline;
1550 						break;
1551 					}
1552 					indice++;
1553 				}
1554 			}
1555 
1556 			if (outline) {
1557 				SetDrawingMode(B_OP_ALPHA);
1558 				SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
1559 
1560 				BFont font;
1561 				GetFont(&font);
1562 				if (textColor == kBlack) {
1563 					// Black text with white halo/glow
1564 					rgb_color glowColor = kWhite;
1565 
1566 					font.SetFalseBoldWidth(2.0);
1567 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1568 
1569 					glowColor.alpha = 30;
1570 					SetHighColor(glowColor);
1571 					DrawString(label.String(), BPoint(6 + colorBox.right, y));
1572 					DrawString(text.String(), BPoint(frame.right - width, y));
1573 
1574 					font.SetFalseBoldWidth(1.0);
1575 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1576 
1577 					glowColor.alpha = 65;
1578 					SetHighColor(glowColor);
1579 					DrawString(label.String(), BPoint(6 + colorBox.right, y));
1580 					DrawString(text.String(), BPoint(frame.right - width, y));
1581 
1582 					font.SetFalseBoldWidth(0.0);
1583 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1584 				} else {
1585 					// white text with black outline
1586 					rgb_color outlineColor = kBlack;
1587 
1588 					font.SetFalseBoldWidth(1.0);
1589 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1590 
1591 					outlineColor.alpha = 30;
1592 					SetHighColor(outlineColor);
1593 					DrawString(label.String(), BPoint(6 + colorBox.right, y));
1594 					DrawString(text.String(), BPoint(frame.right - width, y));
1595 
1596 					font.SetFalseBoldWidth(0.0);
1597 					SetFont(&font, B_FONT_FALSE_BOLD_WIDTH);
1598 
1599 					outlineColor.alpha = 200;
1600 					SetHighColor(outlineColor);
1601 					DrawString(label.String(), BPoint(6 + colorBox.right + 1,
1602 						y + 1));
1603 					DrawString(text.String(), BPoint(frame.right - width + 1,
1604 						y + 1));
1605 				}
1606 			}
1607 			SetDrawingMode(B_OP_OVER);
1608 			SetHighColor(textColor);
1609 		}
1610 		DrawString(label.String(), BPoint(6 + colorBox.right, y));
1611 		DrawString(text.String(), BPoint(frame.right - width, y));
1612 	}
1613 }
1614 
1615 
1616 void
1617 ActivityView::_Refresh()
1618 {
1619 	bigtime_t lastTimeout = system_time() - RefreshInterval();
1620 	BMessenger target(this);
1621 
1622 	while (true) {
1623 		status_t status = acquire_sem_etc(fRefreshSem, 1, B_ABSOLUTE_TIMEOUT,
1624 			lastTimeout + RefreshInterval());
1625 		if (status == B_OK || status == B_BAD_SEM_ID)
1626 			break;
1627 		if (status == B_INTERRUPTED)
1628 			continue;
1629 
1630 		SystemInfo info(fSystemInfoHandler);
1631 		lastTimeout += RefreshInterval();
1632 
1633 		fSourcesLock.Lock();
1634 
1635 		for (uint32 i = fSources.CountItems(); i-- > 0;) {
1636 			DataSource* source = fSources.ItemAt(i);
1637 			DataHistory* values = fValues.ItemAt(i);
1638 
1639 			int64 value = source->NextValue(info);
1640 			values->AddValue(info.Time(), value);
1641 		}
1642 
1643 		fSourcesLock.Unlock();
1644 
1645 		target.SendMessage(B_INVALIDATE);
1646 	}
1647 }
1648 
1649 
1650 /*static*/ status_t
1651 ActivityView::_RefreshThread(void* self)
1652 {
1653 	((ActivityView*)self)->_Refresh();
1654 	return B_OK;
1655 }
1656