btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / workspaces / Workspaces.cpp
blobeae4e3d0c9c469a476f71fc8a25305204db53024
1 /*
2 * Copyright 2002-2016, Haiku, Inc. All rights reserved.
3 * Copyright 2002, François Revol, revol@free.fr.
4 * This file is distributed under the terms of the MIT License.
6 * Authors:
7 * François Revol, revol@free.fr
8 * Axel Dörfler, axeld@pinc-software.de
9 * Oliver "Madison" Kohl,
10 * Matt Madia
11 * Daniel Devine, devine@ddevnet.net
15 #include <AboutWindow.h>
16 #include <Application.h>
17 #include <Catalog.h>
18 #include <Deskbar.h>
19 #include <Dragger.h>
20 #include <Entry.h>
21 #include <File.h>
22 #include <FindDirectory.h>
23 #include <Locale.h>
24 #include <MenuItem.h>
25 #include <Path.h>
26 #include <PopUpMenu.h>
27 #include <Roster.h>
28 #include <Screen.h>
29 #include <TextView.h>
30 #include <Window.h>
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
37 #include <InterfacePrivate.h>
38 #include <ViewPrivate.h>
39 #include <WindowPrivate.h>
41 #undef B_TRANSLATION_CONTEXT
42 #define B_TRANSLATION_CONTEXT "Workspaces"
45 static const char* kDeskbarItemName = "workspaces";
46 static const char* kSignature = "application/x-vnd.Be-WORK";
47 static const char* kDeskbarSignature = "application/x-vnd.Be-TSKB";
48 static const char* kScreenPrefletSignature = "application/x-vnd.Haiku-Screen";
49 static const char* kOldSettingFile = "Workspace_data";
50 static const char* kSettingsFile = "Workspaces_settings";
52 static const uint32 kMsgChangeCount = 'chWC';
53 static const uint32 kMsgToggleTitle = 'tgTt';
54 static const uint32 kMsgToggleBorder = 'tgBd';
55 static const uint32 kMsgToggleAutoRaise = 'tgAR';
56 static const uint32 kMsgToggleAlwaysOnTop = 'tgAT';
57 static const uint32 kMsgToggleLiveInDeskbar = 'tgDb';
58 static const uint32 kMsgToggleSwitchOnWheel = 'tgWh';
61 extern "C" _EXPORT BView* instantiate_deskbar_item();
64 static status_t
65 OpenSettingsFile(BFile& file, int mode)
67 BPath path;
68 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
69 if (status != B_OK)
70 status = find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path);
71 if (status != B_OK)
72 return status;
74 status = path.Append(kSettingsFile);
75 if (status != B_OK)
76 return status;
78 status = file.SetTo(path.Path(), mode);
79 if (mode == B_READ_ONLY && status == B_ENTRY_NOT_FOUND) {
80 if (find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path) == B_OK
81 && path.Append(kSettingsFile) == B_OK) {
82 status = file.SetTo(path.Path(), mode);
86 return status;
90 class WorkspacesSettings {
91 public:
92 WorkspacesSettings();
93 virtual ~WorkspacesSettings();
95 BRect WindowFrame() const { return fWindowFrame; }
96 BRect ScreenFrame() const { return fScreenFrame; }
98 bool AutoRaising() const { return fAutoRaising; }
99 bool AlwaysOnTop() const { return fAlwaysOnTop; }
100 bool HasTitle() const { return fHasTitle; }
101 bool HasBorder() const { return fHasBorder; }
102 bool SettingsLoaded() const { return fLoaded; }
104 void UpdateFramesForScreen(BRect screenFrame);
105 void UpdateScreenFrame();
107 void SetWindowFrame(BRect);
108 void SetAutoRaising(bool enable) { fAutoRaising = enable; }
109 void SetAlwaysOnTop(bool enable) { fAlwaysOnTop = enable; }
110 void SetHasTitle(bool enable) { fHasTitle = enable; }
111 void SetHasBorder(bool enable) { fHasBorder = enable; }
113 private:
114 BRect fWindowFrame;
115 BRect fScreenFrame;
116 bool fAutoRaising;
117 bool fAlwaysOnTop;
118 bool fHasTitle;
119 bool fHasBorder;
120 bool fLoaded;
123 class WorkspacesView : public BView {
124 public:
125 WorkspacesView(BRect frame, bool showDragger);
126 WorkspacesView(BMessage* archive);
127 ~WorkspacesView();
129 static WorkspacesView* Instantiate(BMessage* archive);
130 virtual status_t Archive(BMessage* archive, bool deep = true) const;
132 virtual void AttachedToWindow();
133 virtual void DetachedFromWindow();
134 virtual void FrameMoved(BPoint newPosition);
135 virtual void FrameResized(float newWidth, float newHeight);
136 virtual void MessageReceived(BMessage* message);
137 virtual void MouseMoved(BPoint where, uint32 transit,
138 const BMessage* dragMessage);
139 virtual void MouseDown(BPoint where);
141 bool SwitchOnWheel() const { return fSwitchOnWheel; }
142 void SetSwitchOnWheel(bool enable);
144 private:
145 void _AboutRequested();
147 void _UpdateParentClipping();
148 void _ExcludeFromParentClipping();
149 void _CleanupParentClipping();
151 friend class WorkspacesWindow;
153 void _LoadSettings();
154 void _SaveSettings();
156 BView* fParentWhichDrawsOnChildren;
157 BRect fCurrentFrame;
158 bool fSwitchOnWheel;
161 class WorkspacesWindow : public BWindow {
162 public:
163 WorkspacesWindow(WorkspacesSettings *settings);
164 virtual ~WorkspacesWindow();
166 virtual void ScreenChanged(BRect frame, color_space mode);
167 virtual void FrameMoved(BPoint origin);
168 virtual void FrameResized(float width, float height);
169 virtual void Zoom(BPoint origin, float width, float height);
171 virtual void MessageReceived(BMessage *msg);
172 virtual bool QuitRequested();
174 void SetAutoRaise(bool enable);
175 bool IsAutoRaising() const { return fSettings->AutoRaising(); }
177 float GetTabHeight() { return fSettings->HasTitle() ? fTabHeight : 0; }
178 float GetBorderWidth() { return fBorderWidth; }
179 float GetScreenBorderOffset() { return 2.0 * fBorderWidth; }
181 private:
182 WorkspacesSettings *fSettings;
183 WorkspacesView *fWorkspacesView;
184 float fTabHeight;
185 float fBorderWidth;
188 class WorkspacesApp : public BApplication {
189 public:
190 WorkspacesApp();
191 virtual ~WorkspacesApp();
193 virtual void AboutRequested();
194 virtual void ArgvReceived(int32 argc, char **argv);
195 virtual void ReadyToRun();
197 void Usage(const char *programName);
199 private:
200 WorkspacesWindow* fWindow;
204 // #pragma mark - WorkspacesSettings
207 WorkspacesSettings::WorkspacesSettings()
209 fAutoRaising(false),
210 fAlwaysOnTop(false),
211 fHasTitle(true),
212 fHasBorder(true),
213 fLoaded(false)
215 UpdateScreenFrame();
217 BScreen screen;
219 BFile file;
220 if (OpenSettingsFile(file, B_READ_ONLY) == B_OK) {
221 BMessage settings;
222 if (settings.Unflatten(&file) == B_OK) {
223 fLoaded = settings.FindRect("window", &fWindowFrame) == B_OK
224 && settings.FindRect("screen", &fScreenFrame) == B_OK;
225 settings.FindBool("auto-raise", &fAutoRaising);
226 settings.FindBool("always on top", &fAlwaysOnTop);
227 if (settings.FindBool("has title", &fHasTitle) != B_OK)
228 fHasTitle = true;
229 if (settings.FindBool("has border", &fHasBorder) != B_OK)
230 fHasBorder = true;
232 } else {
233 // try reading BeOS compatible settings
234 BPath path;
235 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
236 path.Append(kOldSettingFile);
237 BFile file(path.Path(), B_READ_ONLY);
238 if (file.InitCheck() == B_OK
239 && file.Read(&fWindowFrame, sizeof(BRect)) == sizeof(BRect)) {
240 // we now also store the frame of the screen to know
241 // in which context the window frame has been chosen
242 BRect frame;
243 if (file.Read(&frame, sizeof(BRect)) == sizeof(BRect))
244 fScreenFrame = frame;
245 else
246 fScreenFrame = screen.Frame();
248 fLoaded = true;
253 if (fLoaded) {
254 // if the current screen frame is different from the one
255 // just loaded, we need to alter the window frame accordingly
256 if (fScreenFrame != screen.Frame())
257 UpdateFramesForScreen(screen.Frame());
262 WorkspacesSettings::~WorkspacesSettings()
264 BFile file;
265 if (OpenSettingsFile(file, B_WRITE_ONLY | B_ERASE_FILE | B_CREATE_FILE)
266 != B_OK) {
267 return;
270 // switch on wheel saved by view later on
272 BMessage settings('wksp');
273 if (settings.AddRect("window", fWindowFrame) == B_OK
274 && settings.AddRect("screen", fScreenFrame) == B_OK
275 && settings.AddBool("auto-raise", fAutoRaising) == B_OK
276 && settings.AddBool("always on top", fAlwaysOnTop) == B_OK
277 && settings.AddBool("has title", fHasTitle) == B_OK
278 && settings.AddBool("has border", fHasBorder) == B_OK) {
279 settings.Flatten(&file);
284 void
285 WorkspacesSettings::UpdateFramesForScreen(BRect newScreenFrame)
287 // don't change the position if the screen frame hasn't changed
288 if (newScreenFrame == fScreenFrame)
289 return;
291 // adjust horizontal position
292 if (fWindowFrame.right > fScreenFrame.right / 2) {
293 fWindowFrame.OffsetTo(newScreenFrame.right
294 - (fScreenFrame.right - fWindowFrame.left), fWindowFrame.top);
297 // adjust vertical position
298 if (fWindowFrame.bottom > fScreenFrame.bottom / 2) {
299 fWindowFrame.OffsetTo(fWindowFrame.left,
300 newScreenFrame.bottom - (fScreenFrame.bottom - fWindowFrame.top));
303 fScreenFrame = newScreenFrame;
307 void
308 WorkspacesSettings::UpdateScreenFrame()
310 BScreen screen;
311 fScreenFrame = screen.Frame();
315 void
316 WorkspacesSettings::SetWindowFrame(BRect frame)
318 fWindowFrame = frame;
322 // #pragma mark - WorkspacesView
325 WorkspacesView::WorkspacesView(BRect frame, bool showDragger = true)
327 BView(frame, kDeskbarItemName, B_FOLLOW_ALL,
328 kWorkspacesViewFlag | B_FRAME_EVENTS),
329 fParentWhichDrawsOnChildren(NULL),
330 fCurrentFrame(frame),
331 fSwitchOnWheel(false)
333 _LoadSettings();
335 if (showDragger) {
336 frame.OffsetTo(B_ORIGIN);
337 frame.top = frame.bottom - 7;
338 frame.left = frame.right - 7;
339 BDragger* dragger = new BDragger(frame, this,
340 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
341 AddChild(dragger);
346 WorkspacesView::WorkspacesView(BMessage* archive)
348 BView(archive),
349 fParentWhichDrawsOnChildren(NULL),
350 fCurrentFrame(Frame()),
351 fSwitchOnWheel(false)
353 _LoadSettings();
355 // Just in case we are instantiated from an older archive...
356 SetFlags(Flags() | B_FRAME_EVENTS);
357 // Make sure the auto-raise feature didn't leave any artifacts - this is
358 // not a good idea to keep enabled for a replicant.
359 if (EventMask() != 0)
360 SetEventMask(0);
364 WorkspacesView::~WorkspacesView()
366 _SaveSettings();
370 /*static*/ WorkspacesView*
371 WorkspacesView::Instantiate(BMessage* archive)
373 if (!validate_instantiation(archive, "WorkspacesView"))
374 return NULL;
376 return new WorkspacesView(archive);
380 status_t
381 WorkspacesView::Archive(BMessage* archive, bool deep) const
383 status_t status = BView::Archive(archive, deep);
384 if (status == B_OK)
385 status = archive->AddString("add_on", kSignature);
386 if (status == B_OK)
387 status = archive->AddString("class", "WorkspacesView");
389 return status;
393 void
394 WorkspacesView::_AboutRequested()
396 BAboutWindow* window = new BAboutWindow(
397 B_TRANSLATE_SYSTEM_NAME("Workspaces"), kSignature);
399 const char* authors[] = {
400 "Axel Dörfler",
401 "Oliver \"Madison\" Kohl",
402 "Matt Madia",
403 "François Revol",
404 NULL
407 const char* extraCopyrights[] = {
408 "2002 François Revol",
409 NULL
412 const char* extraInfo = "Send windows behind using the Option key. "
413 "Move windows to front using the Control key.\n";
415 window->AddCopyright(2002, "Haiku, Inc.",
416 extraCopyrights);
417 window->AddAuthors(authors);
418 window->AddExtraInfo(extraInfo);
420 window->Show();
424 void
425 WorkspacesView::AttachedToWindow()
427 BView* parent = Parent();
428 if (parent != NULL && (parent->Flags() & B_DRAW_ON_CHILDREN) != 0) {
429 fParentWhichDrawsOnChildren = parent;
430 _ExcludeFromParentClipping();
435 void
436 WorkspacesView::DetachedFromWindow()
438 if (fParentWhichDrawsOnChildren != NULL)
439 _CleanupParentClipping();
443 void
444 WorkspacesView::FrameMoved(BPoint newPosition)
446 _UpdateParentClipping();
450 void
451 WorkspacesView::FrameResized(float newWidth, float newHeight)
453 _UpdateParentClipping();
457 void
458 WorkspacesView::_UpdateParentClipping()
460 if (fParentWhichDrawsOnChildren != NULL) {
461 _CleanupParentClipping();
462 _ExcludeFromParentClipping();
463 fParentWhichDrawsOnChildren->Invalidate(fCurrentFrame);
464 fCurrentFrame = Frame();
469 void
470 WorkspacesView::_ExcludeFromParentClipping()
472 // Prevent the parent view to draw over us. Do so in a way that allows
473 // restoring the parent to the previous state.
474 fParentWhichDrawsOnChildren->PushState();
476 BRegion clipping(fParentWhichDrawsOnChildren->Bounds());
477 clipping.Exclude(Frame());
478 fParentWhichDrawsOnChildren->ConstrainClippingRegion(&clipping);
482 void
483 WorkspacesView::_CleanupParentClipping()
485 // Restore the previous parent state. NOTE: This relies on views
486 // being detached in exactly the opposite order as them being
487 // attached. Otherwise we would mess up states if a sibbling view did
488 // the same thing we did in AttachedToWindow()...
489 fParentWhichDrawsOnChildren->PopState();
493 void
494 WorkspacesView::_LoadSettings()
496 BFile file;
497 if (OpenSettingsFile(file, B_READ_ONLY) == B_OK) {
498 BMessage settings;
499 if (settings.Unflatten(&file) == B_OK)
500 settings.FindBool("switch on wheel", &fSwitchOnWheel);
505 void
506 WorkspacesView::_SaveSettings()
508 BFile file;
509 if (OpenSettingsFile(file, B_READ_ONLY | B_CREATE_FILE) != B_OK)
510 return;
512 BMessage settings('wksp');
513 settings.Unflatten(&file);
515 if (OpenSettingsFile(file, B_WRITE_ONLY | B_ERASE_FILE) != B_OK)
516 return;
518 if (settings.ReplaceBool("switch on wheel", fSwitchOnWheel) != B_OK)
519 settings.AddBool("switch on wheel", fSwitchOnWheel);
521 settings.Flatten(&file);
525 void
526 WorkspacesView::MessageReceived(BMessage* message)
528 switch (message->what) {
529 case B_ABOUT_REQUESTED:
530 _AboutRequested();
531 break;
533 case B_MOUSE_WHEEL_CHANGED:
535 if (!fSwitchOnWheel)
536 break;
538 float deltaY = message->FindFloat("be:wheel_delta_y");
539 if (deltaY > 0.1)
540 activate_workspace(current_workspace() + 1);
541 else if (deltaY < -0.1)
542 activate_workspace(current_workspace() - 1);
543 break;
546 case kMsgChangeCount:
547 be_roster->Launch(kScreenPrefletSignature);
548 break;
550 case kMsgToggleLiveInDeskbar:
552 // only actually used from the replicant itself
553 // since HasItem() locks up we just remove directly.
554 BDeskbar deskbar;
555 // we shouldn't do this here actually, but it works for now...
556 deskbar.RemoveItem(kDeskbarItemName);
557 break;
560 case kMsgToggleSwitchOnWheel:
562 fSwitchOnWheel = !fSwitchOnWheel;
563 break;
566 default:
567 BView::MessageReceived(message);
568 break;
573 void
574 WorkspacesView::MouseMoved(BPoint where, uint32 transit,
575 const BMessage* dragMessage)
577 WorkspacesWindow* window = dynamic_cast<WorkspacesWindow*>(Window());
578 if (window == NULL || !window->IsAutoRaising())
579 return;
581 // Auto-Raise
583 where = ConvertToScreen(where);
584 BScreen screen(window);
585 BRect screenFrame = screen.Frame();
586 BRect windowFrame = window->Frame();
587 float tabHeight = window->GetTabHeight();
588 float borderWidth = window->GetBorderWidth();
590 if (where.x == screenFrame.left || where.x == screenFrame.right
591 || where.y == screenFrame.top || where.y == screenFrame.bottom) {
592 // cursor is on screen edge
594 // Stretch frame to also accept mouse moves over the window borders
595 windowFrame.InsetBy(-borderWidth, -(tabHeight + borderWidth));
597 if (windowFrame.Contains(where))
598 window->Activate();
603 void
604 WorkspacesView::MouseDown(BPoint where)
606 // With enabled auto-raise feature, we'll get mouse messages we don't
607 // want to handle here.
608 if (!Bounds().Contains(where))
609 return;
611 int32 buttons = 0;
612 if (Window() != NULL && Window()->CurrentMessage() != NULL)
613 Window()->CurrentMessage()->FindInt32("buttons", &buttons);
615 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
616 return;
618 // open context menu
620 BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
621 menu->SetFont(be_plain_font);
623 // TODO: alternatively change the count here directly?
624 BMenuItem* changeItem = new BMenuItem(B_TRANSLATE("Change workspace count"
625 B_UTF8_ELLIPSIS), new BMessage(kMsgChangeCount));
626 menu->AddItem(changeItem);
628 BMenuItem* switchItem = new BMenuItem(B_TRANSLATE("Switch on mouse wheel"),
629 new BMessage(kMsgToggleSwitchOnWheel));
630 menu->AddItem(switchItem);
631 switchItem->SetMarked(fSwitchOnWheel);
633 WorkspacesWindow *window = dynamic_cast<WorkspacesWindow*>(Window());
634 if (window != NULL) {
635 // inside Workspaces app
636 BMenuItem* item;
638 menu->AddSeparatorItem();
639 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window tab"),
640 new BMessage(kMsgToggleTitle)));
641 if (window->Look() == B_TITLED_WINDOW_LOOK)
642 item->SetMarked(true);
643 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window border"),
644 new BMessage(kMsgToggleBorder)));
645 if (window->Look() == B_TITLED_WINDOW_LOOK
646 || window->Look() == B_MODAL_WINDOW_LOOK) {
647 item->SetMarked(true);
650 menu->AddSeparatorItem();
651 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Always on top"),
652 new BMessage(kMsgToggleAlwaysOnTop)));
653 if (window->Feel() == B_FLOATING_ALL_WINDOW_FEEL)
654 item->SetMarked(true);
655 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Auto-raise"),
656 new BMessage(kMsgToggleAutoRaise)));
657 if (window->IsAutoRaising())
658 item->SetMarked(true);
659 if (be_roster->IsRunning(kDeskbarSignature)) {
660 menu->AddItem(item = new BMenuItem(
661 B_TRANSLATE("Live in the Deskbar"),
662 new BMessage(kMsgToggleLiveInDeskbar)));
663 BDeskbar deskbar;
664 item->SetMarked(deskbar.HasItem(kDeskbarItemName));
667 menu->AddSeparatorItem();
668 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
669 new BMessage(B_QUIT_REQUESTED)));
670 menu->SetTargetForItems(window);
671 } else {
672 // we're replicated in some way...
673 BMenuItem* item;
675 menu->AddSeparatorItem();
677 // check which way
678 BDragger *dragger = dynamic_cast<BDragger*>(ChildAt(0));
679 if (dragger) {
680 // replicant
681 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Remove replicant"),
682 new BMessage(B_TRASH_TARGET)));
683 item->SetTarget(dragger);
684 } else {
685 // Deskbar item
686 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Remove replicant"),
687 new BMessage(kMsgToggleLiveInDeskbar)));
688 item->SetTarget(this);
692 changeItem->SetTarget(this);
693 switchItem->SetTarget(this);
695 ConvertToScreen(&where);
696 menu->Go(where, true, true, true);
700 void
701 WorkspacesView::SetSwitchOnWheel(bool enable)
703 if (enable == fSwitchOnWheel)
704 return;
706 fSwitchOnWheel = enable;
710 // #pragma mark - WorkspacesWindow
713 WorkspacesWindow::WorkspacesWindow(WorkspacesSettings *settings)
715 BWindow(settings->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Workspaces"),
716 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
717 B_AVOID_FRONT | B_WILL_ACCEPT_FIRST_CLICK | B_CLOSE_ON_ESCAPE,
718 B_ALL_WORKSPACES),
719 fSettings(settings),
720 fWorkspacesView(NULL)
722 // Turn window decor on to grab decor widths.
723 BMessage windowSettings;
724 float borderWidth = 0;
726 SetLook(B_TITLED_WINDOW_LOOK);
727 if (GetDecoratorSettings(&windowSettings) == B_OK) {
728 BRect tabFrame = windowSettings.FindRect("tab frame");
729 borderWidth = windowSettings.FindFloat("border width");
730 fTabHeight = tabFrame.Height();
731 fBorderWidth = borderWidth;
734 if (!fSettings->SettingsLoaded()) {
735 // No settings, compute a reasonable default frame.
736 // We aim for previews at 10% of actual screen size, and matching the
737 // aspect ratio. We then scale that down, until it fits the screen.
738 // Finally, we put the window on the bottom right of the screen so the
739 // auto-raise mode can be used.
741 BScreen screen;
743 float screenWidth = screen.Frame().Width();
744 float screenHeight = screen.Frame().Height();
745 float aspectRatio = screenWidth / screenHeight;
747 uint32 columns, rows;
748 BPrivate::get_workspaces_layout(&columns, &rows);
750 // default size of ~1/10 of screen width
751 float workspaceWidth = screenWidth / 10;
752 float workspaceHeight = workspaceWidth / aspectRatio;
754 float width = floor(workspaceWidth * columns);
755 float height = floor(workspaceHeight * rows);
757 // If you have too many workspaces to fit on the screen, shrink until
758 // they fit.
759 while (width + 2 * borderWidth > screenWidth
760 || height + 2 * borderWidth + GetTabHeight() > screenHeight) {
761 width = floor(0.95 * width);
762 height = floor(0.95 * height);
765 BRect frame = fSettings->ScreenFrame();
766 frame.OffsetBy(-2.0 * borderWidth, -2.0 * borderWidth);
767 frame.left = frame.right - width;
768 frame.top = frame.bottom - height;
769 ResizeTo(frame.Width(), frame.Height());
771 // Put it in bottom corner by default.
772 MoveTo(screenWidth - frame.Width() - borderWidth,
773 screenHeight - frame.Height() - borderWidth);
775 fSettings->SetWindowFrame(frame);
778 if (!fSettings->HasBorder())
779 SetLook(B_NO_BORDER_WINDOW_LOOK);
780 else if (!fSettings->HasTitle())
781 SetLook(B_MODAL_WINDOW_LOOK);
783 fWorkspacesView = new WorkspacesView(Bounds());
784 AddChild(fWorkspacesView);
786 if (fSettings->AlwaysOnTop())
787 SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
788 else
789 SetAutoRaise(fSettings->AutoRaising());
793 WorkspacesWindow::~WorkspacesWindow()
795 delete fSettings;
799 void
800 WorkspacesWindow::ScreenChanged(BRect rect, color_space mode)
802 fSettings->UpdateFramesForScreen(rect);
803 MoveTo(fSettings->WindowFrame().LeftTop());
807 void
808 WorkspacesWindow::FrameMoved(BPoint origin)
810 fSettings->SetWindowFrame(Frame());
814 void
815 WorkspacesWindow::FrameResized(float width, float height)
817 if (!(modifiers() & B_SHIFT_KEY)) {
818 BWindow::FrameResized(width, height);
819 return;
822 uint32 columns, rows;
823 BPrivate::get_workspaces_layout(&columns, &rows);
825 BScreen screen;
826 float screenWidth = screen.Frame().Width();
827 float screenHeight = screen.Frame().Height();
829 float windowAspectRatio
830 = (columns * screenWidth) / (rows * screenHeight);
832 float newHeight = width / windowAspectRatio;
834 if (height != newHeight)
835 ResizeTo(width, newHeight);
837 fSettings->SetWindowFrame(Frame());
841 void
842 WorkspacesWindow::Zoom(BPoint origin, float width, float height)
844 BScreen screen;
845 float screenWidth = screen.Frame().Width();
846 float screenHeight = screen.Frame().Height();
847 float aspectRatio = screenWidth / screenHeight;
849 uint32 columns, rows;
850 BPrivate::get_workspaces_layout(&columns, &rows);
852 float workspaceWidth = Frame().Width() / columns;
853 float workspaceHeight = workspaceWidth / aspectRatio;
855 width = floor(workspaceWidth * columns);
856 height = floor(workspaceHeight * rows);
858 while (width + 2 * GetScreenBorderOffset() > screenWidth
859 || height + 2 * GetScreenBorderOffset() + GetTabHeight()
860 > screenHeight) {
861 width = floor(0.95 * width);
862 height = floor(0.95 * height);
865 ResizeTo(width, height);
867 if (fSettings->AutoRaising()) {
868 // The auto-raising mode makes sense only if the window is positionned
869 // exactly in the bottom-right corner. If the setting is enabled, move
870 // the window there.
871 origin = screen.Frame().RightBottom();
872 origin.x -= GetScreenBorderOffset() + width;
873 origin.y -= GetScreenBorderOffset() + height;
875 MoveTo(origin);
880 void
881 WorkspacesWindow::MessageReceived(BMessage *message)
883 switch (message->what) {
884 case B_SIMPLE_DATA:
886 // Drop from Tracker
887 entry_ref ref;
888 for (int i = 0; (message->FindRef("refs", i, &ref) == B_OK); i++)
889 be_roster->Launch(&ref);
890 break;
893 case B_ABOUT_REQUESTED:
894 PostMessage(message, ChildAt(0));
895 break;
897 case kMsgToggleBorder:
899 bool enable = false;
900 if (Look() == B_NO_BORDER_WINDOW_LOOK)
901 enable = true;
903 if (enable)
904 if (fSettings->HasTitle())
905 SetLook(B_TITLED_WINDOW_LOOK);
906 else
907 SetLook(B_MODAL_WINDOW_LOOK);
908 else
909 SetLook(B_NO_BORDER_WINDOW_LOOK);
911 fSettings->SetHasBorder(enable);
912 break;
915 case kMsgToggleTitle:
917 bool enable = false;
918 if (Look() == B_MODAL_WINDOW_LOOK
919 || Look() == B_NO_BORDER_WINDOW_LOOK)
920 enable = true;
922 if (enable)
923 SetLook(B_TITLED_WINDOW_LOOK);
924 else
925 SetLook(B_MODAL_WINDOW_LOOK);
927 // No matter what the setting for title, we must force the border on
928 fSettings->SetHasBorder(true);
929 fSettings->SetHasTitle(enable);
930 break;
933 case kMsgToggleAutoRaise:
934 SetAutoRaise(!IsAutoRaising());
935 SetFeel(B_NORMAL_WINDOW_FEEL);
936 break;
938 case kMsgToggleAlwaysOnTop:
940 bool enable = false;
941 if (Feel() != B_FLOATING_ALL_WINDOW_FEEL)
942 enable = true;
944 if (enable)
945 SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
946 else
947 SetFeel(B_NORMAL_WINDOW_FEEL);
949 fSettings->SetAlwaysOnTop(enable);
950 break;
953 case kMsgToggleLiveInDeskbar:
955 BDeskbar deskbar;
956 if (deskbar.HasItem(kDeskbarItemName))
957 deskbar.RemoveItem(kDeskbarItemName);
958 else {
959 fWorkspacesView->_SaveSettings();
960 // save "switch on wheel" setting for replicant to load
961 entry_ref ref;
962 be_roster->FindApp(kSignature, &ref);
963 deskbar.AddItem(&ref);
965 break;
968 default:
969 BWindow::MessageReceived(message);
970 break;
975 bool
976 WorkspacesWindow::QuitRequested()
978 be_app->PostMessage(B_QUIT_REQUESTED);
979 return true;
983 void
984 WorkspacesWindow::SetAutoRaise(bool enable)
986 fSettings->SetAutoRaising(enable);
988 if (enable)
989 ChildAt(0)->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
990 else
991 ChildAt(0)->SetEventMask(0);
995 // #pragma mark - WorkspacesApp
998 WorkspacesApp::WorkspacesApp()
999 : BApplication(kSignature)
1001 fWindow = new WorkspacesWindow(new WorkspacesSettings());
1005 WorkspacesApp::~WorkspacesApp()
1010 void
1011 WorkspacesApp::AboutRequested()
1013 fWindow->PostMessage(B_ABOUT_REQUESTED);
1017 void
1018 WorkspacesApp::Usage(const char *programName)
1020 printf(B_TRANSLATE("Usage: %s [options] [workspace]\n"
1021 "where \"options\" are:\n"
1022 " --notitle\t\ttitle bar removed, border and resize kept\n"
1023 " --noborder\t\ttitle, border, and resize removed\n"
1024 " --avoidfocus\t\tprevents the window from being the target of "
1025 "keyboard events\n"
1026 " --alwaysontop\t\tkeeps window on top\n"
1027 " --notmovable\t\twindow can't be moved around\n"
1028 " --autoraise\t\tauto-raise the workspace window when it's at the "
1029 "screen edge\n"
1030 " --help\t\tdisplay this help and exit\n"
1031 "and \"workspace\" is the number of the Workspace to which to switch "
1032 "(0-31)\n"),
1033 programName);
1035 // quit only if we aren't running already
1036 if (IsLaunching())
1037 Quit();
1041 void
1042 WorkspacesApp::ArgvReceived(int32 argc, char **argv)
1044 for (int i = 1; i < argc; i++) {
1045 if (argv[i][0] == '-' && argv[i][1] == '-') {
1046 // evaluate --arguments
1047 if (!strcmp(argv[i], "--notitle"))
1048 fWindow->SetLook(B_MODAL_WINDOW_LOOK);
1049 else if (!strcmp(argv[i], "--noborder"))
1050 fWindow->SetLook(B_NO_BORDER_WINDOW_LOOK);
1051 else if (!strcmp(argv[i], "--avoidfocus"))
1052 fWindow->SetFlags(fWindow->Flags() | B_AVOID_FOCUS);
1053 else if (!strcmp(argv[i], "--notmovable"))
1054 fWindow->SetFlags(fWindow->Flags() | B_NOT_MOVABLE);
1055 else if (!strcmp(argv[i], "--alwaysontop"))
1056 fWindow->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
1057 else if (!strcmp(argv[i], "--autoraise"))
1058 fWindow->SetAutoRaise(true);
1059 else {
1060 const char *programName = strrchr(argv[0], '/');
1061 programName = programName ? programName + 1 : argv[0];
1063 Usage(programName);
1065 } else if (isdigit(*argv[i])) {
1066 // check for a numeric arg, if not already given
1067 activate_workspace(atoi(argv[i]));
1069 // if the app is running, don't quit
1070 // but if it isn't, cancel the complete run, so it doesn't
1071 // open any window
1072 if (IsLaunching())
1073 Quit();
1074 } else if (!strcmp(argv[i], "-")) {
1075 activate_workspace(current_workspace() - 1);
1077 if (IsLaunching())
1078 Quit();
1079 } else if (!strcmp(argv[i], "+")) {
1080 activate_workspace(current_workspace() + 1);
1082 if (IsLaunching())
1083 Quit();
1084 } else {
1085 // some unknown arguments were specified
1086 fprintf(stderr, B_TRANSLATE("Invalid argument: %s\n"), argv[i]);
1088 if (IsLaunching())
1089 Quit();
1095 BView* instantiate_deskbar_item()
1097 // Calculate the correct size of the Deskbar replicant first
1099 BScreen screen;
1100 float screenWidth = screen.Frame().Width();
1101 float screenHeight = screen.Frame().Height();
1102 float aspectRatio = screenWidth / screenHeight;
1103 uint32 columns, rows;
1104 BPrivate::get_workspaces_layout(&columns, &rows);
1106 // ╔═╤═╕ A Deskbar replicant can be 16px tall and 129px wide at most.
1107 // ║ │ │ We use 1px for the top and left borders (shown as double)
1108 // ╟─┼─┤ and divide the remainder equally. However, we keep in mind
1109 // ║ │ │ that the actual width and height of each workspace is smaller
1110 // ╙─┴─┘ by 1px, because of bottom/right borders (shown as single).
1111 // When calculating workspace width, we must ensure that the assumed
1112 // actual workspace height is not negative. Zero is OK.
1114 float height = 16;
1115 float rowHeight = floor((height - 1) / rows);
1116 if (rowHeight < 1)
1117 rowHeight = 1;
1119 float columnWidth = floor((rowHeight - 1) * aspectRatio) + 1;
1121 float width = columnWidth * columns + 1;
1122 if (width > 129)
1123 width = 129;
1125 return new WorkspacesView(BRect (0, 0, width - 1, height - 1), false);
1129 void
1130 WorkspacesApp::ReadyToRun()
1132 fWindow->Show();
1136 // #pragma mark -
1140 main(int argc, char **argv)
1142 WorkspacesApp app;
1143 app.Run();
1145 return 0;