2 * Copyright 2008-2015, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
7 #include "ActivityView.h"
15 # include <AboutWindow.h>
16 # include <AbstractLayoutItem.h>
17 # include <ControlLook.h>
19 #include <Application.h>
25 #include <MessageRunner.h>
26 #include <PopUpMenu.h>
30 #include "ActivityMonitor.h"
31 #include "ActivityWindow.h"
32 #include "SettingsWindow.h"
33 #include "SystemInfo.h"
34 #include "SystemInfoHandler.h"
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "ActivityView"
39 template<typename ObjectType
>
40 class ListAddDeleter
{
42 ListAddDeleter(BObjectList
<ObjectType
>& list
, ObjectType
* object
,
48 if (fObject
!= NULL
&& !fList
.AddItem(fObject
, spot
)) {
56 if (fObject
!= NULL
) {
57 fList
.RemoveItem(fObject
);
64 return fObject
== NULL
;
73 BObjectList
<ObjectType
>& fList
;
78 /*! This class manages the scale of a history with a dynamic scale.
79 Every history value will be input via Update(), and the minimum/maximum
80 is computed from that.
84 Scale(scale_type type
);
86 int64
MinimumValue() const { return fMinimumValue
; }
87 int64
MaximumValue() const { return fMaximumValue
; }
89 void Update(int64 value
);
98 /*! Stores the interpolated on screen view values. This is done so that the
99 interpolation is fixed, and does not change when being scrolled.
101 We could also just do this by making sure we always ask for the same
102 interval only, but this way we also save the interpolation.
108 int64
ValueAt(int32 x
);
111 { return fValues
.Size()
112 - fValues
.CountItems(); }
114 void Update(DataHistory
* history
, int32 width
,
115 int32 resolution
, bigtime_t toTime
,
116 bigtime_t step
, bigtime_t refresh
);
119 CircularBuffer
<int64
> fValues
;
131 class ActivityView::HistoryLayoutItem
: public BAbstractLayoutItem
{
133 HistoryLayoutItem(ActivityView
* parent
);
135 virtual bool IsVisible();
136 virtual void SetVisible(bool visible
);
138 virtual BRect
Frame();
139 virtual void SetFrame(BRect frame
);
141 virtual BView
* View();
143 virtual BSize
BasePreferredSize();
146 ActivityView
* fParent
;
150 class ActivityView::LegendLayoutItem
: public BAbstractLayoutItem
{
152 LegendLayoutItem(ActivityView
* parent
);
154 virtual bool IsVisible();
155 virtual void SetVisible(bool visible
);
157 virtual BRect
Frame();
158 virtual void SetFrame(BRect frame
);
160 virtual BView
* View();
162 virtual BSize
BaseMinSize();
163 virtual BSize
BaseMaxSize();
164 virtual BSize
BasePreferredSize();
165 virtual BAlignment
BaseAlignment();
168 ActivityView
* fParent
;
173 const bigtime_t kInitialRefreshInterval
= 250000LL;
175 const uint32 kMsgToggleDataSource
= 'tgds';
176 const uint32 kMsgToggleLegend
= 'tglg';
177 const uint32 kMsgUpdateResolution
= 'ures';
179 extern const char* kAppName
;
180 extern const char* kSignature
;
183 Scale::Scale(scale_type type
)
194 Scale::Update(int64 value
)
196 if (!fInitialized
|| fMinimumValue
> value
)
197 fMinimumValue
= value
;
198 if (!fInitialized
|| fMaximumValue
< value
)
199 fMaximumValue
= value
;
208 ViewHistory::ViewHistory()
219 ViewHistory::ValueAt(int32 x
)
221 int64
* value
= fValues
.ItemAt(x
- Start());
230 ViewHistory::Update(DataHistory
* history
, int32 width
, int32 resolution
,
231 bigtime_t toTime
, bigtime_t step
, bigtime_t refresh
)
234 // ignore this - it seems the view hasn't been layouted yet
238 // Check if we need to invalidate the existing values
239 if ((int32
)fValues
.Size() != width
240 || fResolution
!= resolution
241 || fRefresh
!= refresh
) {
242 fValues
.SetSize(width
);
243 fResolution
= resolution
;
248 // Compute how many new values we need to retrieve
249 if (fLastTime
< history
->Start())
250 fLastTime
= history
->Start();
251 if (fLastTime
> history
->End())
254 int32 updateWidth
= int32((toTime
- fLastTime
) / step
);
258 if (updateWidth
> (int32
)fValues
.Size()) {
259 updateWidth
= fValues
.Size();
260 fLastTime
= toTime
- updateWidth
* step
;
263 for (int32 i
= 0; i
< updateWidth
; i
++) {
264 int64 value
= history
->ValueAt(fLastTime
+= step
);
266 if (step
> refresh
) {
268 for (bigtime_t offset
= refresh
; offset
< step
; offset
+= refresh
) {
269 // TODO: handle int64 overflow correctly!
270 value
+= history
->ValueAt(fLastTime
+ offset
);
276 fValues
.AddItem(value
);
284 DataHistory::DataHistory(bigtime_t memorize
, bigtime_t interval
)
289 fRefreshInterval(interval
),
296 DataHistory::~DataHistory()
302 DataHistory::AddValue(bigtime_t time
, int64 value
)
304 if (fBuffer
.IsEmpty() || fMaximumValue
< value
)
305 fMaximumValue
= value
;
306 if (fBuffer
.IsEmpty() || fMinimumValue
> value
)
307 fMinimumValue
= value
;
309 fScale
->Update(value
);
311 data_item item
= {time
, value
};
312 fBuffer
.AddItem(item
);
317 DataHistory::ValueAt(bigtime_t time
)
320 int32 right
= fBuffer
.CountItems() - 1;
321 data_item
* item
= NULL
;
323 while (left
<= right
) {
324 int32 index
= (left
+ right
) / 2;
325 item
= fBuffer
.ItemAt(index
);
327 if (item
->time
> time
) {
328 // search in left part
331 data_item
* nextItem
= fBuffer
.ItemAt(index
+ 1);
332 if (nextItem
== NULL
)
334 if (nextItem
->time
> time
) {
336 int64 value
= item
->value
;
337 value
+= int64(double(nextItem
->value
- value
)
338 / (nextItem
->time
- item
->time
) * (time
- item
->time
));
342 // search in right part
352 DataHistory::MaximumValue() const
355 return fScale
->MaximumValue();
357 return fMaximumValue
;
362 DataHistory::MinimumValue() const
365 return fScale
->MinimumValue();
367 return fMinimumValue
;
372 DataHistory::Start() const
374 if (fBuffer
.CountItems() == 0)
377 return fBuffer
.ItemAt(0)->time
;
382 DataHistory::End() const
384 if (fBuffer
.CountItems() == 0)
387 return fBuffer
.ItemAt(fBuffer
.CountItems() - 1)->time
;
392 DataHistory::SetRefreshInterval(bigtime_t interval
)
394 // TODO: adjust buffer size
399 DataHistory::SetScale(Scale
* scale
)
409 ActivityView::HistoryLayoutItem::HistoryLayoutItem(ActivityView
* parent
)
418 ActivityView::HistoryLayoutItem::IsVisible()
420 return !fParent
->IsHidden(fParent
);
425 ActivityView::HistoryLayoutItem::SetVisible(bool visible
)
432 ActivityView::HistoryLayoutItem::Frame()
439 ActivityView::HistoryLayoutItem::SetFrame(BRect frame
)
442 fParent
->_UpdateFrame();
447 ActivityView::HistoryLayoutItem::View()
454 ActivityView::HistoryLayoutItem::BasePreferredSize()
456 BSize
size(BaseMaxSize());
464 ActivityView::LegendLayoutItem::LegendLayoutItem(ActivityView
* parent
)
473 ActivityView::LegendLayoutItem::IsVisible()
475 return !fParent
->IsHidden(fParent
);
480 ActivityView::LegendLayoutItem::SetVisible(bool visible
)
487 ActivityView::LegendLayoutItem::Frame()
494 ActivityView::LegendLayoutItem::SetFrame(BRect frame
)
497 fParent
->_UpdateFrame();
502 ActivityView::LegendLayoutItem::View()
509 ActivityView::LegendLayoutItem::BaseMinSize()
511 // TODO: Cache the info. Might be too expensive for this call.
514 size
.height
= fParent
->_LegendHeight();
521 ActivityView::LegendLayoutItem::BaseMaxSize()
523 BSize
size(BaseMinSize());
524 size
.width
= B_SIZE_UNLIMITED
;
530 ActivityView::LegendLayoutItem::BasePreferredSize()
532 BSize
size(BaseMinSize());
538 ActivityView::LegendLayoutItem::BaseAlignment()
540 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
548 const rgb_color kWhite
= (rgb_color
){255, 255, 255, 255};
549 const rgb_color kBlack
= (rgb_color
){0, 0, 0, 255};
550 const float kDraggerSize
= 7;
553 ActivityView::ActivityView(BRect frame
, const char* name
,
554 const BMessage
* settings
, uint32 resizingMode
)
555 : BView(frame
, name
, resizingMode
,
556 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS
),
557 fSourcesLock("data sources")
561 BRect
rect(Bounds());
562 rect
.top
= rect
.bottom
- kDraggerSize
;
563 rect
.left
= rect
.right
- kDraggerSize
;
564 BDragger
* dragger
= new BDragger(rect
, this,
565 B_FOLLOW_RIGHT
| B_FOLLOW_BOTTOM
);
570 ActivityView::ActivityView(const char* name
, const BMessage
* settings
)
572 : BView(name
, B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS
),
574 : BView(BRect(0, 0, 300, 200), name
, B_FOLLOW_NONE
,
575 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_FRAME_EVENTS
),
577 fSourcesLock("data sources")
579 SetLowUIColor(B_PANEL_BACKGROUND_COLOR
);
583 BRect
rect(Bounds());
584 rect
.top
= rect
.bottom
- kDraggerSize
;
585 rect
.left
= rect
.right
- kDraggerSize
;
586 BDragger
* dragger
= new BDragger(rect
, this,
587 B_FOLLOW_RIGHT
| B_FOLLOW_BOTTOM
);
592 ActivityView::ActivityView(BMessage
* archive
)
599 ActivityView::~ActivityView()
602 delete fSystemInfoHandler
;
607 ActivityView::_Init(const BMessage
* settings
)
609 fHistoryBackgroundColor
= (rgb_color
){255, 255, 240};
610 fLegendBackgroundColor
= LowColor();
611 // the low color is restored by the BView unarchiving
614 fHistoryLayoutItem
= NULL
;
615 fLegendLayoutItem
= NULL
;
617 SetViewColor(B_TRANSPARENT_COLOR
);
623 fSystemInfoHandler
= new SystemInfoHandler
;
626 || settings
->FindInt64("refresh interval", &fRefreshInterval
) != B_OK
)
627 fRefreshInterval
= kInitialRefreshInterval
;
630 || settings
->FindBool("show legend", &fShowLegend
) != B_OK
)
633 if (settings
== NULL
)
638 if (settings
->FindData("history background color", B_RGB_COLOR_TYPE
,
639 (const void **)&color
, &colorLength
) == B_OK
640 && colorLength
== sizeof(rgb_color
))
641 fHistoryBackgroundColor
= *color
;
644 for (int32 i
= 0; settings
->FindString("source", i
, &name
) == B_OK
; i
++)
645 AddDataSource(DataSource::FindSource(name
), settings
);
650 ActivityView::Archive(BMessage
* into
, bool deep
) const
654 status
= BView::Archive(into
, deep
);
658 status
= into
->AddString("add_on", kSignature
);
662 status
= SaveState(*into
);
671 ActivityView::Instantiate(BMessage
* archive
)
673 if (!validate_instantiation(archive
, "ActivityView"))
676 return new ActivityView(archive
);
681 ActivityView::SaveState(BMessage
& state
) const
683 status_t status
= state
.AddBool("show legend", fShowLegend
);
687 status
= state
.AddInt64("refresh interval", fRefreshInterval
);
691 status
= state
.AddData("history background color", B_RGB_COLOR_TYPE
,
692 &fHistoryBackgroundColor
, sizeof(rgb_color
));
696 for (int32 i
= 0; i
< fSources
.CountItems(); i
++) {
697 DataSource
* source
= fSources
.ItemAt(i
);
699 if (!source
->PerCPU() || source
->CPU() == 0)
700 status
= state
.AddString("source", source
->InternalName());
704 BString name
= source
->Name();
706 rgb_color color
= source
->Color();
707 state
.AddData(name
.String(), B_RGB_COLOR_TYPE
, &color
,
715 ActivityView::_ScaleFor(scale_type type
)
717 if (type
== kNoScale
)
720 std::map
<scale_type
, ::Scale
*>::iterator iterator
= fScales
.find(type
);
721 if (iterator
!= fScales
.end())
722 return iterator
->second
;
725 ::Scale
* scale
= new ::Scale(type
);
726 fScales
[type
] = scale
;
734 ActivityView::CreateHistoryLayoutItem()
736 if (fHistoryLayoutItem
== NULL
)
737 fHistoryLayoutItem
= new HistoryLayoutItem(this);
739 return fHistoryLayoutItem
;
744 ActivityView::CreateLegendLayoutItem()
746 if (fLegendLayoutItem
== NULL
)
747 fLegendLayoutItem
= new LegendLayoutItem(this);
749 return fLegendLayoutItem
;
755 ActivityView::FindDataSource(const DataSource
* search
)
757 BAutolock
_(fSourcesLock
);
759 for (int32 i
= fSources
.CountItems(); i
-- > 0;) {
760 DataSource
* source
= fSources
.ItemAt(i
);
761 if (!strcmp(source
->Name(), search
->Name()))
770 ActivityView::AddDataSource(const DataSource
* source
, const BMessage
* state
)
775 BAutolock
_(fSourcesLock
);
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
) {
786 if (insert
> fSources
.CountItems())
787 insert
= fSources
.CountItems();
789 // Generate DataHistory and ViewHistory objects for the source
790 // (one might need one history per CPU)
793 if (source
->PerCPU()) {
795 count
= info
.CPUCount();
798 for (uint32 i
= 0; i
< count
; i
++) {
799 DataHistory
* values
= new(std::nothrow
) DataHistory(10 * 60000000LL,
801 ListAddDeleter
<DataHistory
> valuesDeleter(fValues
, values
, insert
);
803 ViewHistory
* viewValues
= new(std::nothrow
) ViewHistory
;
804 ListAddDeleter
<ViewHistory
> viewValuesDeleter(fViewValues
, viewValues
,
807 if (valuesDeleter
.Failed() || viewValuesDeleter
.Failed())
810 values
->SetScale(_ScaleFor(source
->ScaleType()));
813 if (source
->PerCPU())
814 copy
= source
->CopyForCPU(i
);
816 copy
= source
->Copy();
818 ListAddDeleter
<DataSource
> sourceDeleter(fSources
, copy
, insert
);
819 if (sourceDeleter
.Failed())
822 BString colorName
= source
->Name();
823 colorName
<< " color";
825 const rgb_color
* color
= NULL
;
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
);
833 valuesDeleter
.Detach();
834 viewValuesDeleter
.Detach();
835 sourceDeleter
.Detach();
847 ActivityView::RemoveDataSource(const DataSource
* remove
)
849 bool removed
= false;
851 BAutolock
_(fSourcesLock
);
854 DataSource
* source
= FindDataSource(remove
);
855 if (source
== NULL
) {
858 return B_ENTRY_NOT_FOUND
;
861 int32 index
= fSources
.IndexOf(source
);
863 return B_ENTRY_NOT_FOUND
;
865 fSources
.RemoveItemAt(index
);
867 DataHistory
* values
= fValues
.RemoveItemAt(index
);
880 ActivityView::RemoveAllDataSources()
882 BAutolock
_(fSourcesLock
);
884 fSources
.MakeEmpty();
890 ActivityView::AttachedToWindow()
892 Looper()->AddHandler(fSystemInfoHandler
);
893 fSystemInfoHandler
->StartWatching();
895 fRefreshSem
= create_sem(0, "refresh sem");
896 fRefreshThread
= spawn_thread(&_RefreshThread
, "source refresh",
897 B_URGENT_DISPLAY_PRIORITY
, this);
898 resume_thread(fRefreshThread
);
900 FrameResized(Bounds().Width(), Bounds().Height());
905 ActivityView::DetachedFromWindow()
907 fSystemInfoHandler
->StopWatching();
908 Looper()->RemoveHandler(fSystemInfoHandler
);
910 delete_sem(fRefreshSem
);
911 wait_for_thread(fRefreshThread
, NULL
);
917 ActivityView::MinSize()
921 size
.height
= _LegendHeight();
929 ActivityView::FrameResized(float /*width*/, float /*height*/)
931 _UpdateOffscreenBitmap();
936 ActivityView::_UpdateOffscreenBitmap()
938 BRect frame
= _HistoryFrame();
939 frame
.OffsetTo(B_ORIGIN
);
941 if (fOffscreen
!= NULL
&& frame
== fOffscreen
->Bounds())
946 // create offscreen bitmap
948 fOffscreen
= new(std::nothrow
) BBitmap(frame
, B_BITMAP_ACCEPTS_VIEWS
,
950 if (fOffscreen
== NULL
|| fOffscreen
->InitCheck() != B_OK
) {
956 BView
* view
= new BView(frame
, NULL
, B_FOLLOW_NONE
, B_SUBPIXEL_PRECISE
);
957 view
->SetViewColor(fHistoryBackgroundColor
);
958 view
->SetLowColor(view
->ViewColor());
959 fOffscreen
->AddChild(view
);
964 ActivityView::_OffscreenView()
966 if (fOffscreen
== NULL
)
969 return fOffscreen
->ChildAt(0);
974 ActivityView::MouseDown(BPoint where
)
976 int32 buttons
= B_SECONDARY_MOUSE_BUTTON
;
977 if (Looper() != NULL
&& Looper()->CurrentMessage() != NULL
)
978 Looper()->CurrentMessage()->FindInt32("buttons", &buttons
);
980 if (buttons
== B_PRIMARY_MOUSE_BUTTON
) {
982 fOriginalResolution
= fDrawResolution
;
984 SetMouseEventMask(B_POINTER_EVENTS
);
988 BPopUpMenu
*menu
= new BPopUpMenu(B_EMPTY_STRING
, false, false);
989 menu
->SetFont(be_plain_font
);
991 BMenu
* additionalMenu
= new BMenu(B_TRANSLATE("Additional items"));
992 additionalMenu
->SetFont(be_plain_font
);
997 for (int32 i
= 0; i
< DataSource::CountSources(); i
++) {
998 const DataSource
* source
= DataSource::SourceAt(i
);
1000 if (source
->MultiCPUOnly() && info
.CPUCount() == 1)
1003 BMessage
* message
= new BMessage(kMsgToggleDataSource
);
1004 message
->AddInt32("index", i
);
1006 item
= new BMenuItem(source
->Name(), message
);
1007 if (FindDataSource(source
))
1008 item
->SetMarked(true);
1010 if (source
->Primary())
1011 menu
->AddItem(item
);
1013 additionalMenu
->AddItem(item
);
1016 menu
->AddItem(new BMenuItem(additionalMenu
));
1017 menu
->AddSeparatorItem();
1018 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Show legend"),
1019 new BMessage(kMsgToggleLegend
)));
1020 item
->SetMarked(fShowLegend
);
1022 menu
->SetTargetForItems(this);
1023 additionalMenu
->SetTargetForItems(this);
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(B_TRANSLATE("Remove graph"),
1032 item
->SetTarget(window
);
1035 ConvertToScreen(&where
);
1036 menu
->Go(where
, true, false, true);
1041 ActivityView::MouseUp(BPoint where
)
1048 ActivityView::MouseMoved(BPoint where
, uint32 transit
,
1049 const BMessage
* dragMessage
)
1054 int32 shift
= int32(where
.x
- fZoomPoint
.x
) / 25;
1057 resolution
= fOriginalResolution
<< shift
;
1059 resolution
= fOriginalResolution
>> -shift
;
1061 _UpdateResolution(resolution
);
1066 ActivityView::MessageReceived(BMessage
* message
)
1068 // if a color is dropped, use it as background
1069 if (message
->WasDropped()) {
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
);
1078 if (_HistoryFrame().Contains(dropPoint
)) {
1079 fHistoryBackgroundColor
= *color
;
1080 Invalidate(_HistoryFrame());
1082 // check each legend color box
1083 BAutolock
_(fSourcesLock
);
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());
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
);
1107 switch (message
->what
) {
1108 case B_ABOUT_REQUESTED
:
1110 BAboutWindow
* window
= new BAboutWindow(kAppName
, kSignature
);
1112 const char* authors
[] = {
1117 window
->AddCopyright(2008, "Haiku, Inc.");
1118 window
->AddAuthors(authors
);
1124 case kMsgUpdateResolution
:
1127 if (message
->FindInt32("resolution", &resolution
) != B_OK
)
1130 _UpdateResolution(resolution
, false);
1134 case kMsgTimeIntervalUpdated
:
1136 if (message
->FindInt64("interval", &interval
) != B_OK
)
1139 if (interval
< 10000)
1142 atomic_set64(&fRefreshInterval
, interval
);
1145 case kMsgToggleDataSource
:
1148 if (message
->FindInt32("index", &index
) != B_OK
)
1151 const DataSource
* baseSource
= DataSource::SourceAt(index
);
1152 if (baseSource
== NULL
)
1155 DataSource
* source
= FindDataSource(baseSource
);
1157 AddDataSource(baseSource
);
1159 RemoveDataSource(baseSource
);
1165 case kMsgToggleLegend
:
1166 fShowLegend
= !fShowLegend
;
1170 case B_MOUSE_WHEEL_CHANGED
:
1172 float deltaY
= 0.0f
;
1173 if (message
->FindFloat("be:wheel_delta_y", &deltaY
) != B_OK
1177 int32 resolution
= fDrawResolution
;
1183 _UpdateResolution(resolution
);
1188 BView::MessageReceived(message
);
1195 ActivityView::_UpdateFrame()
1198 if (fLegendLayoutItem
== NULL
|| fHistoryLayoutItem
== NULL
)
1201 BRect historyFrame
= fHistoryLayoutItem
->Frame();
1202 BRect legendFrame
= fLegendLayoutItem
->Frame();
1204 BRect historyFrame
= Bounds();
1205 BRect legendFrame
= Bounds();
1206 historyFrame
.bottom
-= 2 * Bounds().Height() / 3;
1207 legendFrame
.top
+= Bounds().Height() / 3;
1209 MoveTo(historyFrame
.left
, historyFrame
.top
);
1210 ResizeTo(legendFrame
.left
+ legendFrame
.Width() - historyFrame
.left
,
1211 legendFrame
.top
+ legendFrame
.Height() - historyFrame
.top
);
1216 ActivityView::_HistoryFrame() const
1218 BRect frame
= Bounds();
1221 BRect legendFrame
= _LegendFrame();
1222 frame
.bottom
= legendFrame
.top
- 1;
1225 frame
.InsetBy(2, 2);
1232 ActivityView::_LegendHeight() const
1234 font_height fontHeight
;
1235 GetFontHeight(&fontHeight
);
1237 BAutolock
_(fSourcesLock
);
1239 int32 rows
= (fSources
.CountItems() + 1) / 2;
1241 int32 boldMargin
= Parent()
1242 && (Parent()->Flags() & B_DRAW_ON_CHILDREN
) != 0 ? 2 : 0;
1244 return rows
* (4 + ceilf(fontHeight
.ascent
)
1245 + ceilf(fontHeight
.descent
) + ceilf(fontHeight
.leading
)) + boldMargin
;
1250 ActivityView::_LegendFrame() const
1254 if (fLegendLayoutItem
!= NULL
)
1255 height
= fLegendLayoutItem
->Frame().Height();
1258 height
= _LegendHeight();
1260 BRect frame
= Bounds();
1261 frame
.bottom
-= kDraggerSize
;
1262 frame
.top
= frame
.bottom
- height
;
1269 ActivityView::_LegendFrameAt(BRect frame
, int32 index
) const
1271 int32 column
= index
& 1;
1272 int32 row
= index
/ 2;
1274 // Use the full width if there is only one item
1275 if (fSources
.CountItems() != 1)
1276 frame
.right
= frame
.left
+ floorf(frame
.Width() / 2) - 5;
1278 frame
.left
= frame
.right
- floorf(frame
.Width() / 2) + 5;
1280 BAutolock
_(fSourcesLock
);
1282 int32 rows
= (fSources
.CountItems() + 1) / 2;
1283 float height
= floorf((frame
.Height() - 5) / rows
);
1285 frame
.top
= frame
.top
+ 5 + row
* height
;
1286 frame
.bottom
= frame
.top
+ height
- 1;
1293 ActivityView::_LegendColorFrameAt(BRect frame
, int32 index
) const
1295 frame
= _LegendFrameAt(frame
, index
);
1296 frame
.InsetBy(1, 1);
1297 frame
.right
= frame
.left
+ frame
.Height();
1304 ActivityView::_PositionForValue(DataSource
* source
, DataHistory
* values
,
1307 int64 min
= source
->Minimum();
1308 int64 max
= source
->Maximum();
1309 if (source
->AdaptiveScale()) {
1310 int64 adaptiveMax
= int64(values
->MaximumValue() * 1.2);
1311 if (adaptiveMax
< max
)
1320 float height
= _HistoryFrame().Height();
1321 return height
- (value
- min
) * height
/ (max
- min
);
1326 ActivityView::_DrawHistory(bool drawBackground
)
1328 _UpdateOffscreenBitmap();
1331 if (fOffscreen
!= NULL
) {
1333 view
= _OffscreenView();
1336 BRect frame
= _HistoryFrame();
1337 BRect outerFrame
= frame
.InsetByCopy(-2, -2);
1339 // draw the outer frame
1341 if (!drawBackground
)
1342 flags
|= BControlLook::B_BLEND_FRAME
;
1343 be_control_look
->DrawTextControlBorder(this, outerFrame
,
1344 outerFrame
, fLegendBackgroundColor
, flags
);
1346 // convert to offscreen view if necessary
1348 frame
.OffsetTo(B_ORIGIN
);
1350 view
->SetLowColor(fHistoryBackgroundColor
);
1351 view
->FillRect(frame
, B_SOLID_LOW
);
1354 uint32 resolution
= fDrawResolution
;
1355 if (fDrawResolution
> 1) {
1360 // We would get a negative number of steps which isn't a good idea.
1361 if (frame
.IntegerWidth() <= 10)
1364 uint32 width
= frame
.IntegerWidth() - 10;
1365 uint32 steps
= width
/ step
;
1366 bigtime_t timeStep
= RefreshInterval() * resolution
;
1367 bigtime_t now
= system_time();
1370 // TODO: add second markers?
1372 view
->SetPenSize(1);
1374 rgb_color scaleColor
= view
->LowColor();
1375 uint32 average
= (scaleColor
.red
+ scaleColor
.green
+ scaleColor
.blue
) / 3;
1377 scaleColor
= tint_color(scaleColor
, B_LIGHTEN_2_TINT
);
1379 scaleColor
= tint_color(scaleColor
, B_DARKEN_2_TINT
);
1381 view
->SetHighColor(scaleColor
);
1382 view
->StrokeLine(BPoint(frame
.left
, frame
.top
+ frame
.Height() / 2),
1383 BPoint(frame
.right
, frame
.top
+ frame
.Height() / 2));
1387 view
->SetPenSize(1.5);
1388 BAutolock
_(fSourcesLock
);
1390 for (uint32 i
= fSources
.CountItems(); i
-- > 0;) {
1391 ViewHistory
* viewValues
= fViewValues
.ItemAt(i
);
1392 DataSource
* source
= fSources
.ItemAt(i
);
1393 DataHistory
* values
= fValues
.ItemAt(i
);
1395 viewValues
->Update(values
, steps
, fDrawResolution
, now
, timeStep
,
1398 if (viewValues
->Start() >= (int32
)steps
- 1)
1401 uint32 x
= viewValues
->Start() * step
;
1405 view
->SetHighColor(source
->Color());
1406 view
->SetLineMode(B_BUTT_CAP
, B_ROUND_JOIN
);
1407 view
->MovePenTo(B_ORIGIN
);
1410 view
->BeginLineArray(steps
- viewValues
->Start() - 1);
1414 for (uint32 j
= viewValues
->Start(); j
< steps
; x
+= step
, j
++) {
1415 float y
= _PositionForValue(source
, values
,
1416 viewValues
->ValueAt(j
));
1421 view
->AddLine(prev
, BPoint(x
, y
), source
->Color());
1426 } catch (std::bad_alloc
) {
1427 // Not enough memory to allocate the line array.
1428 // TODO we could try to draw using the slower but less memory
1429 // consuming solution using StrokeLine.
1432 view
->EndLineArray();
1435 // TODO: add marks when an app started or quit
1437 if (fOffscreen
!= NULL
) {
1438 fOffscreen
->Unlock();
1439 DrawBitmap(fOffscreen
, outerFrame
.LeftTop());
1445 ActivityView::_UpdateResolution(int32 resolution
, bool broadcast
)
1449 if (resolution
> 128)
1452 if (resolution
== fDrawResolution
)
1455 ActivityWindow
* window
= dynamic_cast<ActivityWindow
*>(Window());
1456 if (broadcast
&& window
!= NULL
) {
1457 BMessage
update(kMsgUpdateResolution
);
1458 update
.AddInt32("resolution", resolution
);
1459 window
->BroadcastToActivityViews(&update
, this);
1462 fDrawResolution
= resolution
;
1468 ActivityView::Draw(BRect updateRect
)
1470 bool drawBackground
= true;
1471 if (Parent() && (Parent()->Flags() & B_DRAW_ON_CHILDREN
) != 0)
1472 drawBackground
= false;
1474 _DrawHistory(drawBackground
);
1480 BRect legendFrame
= _LegendFrame();
1481 if (LowUIColor() == B_NO_COLOR
)
1482 SetLowColor(fLegendBackgroundColor
);
1484 if (drawBackground
) {
1485 BRect
backgroundFrame(legendFrame
);
1486 backgroundFrame
.bottom
+= kDraggerSize
;
1487 FillRect(backgroundFrame
, B_SOLID_LOW
);
1490 BAutolock
_(fSourcesLock
);
1492 font_height fontHeight
;
1493 GetFontHeight(&fontHeight
);
1495 for (int32 i
= 0; i
< fSources
.CountItems(); i
++) {
1496 DataSource
* source
= fSources
.ItemAt(i
);
1497 DataHistory
* values
= fValues
.ItemAt(i
);
1498 BRect frame
= _LegendFrameAt(legendFrame
, i
);
1501 BRect colorBox
= _LegendColorFrameAt(legendFrame
, i
);
1502 BRect rect
= colorBox
;
1503 uint32 flags
= BControlLook::B_BLEND_FRAME
;
1504 be_control_look
->DrawTextControlBorder(this, rect
,
1505 rect
, fLegendBackgroundColor
, flags
);
1506 SetHighColor(source
->Color());
1509 // show current value and label
1510 float y
= frame
.top
+ ceilf(fontHeight
.ascent
);
1511 int64 value
= values
->ValueAt(values
->End());
1513 source
->Print(text
, value
);
1514 float width
= StringWidth(text
.String());
1516 BString label
= source
->Label();
1517 float possibleLabelWidth
= frame
.right
- colorBox
.right
- 12 - width
;
1518 // TODO: TruncateString() is broken... remove + 5 when fixed!
1519 if (ceilf(StringWidth(label
.String()) + 5) > possibleLabelWidth
)
1520 label
= source
->ShortLabel();
1521 TruncateString(&label
, B_TRUNCATE_MIDDLE
, possibleLabelWidth
);
1524 SetHighColor(ui_color(B_PANEL_TEXT_COLOR
));
1526 if (be_control_look
== NULL
) {
1527 DrawString(label
.String(), BPoint(6 + colorBox
.right
, y
));
1528 DrawString(text
.String(), BPoint(frame
.right
- width
, y
));
1530 be_control_look
->DrawLabel(this, label
.String(),
1531 Parent()->ViewColor(), 0, BPoint(6 + colorBox
.right
, y
));
1532 be_control_look
->DrawLabel(this, text
.String(),
1533 Parent()->ViewColor(), 0, BPoint(frame
.right
- width
, y
));
1540 ActivityView::_Refresh()
1542 bigtime_t lastTimeout
= system_time() - RefreshInterval();
1543 BMessenger
target(this);
1546 status_t status
= acquire_sem_etc(fRefreshSem
, 1, B_ABSOLUTE_TIMEOUT
,
1547 lastTimeout
+ RefreshInterval());
1548 if (status
== B_OK
|| status
== B_BAD_SEM_ID
)
1550 if (status
== B_INTERRUPTED
)
1553 SystemInfo
info(fSystemInfoHandler
);
1554 lastTimeout
+= RefreshInterval();
1556 fSourcesLock
.Lock();
1558 for (uint32 i
= fSources
.CountItems(); i
-- > 0;) {
1559 DataSource
* source
= fSources
.ItemAt(i
);
1560 DataHistory
* values
= fValues
.ItemAt(i
);
1562 int64 value
= source
->NextValue(info
);
1563 values
->AddValue(info
.Time(), value
);
1566 fSourcesLock
.Unlock();
1568 target
.SendMessage(B_INVALIDATE
);
1574 ActivityView::_RefreshThread(void* self
)
1576 ((ActivityView
*)self
)->_Refresh();