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