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