xref: /haiku/src/apps/activitymonitor/ActivityWindow.cpp (revision 6d2f2ec177bf615a117a7428d71be4330545b320)
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.AddBool("data", true);
200 				fSettingsWindow.SendMessage(&toFront);
201 			} else {
202 				// Open new settings window
203 				BWindow* window = new SettingsWindow(this);
204 				window->Show();
205 
206 				fSettingsWindow = window;
207 			}
208 			break;
209 		}
210 
211 		case kMsgAlwaysOnTop:
212 		{
213 			_SetAlwaysOnTop(!fAlwaysOnTop->IsMarked());
214 			break;
215 		}
216 
217 		case kMsgTimeIntervalUpdated:
218 			BroadcastToActivityViews(message);
219 			break;
220 
221 		default:
222 			BWindow::MessageReceived(message);
223 			break;
224 	}
225 }
226 
227 
228 bool
229 ActivityWindow::QuitRequested()
230 {
231 	_SaveSettings();
232 	be_app->PostMessage(B_QUIT_REQUESTED);
233 	return true;
234 }
235 
236 
237 int32
238 ActivityWindow::ActivityViewCount() const
239 {
240 #ifdef __HAIKU__
241 	return fLayout->View()->CountChildren();
242 #else
243 	return 1;
244 #endif
245 }
246 
247 
248 ActivityView*
249 ActivityWindow::ActivityViewAt(int32 index) const
250 {
251 	return dynamic_cast<ActivityView*>(fLayout->View()->ChildAt(index));
252 }
253 
254 
255 bool
256 ActivityWindow::IsAlwaysOnTop() const
257 {
258 	return fAlwaysOnTop->IsMarked();
259 }
260 
261 
262 void
263 ActivityWindow::BroadcastToActivityViews(BMessage* message, BView* exceptToView)
264 {
265 	BView* view;
266 	for (int32 i = 0; (view = ActivityViewAt(i)) != NULL; i++) {
267 		if (view != exceptToView)
268 			PostMessage(message, view);
269 	}
270 }
271 
272 
273 bigtime_t
274 ActivityWindow::RefreshInterval() const
275 {
276 	ActivityView* view = ActivityViewAt(0);
277 	if (view != 0)
278 		return view->RefreshInterval();
279 
280 	return 100000;
281 }
282 
283 
284 status_t
285 ActivityWindow::_OpenSettings(BFile& file, uint32 mode)
286 {
287 	BPath path;
288 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
289 		return B_ERROR;
290 
291 	path.Append("ActivityMonitor settings");
292 
293 	return file.SetTo(path.Path(), mode);
294 }
295 
296 
297 status_t
298 ActivityWindow::_LoadSettings(BMessage& settings)
299 {
300 	BFile file;
301 	status_t status = _OpenSettings(file, B_READ_ONLY);
302 	if (status < B_OK)
303 		return status;
304 
305 	return settings.Unflatten(&file);
306 }
307 
308 
309 status_t
310 ActivityWindow::_SaveSettings()
311 {
312 	BFile file;
313 	status_t status = _OpenSettings(file, B_WRITE_ONLY | B_CREATE_FILE
314 		| B_ERASE_FILE);
315 	if (status < B_OK)
316 		return status;
317 
318 	BMessage settings('actm');
319 	status = settings.AddRect("window frame", Frame());
320 	if (status != B_OK)
321 		return status;
322 
323 	status = settings.SetBool("always on top", fAlwaysOnTop->IsMarked());
324 	if (status != B_OK)
325 		return status;
326 
327 #ifdef __HAIKU__
328 	BView* top = fLayout->View();
329 #else
330 	BView* top = ChildAt(0);
331 #endif
332 	int32 count = top->CountChildren();
333 	for (int32 i = 0; i < count; i++) {
334 		ActivityView* view = dynamic_cast<ActivityView*>(top->ChildAt(i));
335 		if (view == NULL)
336 			continue;
337 
338 		BMessage viewState;
339 		status = view->SaveState(viewState);
340 		if (status == B_OK)
341 			status = settings.AddMessage("activity view", &viewState);
342 		if (status != B_OK)
343 			break;
344 	}
345 
346 	if (status == B_OK)
347 		status = settings.Flatten(&file);
348 
349 	return status;
350 }
351 
352 
353 void
354 ActivityWindow::_AddDefaultView()
355 {
356 	BMessage settings;
357 	settings.AddInt64("refresh interval", RefreshInterval());
358 
359 	ActivityView* view = new ActivityView("ActivityMonitor", &settings);
360 
361 	switch (ActivityViewCount()) {
362 		case 0:
363 			// The first view defaults to memory usage
364 			view->AddDataSource(new UsedMemoryDataSource());
365 			view->AddDataSource(new CachedMemoryDataSource());
366 			break;
367 		case 2:
368 			// The third view defaults to network in/out
369 			view->AddDataSource(new NetworkUsageDataSource(true));
370 			view->AddDataSource(new NetworkUsageDataSource(false));
371 			break;
372 		case 1:
373 		default:
374 			// Everything beyond that defaults to a CPU usage view
375 			view->AddDataSource(new CPUUsageDataSource());
376 			break;
377 	}
378 
379 	fLayout->AddItem(view->CreateHistoryLayoutItem());
380 	fLayout->AddItem(view->CreateLegendLayoutItem());
381 }
382 
383 
384 void
385 ActivityWindow::_MessageDropped(BMessage* message)
386 {
387 	entry_ref ref;
388 	if (message->FindRef("refs", &ref) != B_OK) {
389 		// TODO: If app, then launch it, and add ActivityView for this one?
390 	}
391 }
392 
393 
394 void
395 ActivityWindow::_SetAlwaysOnTop(bool alwaysOnTop)
396 {
397 	SetFeel(alwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL);
398 	fAlwaysOnTop->SetMarked(alwaysOnTop);
399 	if (fSettingsWindow.IsValid() && alwaysOnTop) {
400 		// Change the settings window feel to modal (via scripting)
401 		BMessage toFront(B_SET_PROPERTY);
402 		toFront.AddSpecifier("Feel");
403 		toFront.AddInt32("data", B_MODAL_ALL_WINDOW_FEEL);
404 		fSettingsWindow.SendMessage(&toFront);
405 	}
406 }
407