/* * Copyright 2002-2016, Haiku, Inc. All rights reserved. * Copyright 2002, François Revol, revol@free.fr. * This file is distributed under the terms of the MIT License. * * Authors: * François Revol, revol@free.fr * Axel Dörfler, axeld@pinc-software.de * Oliver "Madison" Kohl, * Matt Madia * Daniel Devine, devine@ddevnet.net */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Workspaces" static const char* kDeskbarItemName = "workspaces"; static const char* kSignature = "application/x-vnd.Be-WORK"; static const char* kDeskbarSignature = "application/x-vnd.Be-TSKB"; static const char* kScreenPrefletSignature = "application/x-vnd.Haiku-Screen"; static const char* kOldSettingFile = "Workspace_data"; static const char* kSettingsFile = "Workspaces_settings"; static const uint32 kMsgChangeCount = 'chWC'; static const uint32 kMsgToggleTitle = 'tgTt'; static const uint32 kMsgToggleBorder = 'tgBd'; static const uint32 kMsgToggleAutoRaise = 'tgAR'; static const uint32 kMsgToggleAlwaysOnTop = 'tgAT'; static const uint32 kMsgToggleLiveInDeskbar = 'tgDb'; static const uint32 kMsgToggleSwitchOnWheel = 'tgWh'; extern "C" _EXPORT BView* instantiate_deskbar_item(float maxWidth, float maxHeight); static status_t OpenSettingsFile(BFile& file, int mode) { BPath path; status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); if (status != B_OK) status = find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path); if (status != B_OK) return status; status = path.Append(kSettingsFile); if (status != B_OK) return status; status = file.SetTo(path.Path(), mode); if (mode == B_READ_ONLY && status == B_ENTRY_NOT_FOUND) { if (find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path) == B_OK && path.Append(kSettingsFile) == B_OK) { status = file.SetTo(path.Path(), mode); } } return status; } class WorkspacesSettings { public: WorkspacesSettings(); virtual ~WorkspacesSettings(); BRect WindowFrame() const { return fWindowFrame; } BRect ScreenFrame() const { return fScreenFrame; } bool AutoRaising() const { return fAutoRaising; } bool AlwaysOnTop() const { return fAlwaysOnTop; } bool HasTitle() const { return fHasTitle; } bool HasBorder() const { return fHasBorder; } bool SettingsLoaded() const { return fLoaded; } void UpdateFramesForScreen(BRect screenFrame); void UpdateScreenFrame(); void SetWindowFrame(BRect); void SetAutoRaising(bool enable) { fAutoRaising = enable; } void SetAlwaysOnTop(bool enable) { fAlwaysOnTop = enable; } void SetHasTitle(bool enable) { fHasTitle = enable; } void SetHasBorder(bool enable) { fHasBorder = enable; } private: BRect fWindowFrame; BRect fScreenFrame; bool fAutoRaising; bool fAlwaysOnTop; bool fHasTitle; bool fHasBorder; bool fLoaded; }; class WorkspacesView : public BView { public: WorkspacesView(BRect frame, bool showDragger); WorkspacesView(BMessage* archive); ~WorkspacesView(); static WorkspacesView* Instantiate(BMessage* archive); virtual status_t Archive(BMessage* archive, bool deep = true) const; virtual void AttachedToWindow(); virtual void DetachedFromWindow(); virtual void FrameMoved(BPoint newPosition); virtual void FrameResized(float newWidth, float newHeight); virtual void MessageReceived(BMessage* message); virtual void MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage); virtual void MouseDown(BPoint where); bool SwitchOnWheel() const { return fSwitchOnWheel; } void SetSwitchOnWheel(bool enable); private: void _AboutRequested(); void _UpdateParentClipping(); void _ExcludeFromParentClipping(); void _CleanupParentClipping(); friend class WorkspacesWindow; void _LoadSettings(); void _SaveSettings(); BView* fParentWhichDrawsOnChildren; BRect fCurrentFrame; bool fSwitchOnWheel; }; class WorkspacesWindow : public BWindow { public: WorkspacesWindow(WorkspacesSettings *settings); virtual ~WorkspacesWindow(); virtual void ScreenChanged(BRect frame, color_space mode); virtual void FrameMoved(BPoint origin); virtual void FrameResized(float width, float height); virtual void Zoom(BPoint origin, float width, float height); virtual void MessageReceived(BMessage *msg); virtual bool QuitRequested(); void SetAutoRaise(bool enable); bool IsAutoRaising() const { return fSettings->AutoRaising(); } float GetTabHeight() { return fSettings->HasTitle() ? fTabHeight : 0; } float GetBorderWidth() { return fBorderWidth; } float GetScreenBorderOffset() { return 2.0 * fBorderWidth; } private: WorkspacesSettings *fSettings; WorkspacesView *fWorkspacesView; float fTabHeight; float fBorderWidth; }; class WorkspacesApp : public BApplication { public: WorkspacesApp(); virtual ~WorkspacesApp(); virtual void AboutRequested(); virtual void ArgvReceived(int32 argc, char **argv); virtual void ReadyToRun(); void Usage(const char *programName); private: WorkspacesWindow* fWindow; }; // #pragma mark - WorkspacesSettings WorkspacesSettings::WorkspacesSettings() : fAutoRaising(false), fAlwaysOnTop(false), fHasTitle(true), fHasBorder(true), fLoaded(false) { UpdateScreenFrame(); BScreen screen; BFile file; if (OpenSettingsFile(file, B_READ_ONLY) == B_OK) { BMessage settings; if (settings.Unflatten(&file) == B_OK) { fLoaded = settings.FindRect("window", &fWindowFrame) == B_OK && settings.FindRect("screen", &fScreenFrame) == B_OK; settings.FindBool("auto-raise", &fAutoRaising); settings.FindBool("always on top", &fAlwaysOnTop); if (settings.FindBool("has title", &fHasTitle) != B_OK) fHasTitle = true; if (settings.FindBool("has border", &fHasBorder) != B_OK) fHasBorder = true; } } else { // try reading BeOS compatible settings BPath path; if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { path.Append(kOldSettingFile); BFile file(path.Path(), B_READ_ONLY); if (file.InitCheck() == B_OK && file.Read(&fWindowFrame, sizeof(BRect)) == sizeof(BRect)) { // we now also store the frame of the screen to know // in which context the window frame has been chosen BRect frame; if (file.Read(&frame, sizeof(BRect)) == sizeof(BRect)) fScreenFrame = frame; else fScreenFrame = screen.Frame(); fLoaded = true; } } } if (fLoaded) { // if the current screen frame is different from the one // just loaded, we need to alter the window frame accordingly if (fScreenFrame != screen.Frame()) UpdateFramesForScreen(screen.Frame()); } } WorkspacesSettings::~WorkspacesSettings() { BFile file; if (OpenSettingsFile(file, B_WRITE_ONLY | B_ERASE_FILE | B_CREATE_FILE) != B_OK) { return; } // switch on wheel saved by view later on BMessage settings('wksp'); if (settings.AddRect("window", fWindowFrame) == B_OK && settings.AddRect("screen", fScreenFrame) == B_OK && settings.AddBool("auto-raise", fAutoRaising) == B_OK && settings.AddBool("always on top", fAlwaysOnTop) == B_OK && settings.AddBool("has title", fHasTitle) == B_OK && settings.AddBool("has border", fHasBorder) == B_OK) { settings.Flatten(&file); } } void WorkspacesSettings::UpdateFramesForScreen(BRect newScreenFrame) { // don't change the position if the screen frame hasn't changed if (newScreenFrame == fScreenFrame) return; // adjust horizontal position if (fWindowFrame.right > fScreenFrame.right / 2) { fWindowFrame.OffsetTo(newScreenFrame.right - (fScreenFrame.right - fWindowFrame.left), fWindowFrame.top); } // adjust vertical position if (fWindowFrame.bottom > fScreenFrame.bottom / 2) { fWindowFrame.OffsetTo(fWindowFrame.left, newScreenFrame.bottom - (fScreenFrame.bottom - fWindowFrame.top)); } fScreenFrame = newScreenFrame; } void WorkspacesSettings::UpdateScreenFrame() { BScreen screen; fScreenFrame = screen.Frame(); } void WorkspacesSettings::SetWindowFrame(BRect frame) { fWindowFrame = frame; } // #pragma mark - WorkspacesView WorkspacesView::WorkspacesView(BRect frame, bool showDragger = true) : BView(frame, kDeskbarItemName, B_FOLLOW_ALL, kWorkspacesViewFlag | B_FRAME_EVENTS), fParentWhichDrawsOnChildren(NULL), fCurrentFrame(frame), fSwitchOnWheel(false) { _LoadSettings(); if (showDragger) { frame.OffsetTo(B_ORIGIN); frame.top = frame.bottom - 7; frame.left = frame.right - 7; BDragger* dragger = new BDragger(frame, this, B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); AddChild(dragger); } } WorkspacesView::WorkspacesView(BMessage* archive) : BView(archive), fParentWhichDrawsOnChildren(NULL), fCurrentFrame(Frame()), fSwitchOnWheel(false) { _LoadSettings(); // Just in case we are instantiated from an older archive... SetFlags(Flags() | B_FRAME_EVENTS); // Make sure the auto-raise feature didn't leave any artifacts - this is // not a good idea to keep enabled for a replicant. if (EventMask() != 0) SetEventMask(0); } WorkspacesView::~WorkspacesView() { _SaveSettings(); } /*static*/ WorkspacesView* WorkspacesView::Instantiate(BMessage* archive) { if (!validate_instantiation(archive, "WorkspacesView")) return NULL; return new WorkspacesView(archive); } status_t WorkspacesView::Archive(BMessage* archive, bool deep) const { status_t status = BView::Archive(archive, deep); if (status == B_OK) status = archive->AddString("add_on", kSignature); if (status == B_OK) status = archive->AddString("class", "WorkspacesView"); return status; } void WorkspacesView::_AboutRequested() { BAboutWindow* window = new BAboutWindow( B_TRANSLATE_SYSTEM_NAME("Workspaces"), kSignature); const char* authors[] = { "Axel Dörfler", "Oliver \"Madison\" Kohl", "Matt Madia", "François Revol", NULL }; const char* extraCopyrights[] = { "2002 François Revol", NULL }; const char* extraInfo = "Send windows behind using the Option key. " "Move windows to front using the Control key.\n"; window->AddCopyright(2002, "Haiku, Inc.", extraCopyrights); window->AddAuthors(authors); window->AddExtraInfo(extraInfo); window->Show(); } void WorkspacesView::AttachedToWindow() { BView* parent = Parent(); if (parent != NULL && (parent->Flags() & B_DRAW_ON_CHILDREN) != 0) { fParentWhichDrawsOnChildren = parent; _ExcludeFromParentClipping(); } } void WorkspacesView::DetachedFromWindow() { if (fParentWhichDrawsOnChildren != NULL) _CleanupParentClipping(); } void WorkspacesView::FrameMoved(BPoint newPosition) { _UpdateParentClipping(); } void WorkspacesView::FrameResized(float newWidth, float newHeight) { _UpdateParentClipping(); } void WorkspacesView::_UpdateParentClipping() { if (fParentWhichDrawsOnChildren != NULL) { _CleanupParentClipping(); _ExcludeFromParentClipping(); fParentWhichDrawsOnChildren->Invalidate(fCurrentFrame); fCurrentFrame = Frame(); } } void WorkspacesView::_ExcludeFromParentClipping() { // Prevent the parent view to draw over us. Do so in a way that allows // restoring the parent to the previous state. fParentWhichDrawsOnChildren->PushState(); BRegion clipping(fParentWhichDrawsOnChildren->Bounds()); clipping.Exclude(Frame()); fParentWhichDrawsOnChildren->ConstrainClippingRegion(&clipping); } void WorkspacesView::_CleanupParentClipping() { // Restore the previous parent state. NOTE: This relies on views // being detached in exactly the opposite order as them being // attached. Otherwise we would mess up states if a sibbling view did // the same thing we did in AttachedToWindow()... fParentWhichDrawsOnChildren->PopState(); } void WorkspacesView::_LoadSettings() { BFile file; if (OpenSettingsFile(file, B_READ_ONLY) == B_OK) { BMessage settings; if (settings.Unflatten(&file) == B_OK) settings.FindBool("switch on wheel", &fSwitchOnWheel); } } void WorkspacesView::_SaveSettings() { BFile file; if (OpenSettingsFile(file, B_READ_ONLY | B_CREATE_FILE) != B_OK) return; BMessage settings('wksp'); settings.Unflatten(&file); if (OpenSettingsFile(file, B_WRITE_ONLY | B_ERASE_FILE) != B_OK) return; if (settings.ReplaceBool("switch on wheel", fSwitchOnWheel) != B_OK) settings.AddBool("switch on wheel", fSwitchOnWheel); settings.Flatten(&file); } void WorkspacesView::MessageReceived(BMessage* message) { switch (message->what) { case B_ABOUT_REQUESTED: _AboutRequested(); break; case B_MOUSE_WHEEL_CHANGED: { if (!fSwitchOnWheel) break; float deltaY = message->FindFloat("be:wheel_delta_y"); if (deltaY > 0.1) activate_workspace(current_workspace() + 1); else if (deltaY < -0.1) activate_workspace(current_workspace() - 1); break; } case kMsgChangeCount: be_roster->Launch(kScreenPrefletSignature); break; case kMsgToggleLiveInDeskbar: { // only actually used from the replicant itself // since HasItem() locks up we just remove directly. BDeskbar deskbar; // we shouldn't do this here actually, but it works for now... deskbar.RemoveItem(kDeskbarItemName); break; } case kMsgToggleSwitchOnWheel: { fSwitchOnWheel = !fSwitchOnWheel; break; } default: BView::MessageReceived(message); break; } } void WorkspacesView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) { WorkspacesWindow* window = dynamic_cast(Window()); if (window == NULL || !window->IsAutoRaising()) return; // Auto-Raise where = ConvertToScreen(where); BScreen screen(window); BRect screenFrame = screen.Frame(); BRect windowFrame = window->Frame(); float tabHeight = window->GetTabHeight(); float borderWidth = window->GetBorderWidth(); if (where.x == screenFrame.left || where.x == screenFrame.right || where.y == screenFrame.top || where.y == screenFrame.bottom) { // cursor is on screen edge // Stretch frame to also accept mouse moves over the window borders windowFrame.InsetBy(-borderWidth, -(tabHeight + borderWidth)); if (windowFrame.Contains(where)) window->Activate(); } } void WorkspacesView::MouseDown(BPoint where) { // With enabled auto-raise feature, we'll get mouse messages we don't // want to handle here. if (!Bounds().Contains(where)) return; int32 buttons = 0; if (Window() != NULL && Window()->CurrentMessage() != NULL) Window()->CurrentMessage()->FindInt32("buttons", &buttons); if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) return; // open context menu BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false); menu->SetFont(be_plain_font); // TODO: alternatively change the count here directly? BMenuItem* changeItem = new BMenuItem(B_TRANSLATE("Change workspace count" B_UTF8_ELLIPSIS), new BMessage(kMsgChangeCount)); menu->AddItem(changeItem); BMenuItem* switchItem = new BMenuItem(B_TRANSLATE("Switch on mouse wheel"), new BMessage(kMsgToggleSwitchOnWheel)); menu->AddItem(switchItem); switchItem->SetMarked(fSwitchOnWheel); WorkspacesWindow *window = dynamic_cast(Window()); if (window != NULL) { // inside Workspaces app BMenuItem* item; menu->AddSeparatorItem(); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window tab"), new BMessage(kMsgToggleTitle))); if (window->Look() == B_TITLED_WINDOW_LOOK) item->SetMarked(true); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window border"), new BMessage(kMsgToggleBorder))); if (window->Look() == B_TITLED_WINDOW_LOOK || window->Look() == B_MODAL_WINDOW_LOOK) { item->SetMarked(true); } menu->AddSeparatorItem(); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Always on top"), new BMessage(kMsgToggleAlwaysOnTop))); if (window->Feel() == B_FLOATING_ALL_WINDOW_FEEL) item->SetMarked(true); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Auto-raise"), new BMessage(kMsgToggleAutoRaise))); if (window->IsAutoRaising()) item->SetMarked(true); if (be_roster->IsRunning(kDeskbarSignature)) { menu->AddItem(item = new BMenuItem( B_TRANSLATE("Live in the Deskbar"), new BMessage(kMsgToggleLiveInDeskbar))); BDeskbar deskbar; item->SetMarked(deskbar.HasItem(kDeskbarItemName)); } menu->AddSeparatorItem(); menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED))); menu->SetTargetForItems(window); } else { // we're replicated in some way... BMenuItem* item; menu->AddSeparatorItem(); // check which way BDragger *dragger = dynamic_cast(ChildAt(0)); if (dragger) { // replicant menu->AddItem(item = new BMenuItem(B_TRANSLATE("Remove replicant"), new BMessage(B_TRASH_TARGET))); item->SetTarget(dragger); } else { // Deskbar item menu->AddItem(item = new BMenuItem(B_TRANSLATE("Remove replicant"), new BMessage(kMsgToggleLiveInDeskbar))); item->SetTarget(this); } } changeItem->SetTarget(this); switchItem->SetTarget(this); ConvertToScreen(&where); menu->Go(where, true, true, true); } void WorkspacesView::SetSwitchOnWheel(bool enable) { if (enable == fSwitchOnWheel) return; fSwitchOnWheel = enable; } // #pragma mark - WorkspacesWindow WorkspacesWindow::WorkspacesWindow(WorkspacesSettings *settings) : BWindow(settings->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Workspaces"), B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_AVOID_FRONT | B_WILL_ACCEPT_FIRST_CLICK | B_CLOSE_ON_ESCAPE, B_ALL_WORKSPACES), fSettings(settings), fWorkspacesView(NULL) { // Turn window decor on to grab decor widths. BMessage windowSettings; float borderWidth = 0; SetLook(B_TITLED_WINDOW_LOOK); if (GetDecoratorSettings(&windowSettings) == B_OK) { BRect tabFrame = windowSettings.FindRect("tab frame"); borderWidth = windowSettings.FindFloat("border width"); fTabHeight = tabFrame.Height(); fBorderWidth = borderWidth; } if (!fSettings->SettingsLoaded()) { // No settings, compute a reasonable default frame. // We aim for previews at 10% of actual screen size, and matching the // aspect ratio. We then scale that down, until it fits the screen. // Finally, we put the window on the bottom right of the screen so the // auto-raise mode can be used. BScreen screen; float screenWidth = screen.Frame().Width(); float screenHeight = screen.Frame().Height(); float aspectRatio = screenWidth / screenHeight; uint32 columns, rows; BPrivate::get_workspaces_layout(&columns, &rows); // default size of ~1/10 of screen width float workspaceWidth = screenWidth / 10; float workspaceHeight = workspaceWidth / aspectRatio; float width = floor(workspaceWidth * columns); float height = floor(workspaceHeight * rows); // If you have too many workspaces to fit on the screen, shrink until // they fit. while (width + 2 * borderWidth > screenWidth || height + 2 * borderWidth + GetTabHeight() > screenHeight) { width = floor(0.95 * width); height = floor(0.95 * height); } BRect frame = fSettings->ScreenFrame(); frame.OffsetBy(-2.0 * borderWidth, -2.0 * borderWidth); frame.left = frame.right - width; frame.top = frame.bottom - height; ResizeTo(frame.Width(), frame.Height()); // Put it in bottom corner by default. MoveTo(screenWidth - frame.Width() - borderWidth, screenHeight - frame.Height() - borderWidth); fSettings->SetWindowFrame(frame); } if (!fSettings->HasBorder()) SetLook(B_NO_BORDER_WINDOW_LOOK); else if (!fSettings->HasTitle()) SetLook(B_MODAL_WINDOW_LOOK); fWorkspacesView = new WorkspacesView(Bounds()); AddChild(fWorkspacesView); if (fSettings->AlwaysOnTop()) SetFeel(B_FLOATING_ALL_WINDOW_FEEL); else SetAutoRaise(fSettings->AutoRaising()); } WorkspacesWindow::~WorkspacesWindow() { delete fSettings; } void WorkspacesWindow::ScreenChanged(BRect rect, color_space mode) { fSettings->UpdateFramesForScreen(rect); MoveTo(fSettings->WindowFrame().LeftTop()); } void WorkspacesWindow::FrameMoved(BPoint origin) { fSettings->SetWindowFrame(Frame()); } void WorkspacesWindow::FrameResized(float width, float height) { if (!(modifiers() & B_SHIFT_KEY)) { BWindow::FrameResized(width, height); return; } uint32 columns, rows; BPrivate::get_workspaces_layout(&columns, &rows); BScreen screen; float screenWidth = screen.Frame().Width(); float screenHeight = screen.Frame().Height(); float windowAspectRatio = (columns * screenWidth) / (rows * screenHeight); float newHeight = width / windowAspectRatio; if (height != newHeight) ResizeTo(width, newHeight); fSettings->SetWindowFrame(Frame()); } void WorkspacesWindow::Zoom(BPoint origin, float width, float height) { BScreen screen; float screenWidth = screen.Frame().Width(); float screenHeight = screen.Frame().Height(); float aspectRatio = screenWidth / screenHeight; uint32 columns, rows; BPrivate::get_workspaces_layout(&columns, &rows); float workspaceWidth = Frame().Width() / columns; float workspaceHeight = workspaceWidth / aspectRatio; width = floor(workspaceWidth * columns); height = floor(workspaceHeight * rows); while (width + 2 * GetScreenBorderOffset() > screenWidth || height + 2 * GetScreenBorderOffset() + GetTabHeight() > screenHeight) { width = floor(0.95 * width); height = floor(0.95 * height); } ResizeTo(width, height); if (fSettings->AutoRaising()) { // The auto-raising mode makes sense only if the window is positionned // exactly in the bottom-right corner. If the setting is enabled, move // the window there. origin = screen.Frame().RightBottom(); origin.x -= GetScreenBorderOffset() + width; origin.y -= GetScreenBorderOffset() + height; MoveTo(origin); } } void WorkspacesWindow::MessageReceived(BMessage *message) { switch (message->what) { case B_SIMPLE_DATA: { // Drop from Tracker entry_ref ref; for (int i = 0; (message->FindRef("refs", i, &ref) == B_OK); i++) be_roster->Launch(&ref); break; } case B_ABOUT_REQUESTED: PostMessage(message, ChildAt(0)); break; case kMsgToggleBorder: { bool enable = false; if (Look() == B_NO_BORDER_WINDOW_LOOK) enable = true; if (enable) if (fSettings->HasTitle()) SetLook(B_TITLED_WINDOW_LOOK); else SetLook(B_MODAL_WINDOW_LOOK); else SetLook(B_NO_BORDER_WINDOW_LOOK); fSettings->SetHasBorder(enable); break; } case kMsgToggleTitle: { bool enable = false; if (Look() == B_MODAL_WINDOW_LOOK || Look() == B_NO_BORDER_WINDOW_LOOK) enable = true; if (enable) SetLook(B_TITLED_WINDOW_LOOK); else SetLook(B_MODAL_WINDOW_LOOK); // No matter what the setting for title, we must force the border on fSettings->SetHasBorder(true); fSettings->SetHasTitle(enable); break; } case kMsgToggleAutoRaise: SetAutoRaise(!IsAutoRaising()); SetFeel(B_NORMAL_WINDOW_FEEL); break; case kMsgToggleAlwaysOnTop: { bool enable = false; if (Feel() != B_FLOATING_ALL_WINDOW_FEEL) enable = true; if (enable) SetFeel(B_FLOATING_ALL_WINDOW_FEEL); else SetFeel(B_NORMAL_WINDOW_FEEL); fSettings->SetAlwaysOnTop(enable); break; } case kMsgToggleLiveInDeskbar: { BDeskbar deskbar; if (deskbar.HasItem(kDeskbarItemName)) deskbar.RemoveItem(kDeskbarItemName); else { fWorkspacesView->_SaveSettings(); // save "switch on wheel" setting for replicant to load entry_ref ref; be_roster->FindApp(kSignature, &ref); deskbar.AddItem(&ref); } break; } default: BWindow::MessageReceived(message); break; } } bool WorkspacesWindow::QuitRequested() { be_app->PostMessage(B_QUIT_REQUESTED); return true; } void WorkspacesWindow::SetAutoRaise(bool enable) { fSettings->SetAutoRaising(enable); if (enable) ChildAt(0)->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); else ChildAt(0)->SetEventMask(0); } // #pragma mark - WorkspacesApp WorkspacesApp::WorkspacesApp() : BApplication(kSignature) { fWindow = new WorkspacesWindow(new WorkspacesSettings()); } WorkspacesApp::~WorkspacesApp() { } void WorkspacesApp::AboutRequested() { fWindow->PostMessage(B_ABOUT_REQUESTED); } void WorkspacesApp::Usage(const char *programName) { printf(B_TRANSLATE("Usage: %s [options] [workspace]\n" "where \"options\" are:\n" " --notitle\t\ttitle bar removed, border and resize kept\n" " --noborder\t\ttitle, border, and resize removed\n" " --avoidfocus\t\tprevents the window from being the target of " "keyboard events\n" " --alwaysontop\t\tkeeps window on top\n" " --notmovable\t\twindow can't be moved around\n" " --autoraise\t\tauto-raise the workspace window when it's at the " "screen edge\n" " --help\t\tdisplay this help and exit\n" "and \"workspace\" is the number of the Workspace to which to switch " "(0-31)\n"), programName); // quit only if we aren't running already if (IsLaunching()) Quit(); } void WorkspacesApp::ArgvReceived(int32 argc, char **argv) { for (int i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == '-') { // evaluate --arguments if (!strcmp(argv[i], "--notitle")) fWindow->SetLook(B_MODAL_WINDOW_LOOK); else if (!strcmp(argv[i], "--noborder")) fWindow->SetLook(B_NO_BORDER_WINDOW_LOOK); else if (!strcmp(argv[i], "--avoidfocus")) fWindow->SetFlags(fWindow->Flags() | B_AVOID_FOCUS); else if (!strcmp(argv[i], "--notmovable")) fWindow->SetFlags(fWindow->Flags() | B_NOT_MOVABLE); else if (!strcmp(argv[i], "--alwaysontop")) fWindow->SetFeel(B_FLOATING_ALL_WINDOW_FEEL); else if (!strcmp(argv[i], "--autoraise")) fWindow->SetAutoRaise(true); else { const char *programName = strrchr(argv[0], '/'); programName = programName ? programName + 1 : argv[0]; Usage(programName); } } else if (isdigit(*argv[i])) { // check for a numeric arg, if not already given activate_workspace(atoi(argv[i])); // if the app is running, don't quit // but if it isn't, cancel the complete run, so it doesn't // open any window if (IsLaunching()) Quit(); } else if (!strcmp(argv[i], "-")) { activate_workspace(current_workspace() - 1); if (IsLaunching()) Quit(); } else if (!strcmp(argv[i], "+")) { activate_workspace(current_workspace() + 1); if (IsLaunching()) Quit(); } else { // some unknown arguments were specified fprintf(stderr, B_TRANSLATE("Invalid argument: %s\n"), argv[i]); if (IsLaunching()) Quit(); } } } void WorkspacesApp::ReadyToRun() { fWindow->Show(); } // #pragma mark - BView* instantiate_deskbar_item(float maxWidth, float maxHeight) { // Calculate the correct size of the Deskbar replicant first BScreen screen; float screenWidth = screen.Frame().Width(); float screenHeight = screen.Frame().Height(); float aspectRatio = screenWidth / screenHeight; uint32 columns, rows; BPrivate::get_workspaces_layout(&columns, &rows); // We use 1px for the top and left borders (shown as double) // and divide the remainder equally. However, we keep in mind // that the actual width and height of each workspace is smaller // by 1px, because of bottom/right borders (shown as single). // When calculating workspace width, we must ensure that the assumed // actual workspace height is not negative. Zero is OK. float height = maxHeight; float rowHeight = floor((height - 1) / rows); if (rowHeight < 1) rowHeight = 1; float columnWidth = floor((rowHeight - 1) * aspectRatio) + 1; float width = columnWidth * columns + 1; if (width > maxWidth) width = maxWidth; return new WorkspacesView(BRect (0, 0, width - 1, height - 1), false); } // #pragma mark - int main(int argc, char **argv) { WorkspacesApp app; app.Run(); return 0; }