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