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