/* * Copyright 2008-2015, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #include "ActivityWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include "ActivityMonitor.h" #include "ActivityView.h" #include "DataSource.h" #include "SettingsWindow.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "ActivityWindow" static const uint32 kMsgAddView = 'advw'; static const uint32 kMsgAlwaysOnTop = 'alot'; static const uint32 kMsgShowSettings = 'shst'; ActivityWindow::ActivityWindow() : BWindow(BRect(100, 100, 500, 350), B_TRANSLATE_SYSTEM_NAME("ActivityMonitor"), B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE) { BMessage settings; _LoadSettings(settings); BRect frame; if (settings.FindRect("window frame", &frame) == B_OK) { MoveTo(frame.LeftTop()); ResizeTo(frame.Width(), frame.Height()); } else { float scaling = be_plain_font->Size() / 12.0f; ResizeTo(Frame().Width() * scaling, Frame().Height() * scaling); CenterOnScreen(); } BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0); SetLayout(layout); // create GUI BMenuBar* menuBar = new BMenuBar("menu"); layout->AddView(menuBar); fLayout = new BGroupLayout(B_VERTICAL); fLayout->SetInsets(B_USE_WINDOW_SPACING); fLayout->SetSpacing(B_USE_ITEM_SPACING); BView* top = new BView("top", 0, fLayout); layout->AddView(top); top->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); BMessage viewState; int32 count = 0; for (int32 i = 0; settings.FindMessage("activity view", i, &viewState) == B_OK; i++) { ActivityView* view = new ActivityView("ActivityMonitor", &viewState); fLayout->AddItem(view->CreateHistoryLayoutItem()); fLayout->AddItem(view->CreateLegendLayoutItem()); count++; } if (count == 0) { // Add default views (memory & CPU usage) _AddDefaultView(); _AddDefaultView(); } // add menu // "File" menu BMenu* menu = new BMenu(B_TRANSLATE("File")); menu->AddItem(new BMenuItem(B_TRANSLATE("Add graph"), new BMessage(kMsgAddView))); menu->AddSeparatorItem(); menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED), 'Q')); menu->SetTargetForItems(this); menuBar->AddItem(menu); // "Settings" menu menu = new BMenu(B_TRANSLATE("Settings")); menu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), new BMessage(kMsgShowSettings), ',')); menu->AddSeparatorItem(); fAlwaysOnTop = new BMenuItem(B_TRANSLATE("Always on top"), new BMessage(kMsgAlwaysOnTop)); _SetAlwaysOnTop(settings.GetBool("always on top", false)); menu->AddItem(fAlwaysOnTop); menu->SetTargetForItems(this); menuBar->AddItem(menu); } ActivityWindow::~ActivityWindow() { } void ActivityWindow::MessageReceived(BMessage* message) { if (message->WasDropped()) { _MessageDropped(message); return; } switch (message->what) { case B_REFS_RECEIVED: case B_SIMPLE_DATA: _MessageDropped(message); break; case kMsgAddView: { #ifdef __HAIKU__ BView* firstView = fLayout->View()->ChildAt(0); _AddDefaultView(); if (firstView != NULL) ResizeBy(0, firstView->Bounds().Height() + fLayout->Spacing()); #endif break; } case kMsgRemoveView: { #ifdef __HAIKU__ BView* view; if (message->FindPointer("view", (void**)&view) != B_OK) break; view->RemoveSelf(); ResizeBy(0, -view->Bounds().Height() - fLayout->Spacing()); delete view; #endif break; } case kMsgShowSettings: { if (fSettingsWindow.IsValid()) { // Just bring the window to front (via scripting) BMessage toFront(B_SET_PROPERTY); toFront.AddSpecifier("Active"); toFront.AddBool("data", true); fSettingsWindow.SendMessage(&toFront); } else { // Open new settings window BWindow* window = new SettingsWindow(this); window->Show(); fSettingsWindow = window; } break; } case kMsgAlwaysOnTop: { _SetAlwaysOnTop(!fAlwaysOnTop->IsMarked()); break; } case kMsgTimeIntervalUpdated: BroadcastToActivityViews(message); break; default: BWindow::MessageReceived(message); break; } } bool ActivityWindow::QuitRequested() { _SaveSettings(); be_app->PostMessage(B_QUIT_REQUESTED); return true; } int32 ActivityWindow::ActivityViewCount() const { #ifdef __HAIKU__ return fLayout->View()->CountChildren(); #else return 1; #endif } ActivityView* ActivityWindow::ActivityViewAt(int32 index) const { return dynamic_cast(fLayout->View()->ChildAt(index)); } bool ActivityWindow::IsAlwaysOnTop() const { return fAlwaysOnTop->IsMarked(); } void ActivityWindow::BroadcastToActivityViews(BMessage* message, BView* exceptToView) { BView* view; for (int32 i = 0; (view = ActivityViewAt(i)) != NULL; i++) { if (view != exceptToView) PostMessage(message, view); } } bigtime_t ActivityWindow::RefreshInterval() const { ActivityView* view = ActivityViewAt(0); if (view != 0) return view->RefreshInterval(); return 100000; } status_t ActivityWindow::_OpenSettings(BFile& file, uint32 mode) { BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) return B_ERROR; path.Append("ActivityMonitor settings"); return file.SetTo(path.Path(), mode); } status_t ActivityWindow::_LoadSettings(BMessage& settings) { BFile file; status_t status = _OpenSettings(file, B_READ_ONLY); if (status < B_OK) return status; return settings.Unflatten(&file); } status_t ActivityWindow::_SaveSettings() { BFile file; status_t status = _OpenSettings(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); if (status < B_OK) return status; BMessage settings('actm'); status = settings.AddRect("window frame", Frame()); if (status != B_OK) return status; status = settings.SetBool("always on top", fAlwaysOnTop->IsMarked()); if (status != B_OK) return status; #ifdef __HAIKU__ BView* top = fLayout->View(); #else BView* top = ChildAt(0); #endif int32 count = top->CountChildren(); for (int32 i = 0; i < count; i++) { ActivityView* view = dynamic_cast(top->ChildAt(i)); if (view == NULL) continue; BMessage viewState; status = view->SaveState(viewState); if (status == B_OK) status = settings.AddMessage("activity view", &viewState); if (status != B_OK) break; } if (status == B_OK) status = settings.Flatten(&file); return status; } void ActivityWindow::_AddDefaultView() { BMessage settings; settings.AddInt64("refresh interval", RefreshInterval()); ActivityView* view = new ActivityView("ActivityMonitor", &settings); switch (ActivityViewCount()) { case 0: // The first view defaults to memory usage view->AddDataSource(new UsedMemoryDataSource()); view->AddDataSource(new CachedMemoryDataSource()); view->AddDataSource(new SwapSpaceDataSource()); break; case 2: // The third view defaults to network in/out view->AddDataSource(new NetworkUsageDataSource(true)); view->AddDataSource(new NetworkUsageDataSource(false)); break; case 3: view->AddDataSource(new CPUFrequencyDataSource()); break; case 1: default: // Everything beyond that defaults to a CPU usage view view->AddDataSource(new CPUUsageDataSource()); break; } fLayout->AddItem(view->CreateHistoryLayoutItem()); fLayout->AddItem(view->CreateLegendLayoutItem()); } void ActivityWindow::_MessageDropped(BMessage* message) { entry_ref ref; if (message->FindRef("refs", &ref) != B_OK) { // TODO: If app, then launch it, and add ActivityView for this one? } } void ActivityWindow::_SetAlwaysOnTop(bool alwaysOnTop) { SetFeel(alwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); fAlwaysOnTop->SetMarked(alwaysOnTop); if (fSettingsWindow.IsValid() && alwaysOnTop) { // Change the settings window feel to modal (via scripting) BMessage toFront(B_SET_PROPERTY); toFront.AddSpecifier("Feel"); toFront.AddInt32("data", B_MODAL_ALL_WINDOW_FEEL); fSettingsWindow.SendMessage(&toFront); } }