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