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