xref: /haiku/src/apps/activitymonitor/ActivityWindow.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
1 /*
2  * Copyright 2008-2015, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ActivityWindow.h"
8 
9 #include <stdio.h>
10 
11 #include <Application.h>
12 #include <Catalog.h>
13 #include <File.h>
14 #include <FindDirectory.h>
15 #ifdef __HAIKU__
16 #include <GroupLayout.h>
17 #endif
18 #include <Menu.h>
19 #include <MenuBar.h>
20 #include <MenuItem.h>
21 #include <Path.h>
22 #include <Roster.h>
23 
24 #include "ActivityMonitor.h"
25 #include "ActivityView.h"
26 #include "DataSource.h"
27 #include "SettingsWindow.h"
28 
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "ActivityWindow"
31 
32 
33 static const uint32 kMsgAddView = 'advw';
34 static const uint32 kMsgAlwaysOnTop = 'alot';
35 static const uint32 kMsgShowSettings = 'shst';
36 
37 
38 ActivityWindow::ActivityWindow()
39 	:
40 	BWindow(BRect(100, 100, 500, 350), B_TRANSLATE_SYSTEM_NAME("ActivityMonitor"),
41 	B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE)
42 {
43 	BMessage settings;
44 	_LoadSettings(settings);
45 
46 	BRect frame;
47 	if (settings.FindRect("window frame", &frame) == B_OK) {
48 		MoveTo(frame.LeftTop());
49 		ResizeTo(frame.Width(), frame.Height());
50 	} else {
51 		float scaling = be_plain_font->Size() / 12.0f;
52 		ResizeTo(Frame().Width() * scaling, Frame().Height() * scaling);
53 		CenterOnScreen();
54 	}
55 
56 #ifdef __HAIKU__
57 	BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0);
58 	SetLayout(layout);
59 
60 	// create GUI
61 
62 	BMenuBar* menuBar = new BMenuBar("menu");
63 	layout->AddView(menuBar);
64 
65 	fLayout = new BGroupLayout(B_VERTICAL);
66 	float inset = ceilf(be_plain_font->Size() * 0.7);
67 	fLayout->SetInsets(B_USE_WINDOW_SPACING);
68 	fLayout->SetSpacing(inset);
69 
70 	BView* top = new BView("top", 0, fLayout);
71 	layout->AddView(top);
72 	top->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
73 
74 	BMessage viewState;
75 	int32 count = 0;
76 	for (int32 i = 0; settings.FindMessage("activity view", i, &viewState)
77 			== B_OK; i++) {
78 		ActivityView* view = new ActivityView("ActivityMonitor", &viewState);
79 		fLayout->AddItem(view->CreateHistoryLayoutItem());
80 		fLayout->AddItem(view->CreateLegendLayoutItem());
81 		count++;
82 	}
83 	if (count == 0) {
84 		// Add default views (memory & CPU usage)
85 		_AddDefaultView();
86 		_AddDefaultView();
87 	}
88 #else	// !__HAIKU__
89 	BView *layout = new BView(Bounds(), "topmost", B_FOLLOW_NONE, 0);
90 	AddChild(layout);
91 
92 	// create GUI
93 	BRect mbRect(Bounds());
94 	mbRect.bottom = 10;
95 	BMenuBar* menuBar = new BMenuBar(mbRect, "menu");
96 	layout->AddChild(menuBar);
97 
98 	BRect topRect(Bounds());
99 	topRect.top = menuBar->Bounds().bottom + 1;
100 
101 	BView* top = new BView(topRect, "top", B_FOLLOW_ALL, 0);
102 	layout->AddChild(top);
103 
104 	BMessage viewState;
105 	int32 count = 0;
106 	ActivityView *aview;
107 	BRect rect;
108 	for (int32 i = 0; settings.FindMessage("activity view", i, &viewState)
109 			== B_OK; i++) {
110 		aview = new ActivityView("ActivityMonitor", &viewState);
111 		if (!rect.IsValid())
112 			rect = aview->Bounds();
113 		else
114 			rect.OffsetBySelf(0.0, aview->Bounds().Height());
115 		top->AddChild(aview);
116 		count++;
117 	}
118 	if (count == 0)
119 		top->AddChild(new ActivityView("ActivityMonitor", NULL));
120 
121 #endif
122 	// add menu
123 
124 	// "File" menu
125 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
126 	menu->AddItem(new BMenuItem(B_TRANSLATE("Add graph"),
127 		new BMessage(kMsgAddView)));
128 	menu->AddSeparatorItem();
129 
130 	menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
131 		new BMessage(B_QUIT_REQUESTED), 'Q'));
132 	menu->SetTargetForItems(this);
133 	menuBar->AddItem(menu);
134 
135 	// "Settings" menu
136 	menu = new BMenu(B_TRANSLATE("Settings"));
137 	menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS),
138 		new BMessage(kMsgShowSettings)));
139 
140 	menu->AddSeparatorItem();
141 	fAlwaysOnTop = new BMenuItem(B_TRANSLATE("Always on top"), new BMessage(kMsgAlwaysOnTop));
142 	_SetAlwaysOnTop(settings.GetBool("always on top", false));
143 	menu->AddItem(fAlwaysOnTop);
144 
145 	menu->SetTargetForItems(this);
146 	menuBar->AddItem(menu);
147 }
148 
149 
150 ActivityWindow::~ActivityWindow()
151 {
152 }
153 
154 
155 void
156 ActivityWindow::MessageReceived(BMessage* message)
157 {
158 	if (message->WasDropped()) {
159 		_MessageDropped(message);
160 		return;
161 	}
162 
163 	switch (message->what) {
164 		case B_REFS_RECEIVED:
165 		case B_SIMPLE_DATA:
166 			_MessageDropped(message);
167 			break;
168 
169 		case kMsgAddView:
170 		{
171 #ifdef __HAIKU__
172 			BView* firstView = fLayout->View()->ChildAt(0);
173 
174 			_AddDefaultView();
175 
176 			if (firstView != NULL)
177 				ResizeBy(0, firstView->Bounds().Height() + fLayout->Spacing());
178 #endif
179 			break;
180 		}
181 
182 		case kMsgRemoveView:
183 		{
184 #ifdef __HAIKU__
185 			BView* view;
186 			if (message->FindPointer("view", (void**)&view) != B_OK)
187 				break;
188 
189 			view->RemoveSelf();
190 			ResizeBy(0, -view->Bounds().Height() - fLayout->Spacing());
191 			delete view;
192 #endif
193 			break;
194 		}
195 
196 		case kMsgShowSettings:
197 		{
198 			if (fSettingsWindow.IsValid()) {
199 				// Just bring the window to front (via scripting)
200 				BMessage toFront(B_SET_PROPERTY);
201 				toFront.AddSpecifier("Active");
202 				toFront.AddBool("data", true);
203 				fSettingsWindow.SendMessage(&toFront);
204 			} else {
205 				// Open new settings window
206 				BWindow* window = new SettingsWindow(this);
207 				window->Show();
208 
209 				fSettingsWindow = window;
210 			}
211 			break;
212 		}
213 
214 		case kMsgAlwaysOnTop:
215 		{
216 			_SetAlwaysOnTop(!fAlwaysOnTop->IsMarked());
217 			break;
218 		}
219 
220 		case kMsgTimeIntervalUpdated:
221 			BroadcastToActivityViews(message);
222 			break;
223 
224 		default:
225 			BWindow::MessageReceived(message);
226 			break;
227 	}
228 }
229 
230 
231 bool
232 ActivityWindow::QuitRequested()
233 {
234 	_SaveSettings();
235 	be_app->PostMessage(B_QUIT_REQUESTED);
236 	return true;
237 }
238 
239 
240 int32
241 ActivityWindow::ActivityViewCount() const
242 {
243 #ifdef __HAIKU__
244 	return fLayout->View()->CountChildren();
245 #else
246 	return 1;
247 #endif
248 }
249 
250 
251 ActivityView*
252 ActivityWindow::ActivityViewAt(int32 index) const
253 {
254 	return dynamic_cast<ActivityView*>(fLayout->View()->ChildAt(index));
255 }
256 
257 
258 bool
259 ActivityWindow::IsAlwaysOnTop() const
260 {
261 	return fAlwaysOnTop->IsMarked();
262 }
263 
264 
265 void
266 ActivityWindow::BroadcastToActivityViews(BMessage* message, BView* exceptToView)
267 {
268 	BView* view;
269 	for (int32 i = 0; (view = ActivityViewAt(i)) != NULL; i++) {
270 		if (view != exceptToView)
271 			PostMessage(message, view);
272 	}
273 }
274 
275 
276 bigtime_t
277 ActivityWindow::RefreshInterval() const
278 {
279 	ActivityView* view = ActivityViewAt(0);
280 	if (view != 0)
281 		return view->RefreshInterval();
282 
283 	return 100000;
284 }
285 
286 
287 status_t
288 ActivityWindow::_OpenSettings(BFile& file, uint32 mode)
289 {
290 	BPath path;
291 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
292 		return B_ERROR;
293 
294 	path.Append("ActivityMonitor settings");
295 
296 	return file.SetTo(path.Path(), mode);
297 }
298 
299 
300 status_t
301 ActivityWindow::_LoadSettings(BMessage& settings)
302 {
303 	BFile file;
304 	status_t status = _OpenSettings(file, B_READ_ONLY);
305 	if (status < B_OK)
306 		return status;
307 
308 	return settings.Unflatten(&file);
309 }
310 
311 
312 status_t
313 ActivityWindow::_SaveSettings()
314 {
315 	BFile file;
316 	status_t status = _OpenSettings(file, B_WRITE_ONLY | B_CREATE_FILE
317 		| B_ERASE_FILE);
318 	if (status < B_OK)
319 		return status;
320 
321 	BMessage settings('actm');
322 	status = settings.AddRect("window frame", Frame());
323 	if (status != B_OK)
324 		return status;
325 
326 	status = settings.SetBool("always on top", fAlwaysOnTop->IsMarked());
327 	if (status != B_OK)
328 		return status;
329 
330 #ifdef __HAIKU__
331 	BView* top = fLayout->View();
332 #else
333 	BView* top = ChildAt(0);
334 #endif
335 	int32 count = top->CountChildren();
336 	for (int32 i = 0; i < count; i++) {
337 		ActivityView* view = dynamic_cast<ActivityView*>(top->ChildAt(i));
338 		if (view == NULL)
339 			continue;
340 
341 		BMessage viewState;
342 		status = view->SaveState(viewState);
343 		if (status == B_OK)
344 			status = settings.AddMessage("activity view", &viewState);
345 		if (status != B_OK)
346 			break;
347 	}
348 
349 	if (status == B_OK)
350 		status = settings.Flatten(&file);
351 
352 	return status;
353 }
354 
355 
356 void
357 ActivityWindow::_AddDefaultView()
358 {
359 	BMessage settings;
360 	settings.AddInt64("refresh interval", RefreshInterval());
361 
362 	ActivityView* view = new ActivityView("ActivityMonitor", &settings);
363 
364 	switch (ActivityViewCount()) {
365 		case 0:
366 			// The first view defaults to memory usage
367 			view->AddDataSource(new UsedMemoryDataSource());
368 			view->AddDataSource(new CachedMemoryDataSource());
369 			break;
370 		case 2:
371 			// The third view defaults to network in/out
372 			view->AddDataSource(new NetworkUsageDataSource(true));
373 			view->AddDataSource(new NetworkUsageDataSource(false));
374 			break;
375 		case 1:
376 		default:
377 			// Everything beyond that defaults to a CPU usage view
378 			view->AddDataSource(new CPUUsageDataSource());
379 			break;
380 	}
381 
382 	fLayout->AddItem(view->CreateHistoryLayoutItem());
383 	fLayout->AddItem(view->CreateLegendLayoutItem());
384 }
385 
386 
387 void
388 ActivityWindow::_MessageDropped(BMessage* message)
389 {
390 	entry_ref ref;
391 	if (message->FindRef("refs", &ref) != B_OK) {
392 		// TODO: If app, then launch it, and add ActivityView for this one?
393 	}
394 }
395 
396 
397 void
398 ActivityWindow::_SetAlwaysOnTop(bool alwaysOnTop)
399 {
400 	SetFeel(alwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL);
401 	fAlwaysOnTop->SetMarked(alwaysOnTop);
402 	if (fSettingsWindow.IsValid() && alwaysOnTop) {
403 		// Change the settings window feel to modal (via scripting)
404 		BMessage toFront(B_SET_PROPERTY);
405 		toFront.AddSpecifier("Feel");
406 		toFront.AddInt32("data", B_MODAL_ALL_WINDOW_FEEL);
407 		fSettingsWindow.SendMessage(&toFront);
408 	}
409 }
410