1151 lines
28 KiB
C++
1151 lines
28 KiB
C++
/*
|
|
* 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 <AboutWindow.h>
|
|
#include <Application.h>
|
|
#include <Catalog.h>
|
|
#include <Deskbar.h>
|
|
#include <Dragger.h>
|
|
#include <Entry.h>
|
|
#include <File.h>
|
|
#include <FindDirectory.h>
|
|
#include <Locale.h>
|
|
#include <MenuItem.h>
|
|
#include <Path.h>
|
|
#include <PopUpMenu.h>
|
|
#include <Roster.h>
|
|
#include <Screen.h>
|
|
#include <TextView.h>
|
|
#include <Window.h>
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <InterfacePrivate.h>
|
|
#include <ViewPrivate.h>
|
|
#include <WindowPrivate.h>
|
|
|
|
#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<WorkspacesWindow*>(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<WorkspacesWindow*>(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<BDragger*>(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;
|
|
}
|