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); 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