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