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 31static const uint32 kMsgAddView = 'advw'; 32static const uint32 kMsgAlwaysOnTop = 'alot'; 33static const uint32 kMsgShowSettings = 'shst'; 34 35 36ActivityWindow::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 113ActivityWindow::~ActivityWindow() 114{ 115} 116 117 118void 119ActivityWindow::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 194bool 195ActivityWindow::QuitRequested() 196{ 197 _SaveSettings(); 198 be_app->PostMessage(B_QUIT_REQUESTED); 199 return true; 200} 201 202 203int32 204ActivityWindow::ActivityViewCount() const 205{ 206#ifdef __HAIKU__ 207 return fLayout->View()->CountChildren(); 208#else 209 return 1; 210#endif 211} 212 213 214ActivityView* 215ActivityWindow::ActivityViewAt(int32 index) const 216{ 217 return dynamic_cast<ActivityView*>(fLayout->View()->ChildAt(index)); 218} 219 220 221bool 222ActivityWindow::IsAlwaysOnTop() const 223{ 224 return fAlwaysOnTop->IsMarked(); 225} 226 227 228void 229ActivityWindow::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 239bigtime_t 240ActivityWindow::RefreshInterval() const 241{ 242 ActivityView* view = ActivityViewAt(0); 243 if (view != 0) 244 return view->RefreshInterval(); 245 246 return 100000; 247} 248 249 250status_t 251ActivityWindow::_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 263status_t 264ActivityWindow::_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 275status_t 276ActivityWindow::_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 319void 320ActivityWindow::_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 354void 355ActivityWindow::_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 364void 365ActivityWindow::_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