Workspaces.cpp revision fc111138
1/*
2 * Copyright 2002-2009, 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.
5 *
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 */
12
13
14#include <Alert.h>
15#include <Application.h>
16#include <Catalog.h>
17#include <Deskbar.h>
18#include <Dragger.h>
19#include <Entry.h>
20#include <File.h>
21#include <FindDirectory.h>
22#include <Locale.h>
23#include <MenuItem.h>
24#include <Path.h>
25#include <PopUpMenu.h>
26#include <Roster.h>
27#include <Screen.h>
28#include <TextView.h>
29#include <Window.h>
30
31#include <ctype.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include <InterfacePrivate.h>
37#include <ViewPrivate.h>
38#include <WindowPrivate.h>
39
40#undef B_TRANSLATE_CONTEXT
41#define B_TRANSLATE_CONTEXT "Workspaces"
42
43
44static const char* kDeskbarItemName = "workspaces";
45static const char* kSignature = "application/x-vnd.Be-WORK";
46static const char* kDeskbarSignature = "application/x-vnd.Be-TSKB";
47static const char* kScreenPrefletSignature = "application/x-vnd.Haiku-Screen";
48static const char* kOldSettingFile = "Workspace_data";
49static const char* kSettingsFile = "Workspaces_settings";
50
51static const uint32 kMsgChangeCount = 'chWC';
52static const uint32 kMsgToggleTitle = 'tgTt';
53static const uint32 kMsgToggleBorder = 'tgBd';
54static const uint32 kMsgToggleAutoRaise = 'tgAR';
55static const uint32 kMsgToggleAlwaysOnTop = 'tgAT';
56static const uint32 kMsgToggleLiveInDeskbar = 'tgDb';
57
58static const float kScreenBorderOffset = 10.0;
59
60extern "C" _EXPORT BView* instantiate_deskbar_item();
61
62class WorkspacesSettings {
63	public:
64		WorkspacesSettings();
65		virtual ~WorkspacesSettings();
66
67		BRect WindowFrame() const { return fWindowFrame; }
68		BRect ScreenFrame() const { return fScreenFrame; }
69
70		bool AutoRaising() const { return fAutoRaising; }
71		bool AlwaysOnTop() const { return fAlwaysOnTop; }
72		bool HasTitle() const { return fHasTitle; }
73		bool HasBorder() const { return fHasBorder; }
74
75		void UpdateFramesForScreen(BRect screenFrame);
76		void UpdateScreenFrame();
77
78		void SetWindowFrame(BRect);
79		void SetAutoRaising(bool enable) { fAutoRaising = enable; }
80		void SetAlwaysOnTop(bool enable) { fAlwaysOnTop = enable; }
81		void SetHasTitle(bool enable) { fHasTitle = enable; }
82		void SetHasBorder(bool enable) { fHasBorder = enable; }
83
84	private:
85		status_t _Open(BFile& file, int mode);
86
87		BRect	fWindowFrame;
88		BRect	fScreenFrame;
89		bool	fAutoRaising;
90		bool	fAlwaysOnTop;
91		bool	fHasTitle;
92		bool	fHasBorder;
93};
94
95class WorkspacesView : public BView {
96	public:
97		WorkspacesView(BRect frame, bool showDragger);
98		WorkspacesView(BMessage* archive);
99		~WorkspacesView();
100
101		static	WorkspacesView* Instantiate(BMessage* archive);
102		virtual	status_t Archive(BMessage* archive, bool deep = true) const;
103
104		virtual void AttachedToWindow();
105		virtual void DetachedFromWindow();
106		virtual void FrameMoved(BPoint newPosition);
107		virtual void FrameResized(float newWidth, float newHeight);
108		virtual void MessageReceived(BMessage* message);
109		virtual void MouseMoved(BPoint where, uint32 transit,
110			const BMessage* dragMessage);
111		virtual void MouseDown(BPoint where);
112
113	private:
114		void _AboutRequested();
115
116		void _UpdateParentClipping();
117		void _ExcludeFromParentClipping();
118		void _CleanupParentClipping();
119
120		BView*	fParentWhichDrawsOnChildren;
121		BRect	fCurrentFrame;
122};
123
124class WorkspacesWindow : public BWindow {
125	public:
126		WorkspacesWindow(WorkspacesSettings *settings);
127		virtual ~WorkspacesWindow();
128
129		virtual void ScreenChanged(BRect frame, color_space mode);
130		virtual void FrameMoved(BPoint origin);
131		virtual void FrameResized(float width, float height);
132		virtual void Zoom(BPoint origin, float width, float height);
133
134		virtual void MessageReceived(BMessage *msg);
135		virtual bool QuitRequested();
136
137		void SetAutoRaise(bool enable);
138		bool IsAutoRaising() const { return fAutoRaising; }
139
140	private:
141		WorkspacesSettings *fSettings;
142		bool	fAutoRaising;
143};
144
145class WorkspacesApp : public BApplication {
146	public:
147		WorkspacesApp();
148		virtual ~WorkspacesApp();
149
150		virtual void AboutRequested();
151		virtual void ArgvReceived(int32 argc, char **argv);
152		virtual void ReadyToRun();
153
154		void Usage(const char *programName);
155
156	private:
157		WorkspacesWindow*	fWindow;
158};
159
160
161WorkspacesSettings::WorkspacesSettings()
162	:
163	fAutoRaising(false),
164	fAlwaysOnTop(false),
165	fHasTitle(true),
166	fHasBorder(true)
167{
168	UpdateScreenFrame();
169
170	bool loaded = false;
171	BScreen screen;
172
173	BFile file;
174	if (_Open(file, B_READ_ONLY) == B_OK) {
175		BMessage settings;
176		if (settings.Unflatten(&file) == B_OK) {
177			if (settings.FindRect("window", &fWindowFrame) == B_OK
178				&& settings.FindRect("screen", &fScreenFrame) == B_OK)
179				loaded = true;
180
181			settings.FindBool("auto-raise", &fAutoRaising);
182			settings.FindBool("always on top", &fAlwaysOnTop);
183
184			if (settings.FindBool("has title", &fHasTitle) != B_OK)
185				fHasTitle = true;
186			if (settings.FindBool("has border", &fHasBorder) != B_OK)
187				fHasBorder = true;
188		}
189	} else {
190		// try reading BeOS compatible settings
191		BPath path;
192		if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
193			path.Append(kOldSettingFile);
194			BFile file(path.Path(), B_READ_ONLY);
195			if (file.InitCheck() == B_OK
196				&& file.Read(&fWindowFrame, sizeof(BRect)) == sizeof(BRect)) {
197				// we now also store the frame of the screen to know
198				// in which context the window frame has been chosen
199				BRect frame;
200				if (file.Read(&frame, sizeof(BRect)) == sizeof(BRect))
201					fScreenFrame = frame;
202				else
203					fScreenFrame = screen.Frame();
204
205				loaded = true;
206			}
207		}
208	}
209
210	if (loaded) {
211		// if the current screen frame is different from the one
212		// just loaded, we need to alter the window frame accordingly
213		if (fScreenFrame != screen.Frame())
214			UpdateFramesForScreen(screen.Frame());
215	}
216
217	if (!loaded
218		|| !(screen.Frame().right + 5 >= fWindowFrame.right
219			&& screen.Frame().bottom + 5 >= fWindowFrame.bottom
220			&& screen.Frame().left - 5 <= fWindowFrame.left
221			&& screen.Frame().top - 5 <= fWindowFrame.top)) {
222		// set to some usable defaults
223		float screenWidth = screen.Frame().Width();
224		float screenHeight = screen.Frame().Height();
225		float aspectRatio = screenWidth / screenHeight;
226
227		uint32 columns, rows;
228		BPrivate::get_workspaces_layout(&columns, &rows);
229
230		// default size of ~1/10 of screen width
231		float workspaceWidth = screenWidth / 10;
232		float workspaceHeight = workspaceWidth / aspectRatio;
233
234		float width = floor(workspaceWidth * columns);
235		float height = floor(workspaceHeight * rows);
236
237		float tabHeight = 20;
238			// TODO: find tabHeight without being a window
239
240		// shrink to fit more
241		while (width + 2 * kScreenBorderOffset > screenWidth
242			|| height + 2 * kScreenBorderOffset + tabHeight > screenHeight) {
243			width = floor(0.95 * width);
244			height = floor(0.95 * height);
245		}
246
247		fWindowFrame = fScreenFrame;
248		fWindowFrame.OffsetBy(-kScreenBorderOffset, -kScreenBorderOffset);
249		fWindowFrame.left = fWindowFrame.right - width;
250		fWindowFrame.top = fWindowFrame.bottom - height;
251	}
252}
253
254
255WorkspacesSettings::~WorkspacesSettings()
256{
257	// write settings file
258	BFile file;
259	if (_Open(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK)
260		return;
261
262	BMessage settings('wksp');
263
264	if (settings.AddRect("window", fWindowFrame) == B_OK
265		&& settings.AddRect("screen", fScreenFrame) == B_OK
266		&& settings.AddBool("auto-raise", fAutoRaising) == B_OK
267		&& settings.AddBool("always on top", fAlwaysOnTop) == B_OK
268		&& settings.AddBool("has title", fHasTitle) == B_OK
269		&& settings.AddBool("has border", fHasBorder) == B_OK)
270		settings.Flatten(&file);
271}
272
273
274status_t
275WorkspacesSettings::_Open(BFile& file, int mode)
276{
277	BPath path;
278	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
279	 if (status != B_OK)
280		status = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path);
281	 if (status != B_OK)
282		return status;
283
284	path.Append(kSettingsFile);
285
286	status = file.SetTo(path.Path(), mode);
287	if (mode == B_READ_ONLY && status == B_ENTRY_NOT_FOUND) {
288		if (find_directory(B_COMMON_SETTINGS_DIRECTORY, &path) == B_OK) {
289			path.Append(kSettingsFile);
290			status = file.SetTo(path.Path(), mode);
291		}
292	}
293
294	return status;
295}
296
297
298void
299WorkspacesSettings::UpdateFramesForScreen(BRect newScreenFrame)
300{
301	// don't change the position if the screen frame hasn't changed
302	if (newScreenFrame == fScreenFrame)
303		return;
304
305	// adjust horizontal position
306	if (fWindowFrame.right > fScreenFrame.right / 2) {
307		fWindowFrame.OffsetTo(newScreenFrame.right
308			- (fScreenFrame.right - fWindowFrame.left), fWindowFrame.top);
309	}
310
311	// adjust vertical position
312	if (fWindowFrame.bottom > fScreenFrame.bottom / 2) {
313		fWindowFrame.OffsetTo(fWindowFrame.left,
314			newScreenFrame.bottom - (fScreenFrame.bottom - fWindowFrame.top));
315	}
316
317	fScreenFrame = newScreenFrame;
318}
319
320
321void
322WorkspacesSettings::UpdateScreenFrame()
323{
324	BScreen screen;
325	fScreenFrame = screen.Frame();
326}
327
328
329void
330WorkspacesSettings::SetWindowFrame(BRect frame)
331{
332	fWindowFrame = frame;
333}
334
335
336//	#pragma mark -
337
338
339WorkspacesView::WorkspacesView(BRect frame, bool showDragger=true)
340	:
341	BView(frame, kDeskbarItemName, B_FOLLOW_ALL,
342		kWorkspacesViewFlag | B_FRAME_EVENTS),
343	fParentWhichDrawsOnChildren(NULL),
344	fCurrentFrame(frame)
345{
346	if(showDragger) {
347		frame.OffsetTo(B_ORIGIN);
348		frame.top = frame.bottom - 7;
349		frame.left = frame.right - 7;
350		BDragger* dragger = new BDragger(frame, this,
351			B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
352		AddChild(dragger);
353	}
354}
355
356
357WorkspacesView::WorkspacesView(BMessage* archive)
358	:
359	BView(archive),
360	fParentWhichDrawsOnChildren(NULL),
361	fCurrentFrame(Frame())
362{
363	// Just in case we are instantiated from an older archive...
364	SetFlags(Flags() | B_FRAME_EVENTS);
365	// Make sure the auto-raise feature didn't leave any artifacts - this is
366	// not a good idea to keep enabled for a replicant.
367	if (EventMask() != 0)
368		SetEventMask(0);
369}
370
371
372WorkspacesView::~WorkspacesView()
373{
374}
375
376
377/*static*/ WorkspacesView*
378WorkspacesView::Instantiate(BMessage* archive)
379{
380	if (!validate_instantiation(archive, "WorkspacesView"))
381		return NULL;
382
383	return new WorkspacesView(archive);
384}
385
386
387status_t
388WorkspacesView::Archive(BMessage* archive, bool deep) const
389{
390	status_t status = BView::Archive(archive, deep);
391	if (status == B_OK)
392		status = archive->AddString("add_on", kSignature);
393	if (status == B_OK)
394		status = archive->AddString("class", "WorkspacesView");
395
396	return status;
397}
398
399
400void
401WorkspacesView::_AboutRequested()
402{
403	BString text = B_TRANSLATE("Workspaces\n"
404		"written by %1, and %2.\n\n"
405		"Copyright %3, Haiku.\n\n"
406		"Send windows behind using the Option key. "
407		"Move windows to front using the Control key.\n");
408	text.ReplaceFirst("%1", "Fran��ois Revol, Axel D��rfler");
409	text.ReplaceFirst("%2", "Matt Madia");
410	text.ReplaceFirst("%3", "2002-2008");
411
412	BAlert *alert = new BAlert("about", text.String(), B_TRANSLATE("OK"));
413	BTextView *view = alert->TextView();
414	BFont font;
415
416	view->SetStylable(true);
417
418	view->GetFont(&font);
419	font.SetSize(18);
420	font.SetFace(B_BOLD_FACE);
421	view->SetFontAndColor(0, 10, &font);
422
423	alert->Go();
424}
425
426
427void
428WorkspacesView::AttachedToWindow()
429{
430	BView* parent = Parent();
431	if (parent != NULL && (parent->Flags() & B_DRAW_ON_CHILDREN) != 0) {
432		fParentWhichDrawsOnChildren = parent;
433		_ExcludeFromParentClipping();
434	}
435}
436
437
438void
439WorkspacesView::DetachedFromWindow()
440{
441	if (fParentWhichDrawsOnChildren != NULL)
442		_CleanupParentClipping();
443}
444
445
446void
447WorkspacesView::FrameMoved(BPoint newPosition)
448{
449	_UpdateParentClipping();
450}
451
452
453void
454WorkspacesView::FrameResized(float newWidth, float newHeight)
455{
456	_UpdateParentClipping();
457}
458
459
460void
461WorkspacesView::_UpdateParentClipping()
462{
463	if (fParentWhichDrawsOnChildren != NULL) {
464		_CleanupParentClipping();
465		_ExcludeFromParentClipping();
466		fParentWhichDrawsOnChildren->Invalidate(fCurrentFrame);
467		fCurrentFrame = Frame();
468	}
469}
470
471
472void
473WorkspacesView::_ExcludeFromParentClipping()
474{
475	// Prevent the parent view to draw over us. Do so in a way that allows
476	// restoring the parent to the previous state.
477	fParentWhichDrawsOnChildren->PushState();
478
479	BRegion clipping(fParentWhichDrawsOnChildren->Bounds());
480	clipping.Exclude(Frame());
481	fParentWhichDrawsOnChildren->ConstrainClippingRegion(&clipping);
482}
483
484
485void
486WorkspacesView::_CleanupParentClipping()
487{
488	// Restore the previous parent state. NOTE: This relies on views
489	// being detached in exactly the opposite order as them being
490	// attached. Otherwise we would mess up states if a sibbling view did
491	// the same thing we did in AttachedToWindow()...
492	fParentWhichDrawsOnChildren->PopState();
493}
494
495
496void
497WorkspacesView::MessageReceived(BMessage* message)
498{
499	switch (message->what) {
500		case B_ABOUT_REQUESTED:
501			_AboutRequested();
502			break;
503
504		case kMsgChangeCount:
505			be_roster->Launch(kScreenPrefletSignature);
506			break;
507
508		case kMsgToggleLiveInDeskbar:
509		{
510			// only actually used from the replicant itself
511			// since HasItem() locks up we just remove directly.
512			BDeskbar deskbar;
513			// we shouldn't do this here actually, but it works for now...
514			deskbar.RemoveItem (kDeskbarItemName);
515			break;
516		}
517
518		default:
519			BView::MessageReceived(message);
520			break;
521	}
522}
523
524
525void
526WorkspacesView::MouseMoved(BPoint where, uint32 transit,
527	const BMessage* dragMessage)
528{
529	WorkspacesWindow* window = dynamic_cast<WorkspacesWindow*>(Window());
530	if (window == NULL || !window->IsAutoRaising())
531		return;
532
533	// Auto-Raise
534
535	where = ConvertToScreen(where);
536	BScreen screen(window);
537	BRect frame = screen.Frame();
538	if (where.x == frame.left || where.x == frame.right
539		|| where.y == frame.top || where.y == frame.bottom) {
540		// cursor is on screen edge
541		if (window->Frame().Contains(where))
542			window->Activate();
543	}
544}
545
546
547void
548WorkspacesView::MouseDown(BPoint where)
549{
550	// With enabled auto-raise feature, we'll get mouse messages we don't
551	// want to handle here.
552	if (!Bounds().Contains(where))
553		return;
554
555	int32 buttons = 0;
556	if (Window() != NULL && Window()->CurrentMessage() != NULL)
557		Window()->CurrentMessage()->FindInt32("buttons", &buttons);
558
559	if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
560		return;
561
562	// open context menu
563
564	BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false);
565	menu->SetFont(be_plain_font);
566
567	// TODO: alternatively change the count here directly?
568	BMenuItem* changeItem = new BMenuItem(B_TRANSLATE("Change workspace count"
569		B_UTF8_ELLIPSIS), new BMessage(kMsgChangeCount));
570	menu->AddItem(changeItem);
571
572	WorkspacesWindow* window = dynamic_cast<WorkspacesWindow*>(Window());
573	if (window != NULL) {
574		// inside Workspaces app
575		BMenuItem* item;
576
577		menu->AddSeparatorItem();
578		menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window tab"),
579			new BMessage(kMsgToggleTitle)));
580		if (window->Look() == B_TITLED_WINDOW_LOOK)
581			item->SetMarked(true);
582		menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window border"),
583			new BMessage(kMsgToggleBorder)));
584		if (window->Look() == B_TITLED_WINDOW_LOOK
585			|| window->Look() == B_MODAL_WINDOW_LOOK)
586			item->SetMarked(true);
587
588		menu->AddSeparatorItem();
589		menu->AddItem(item = new BMenuItem(B_TRANSLATE("Always on top"),
590			new BMessage(kMsgToggleAlwaysOnTop)));
591		if (window->Feel() == B_FLOATING_ALL_WINDOW_FEEL)
592			item->SetMarked(true);
593		menu->AddItem(item = new BMenuItem(B_TRANSLATE("Auto-raise"),
594			new BMessage(kMsgToggleAutoRaise)));
595		if (window->IsAutoRaising())
596			item->SetMarked(true);
597		if (be_roster->IsRunning(kDeskbarSignature)) {
598			menu->AddItem(item = new BMenuItem(B_TRANSLATE("Live in the Deskbar"),
599			new BMessage(kMsgToggleLiveInDeskbar)));
600			BDeskbar deskbar;
601			item->SetMarked(deskbar.HasItem(kDeskbarItemName));
602		}
603
604		menu->AddSeparatorItem();
605		menu->AddItem(new BMenuItem(B_TRANSLATE("About Workspaces"
606			B_UTF8_ELLIPSIS), new BMessage(B_ABOUT_REQUESTED)));
607		menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
608			new BMessage(B_QUIT_REQUESTED)));
609		menu->SetTargetForItems(window);
610	} else {
611		// we're replicated in some way...
612		BMenuItem* item;
613
614		menu->AddSeparatorItem();
615
616		// check which way
617		BDragger *dragger = dynamic_cast<BDragger*>(ChildAt(0));
618		if (dragger) {
619			// replicant
620			menu->AddItem(item = new BMenuItem(B_TRANSLATE("Remove replicant"),
621				new BMessage(B_TRASH_TARGET)));
622			item->SetTarget(dragger);
623		} else {
624			// Deskbar item
625			menu->AddItem(item = new BMenuItem(B_TRANSLATE("Remove replicant"),
626				new BMessage(kMsgToggleLiveInDeskbar)));
627			item->SetTarget(this);
628		}
629	}
630
631	changeItem->SetTarget(this);
632	ConvertToScreen(&where);
633	menu->Go(where, true, true, true);
634}
635
636
637//	#pragma mark -
638
639
640WorkspacesWindow::WorkspacesWindow(WorkspacesSettings *settings)
641	:
642	BWindow(settings->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Workspaces"),
643		B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
644		B_AVOID_FRONT | B_WILL_ACCEPT_FIRST_CLICK, B_ALL_WORKSPACES),
645 	fSettings(settings),
646 	fAutoRaising(false)
647{
648	AddChild(new WorkspacesView(Bounds()));
649
650	if (!fSettings->HasBorder())
651		SetLook(B_NO_BORDER_WINDOW_LOOK);
652	else if (!fSettings->HasTitle())
653		SetLook(B_MODAL_WINDOW_LOOK);
654
655	if (fSettings->AlwaysOnTop())
656		SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
657	else
658		SetAutoRaise(fSettings->AutoRaising());
659}
660
661
662WorkspacesWindow::~WorkspacesWindow()
663{
664	delete fSettings;
665}
666
667
668void
669WorkspacesWindow::ScreenChanged(BRect rect, color_space mode)
670{
671	fSettings->UpdateFramesForScreen(rect);
672	MoveTo(fSettings->WindowFrame().LeftTop());
673}
674
675
676void
677WorkspacesWindow::FrameMoved(BPoint origin)
678{
679	fSettings->SetWindowFrame(Frame());
680}
681
682
683void
684WorkspacesWindow::FrameResized(float width, float height)
685{
686	if (!modifiers() & B_SHIFT_KEY) {
687		BWindow::FrameResized(width, height);
688		return;
689	}
690
691	uint32 columns, rows;
692	BPrivate::get_workspaces_layout(&columns, &rows);
693
694	BScreen screen;
695	float screenWidth = screen.Frame().Width();
696	float screenHeight = screen.Frame().Height();
697
698	float windowAspectRatio
699		= (columns * screenWidth) / (rows * screenHeight);
700
701	float newHeight = width / windowAspectRatio;
702
703	if (height != newHeight)
704		ResizeTo(width, newHeight);
705
706	fSettings->SetWindowFrame(Frame());
707}
708
709
710void
711WorkspacesWindow::Zoom(BPoint origin, float width, float height)
712{
713	BScreen screen;
714	float screenWidth = screen.Frame().Width();
715	float screenHeight = screen.Frame().Height();
716	float aspectRatio = screenWidth / screenHeight;
717
718	uint32 columns, rows;
719	BPrivate::get_workspaces_layout(&columns, &rows);
720
721	float workspaceWidth = screenWidth / 10;
722	float workspaceHeight = workspaceWidth / aspectRatio;
723
724	width = floor(workspaceWidth * columns);
725	height = floor(workspaceHeight * rows);
726
727	float tabHeight = Frame().top - DecoratorFrame().top;
728
729	while (width + 2 * kScreenBorderOffset > screenWidth
730		|| height + 2 * kScreenBorderOffset + tabHeight > screenHeight) {
731		width = floor(0.95 * width);
732		height = floor(0.95 * height);
733	}
734
735	ResizeTo(width, height);
736
737	origin = screen.Frame().RightBottom();
738	origin.x -= kScreenBorderOffset + width;
739	origin.y -= kScreenBorderOffset + height;
740
741	MoveTo(origin);
742}
743
744
745void
746WorkspacesWindow::MessageReceived(BMessage *message)
747{
748	switch (message->what) {
749		case B_SIMPLE_DATA:
750		{
751			// Drop from Tracker
752			entry_ref ref;
753			for (int i = 0; (message->FindRef("refs", i, &ref) == B_OK); i++)
754				be_roster->Launch(&ref);
755			break;
756		}
757
758		case B_ABOUT_REQUESTED:
759			PostMessage(message, ChildAt(0));
760			break;
761
762		case kMsgToggleBorder:
763		{
764			bool enable = false;
765			if (Look() == B_NO_BORDER_WINDOW_LOOK)
766				enable = true;
767
768			if (enable)
769				if (fSettings->HasTitle())
770					SetLook(B_TITLED_WINDOW_LOOK);
771				else
772					SetLook(B_MODAL_WINDOW_LOOK);
773			else
774				SetLook(B_NO_BORDER_WINDOW_LOOK);
775
776			fSettings->SetHasBorder(enable);
777			break;
778		}
779
780		case kMsgToggleTitle:
781		{
782			bool enable = false;
783			if (Look() == B_MODAL_WINDOW_LOOK
784				|| Look() == B_NO_BORDER_WINDOW_LOOK)
785				enable = true;
786
787			if (enable)
788				SetLook(B_TITLED_WINDOW_LOOK);
789			else
790				SetLook(B_MODAL_WINDOW_LOOK);
791
792			// No matter what the setting for title,
793			// we must force the border on
794			fSettings->SetHasBorder(true);
795			fSettings->SetHasTitle(enable);
796			break;
797		}
798
799		case kMsgToggleAutoRaise:
800			SetAutoRaise(!IsAutoRaising());
801			SetFeel(B_NORMAL_WINDOW_FEEL);
802			break;
803
804		case kMsgToggleAlwaysOnTop:
805		{
806			bool enable = false;
807			if (Feel() != B_FLOATING_ALL_WINDOW_FEEL)
808				enable = true;
809
810			if (enable)
811				SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
812			else
813				SetFeel(B_NORMAL_WINDOW_FEEL);
814
815			fSettings->SetAlwaysOnTop(enable);
816			break;
817		}
818
819		case kMsgToggleLiveInDeskbar:
820		{
821			BDeskbar deskbar;
822			if (deskbar.HasItem (kDeskbarItemName))
823				deskbar.RemoveItem (kDeskbarItemName);
824			else {
825				entry_ref ref;
826				be_roster->FindApp(kSignature, &ref);
827				deskbar.AddItem(&ref);
828			}
829			break;
830		}
831
832		default:
833			BWindow::MessageReceived(message);
834			break;
835	}
836}
837
838
839bool
840WorkspacesWindow::QuitRequested()
841{
842	be_app->PostMessage(B_QUIT_REQUESTED);
843	return true;
844}
845
846
847void
848WorkspacesWindow::SetAutoRaise(bool enable)
849{
850	if (enable == fAutoRaising)
851		return;
852
853	fAutoRaising = enable;
854	fSettings->SetAutoRaising(enable);
855
856	if (enable)
857		ChildAt(0)->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
858	else
859		ChildAt(0)->SetEventMask(0);
860}
861
862
863//	#pragma mark -
864
865
866WorkspacesApp::WorkspacesApp()
867	: BApplication(kSignature)
868{
869	fWindow = new WorkspacesWindow(new WorkspacesSettings());
870}
871
872
873WorkspacesApp::~WorkspacesApp()
874{
875}
876
877
878void
879WorkspacesApp::AboutRequested()
880{
881	fWindow->PostMessage(B_ABOUT_REQUESTED);
882}
883
884
885void
886WorkspacesApp::Usage(const char *programName)
887{
888	printf(B_TRANSLATE("Usage: %s [options] [workspace]\n"
889		"where \"options\" are:\n"
890		"  --notitle\t\ttitle bar removed, border and resize kept\n"
891		"  --noborder\t\ttitle, border, and resize removed\n"
892		"  --avoidfocus\t\tprevents the window from being the target of "
893		"keyboard events\n"
894		"  --alwaysontop\t\tkeeps window on top\n"
895		"  --notmovable\t\twindow can't be moved around\n"
896		"  --autoraise\t\tauto-raise the workspace window when it's at the "
897		"screen edge\n"
898		"  --help\t\tdisplay this help and exit\n"
899		"and \"workspace\" is the number of the Workspace to which to switch "
900		"(0-31)\n"),
901		programName);
902
903	// quit only if we aren't running already
904	if (IsLaunching())
905		Quit();
906}
907
908
909void
910WorkspacesApp::ArgvReceived(int32 argc, char **argv)
911{
912	for (int i = 1;  i < argc;  i++) {
913		if (argv[i][0] == '-' && argv[i][1] == '-') {
914			// evaluate --arguments
915			if (!strcmp(argv[i], "--notitle"))
916				fWindow->SetLook(B_MODAL_WINDOW_LOOK);
917			else if (!strcmp(argv[i], "--noborder"))
918				fWindow->SetLook(B_NO_BORDER_WINDOW_LOOK);
919			else if (!strcmp(argv[i], "--avoidfocus"))
920				fWindow->SetFlags(fWindow->Flags() | B_AVOID_FOCUS);
921			else if (!strcmp(argv[i], "--notmovable"))
922				fWindow->SetFlags(fWindow->Flags() | B_NOT_MOVABLE);
923			else if (!strcmp(argv[i], "--alwaysontop"))
924				fWindow->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
925			else if (!strcmp(argv[i], "--autoraise"))
926				fWindow->SetAutoRaise(true);
927			else {
928				const char *programName = strrchr(argv[0], '/');
929				programName = programName ? programName + 1 : argv[0];
930
931				Usage(programName);
932			}
933		} else if (isdigit(*argv[i])) {
934			// check for a numeric arg, if not already given
935			activate_workspace(atoi(argv[i]));
936
937			// if the app is running, don't quit
938			// but if it isn't, cancel the complete run, so it doesn't
939			// open any window
940			if (IsLaunching())
941				Quit();
942		} else if (!strcmp(argv[i], "-")) {
943			activate_workspace(current_workspace() - 1);
944
945			if (IsLaunching())
946				Quit();
947		} else if (!strcmp(argv[i], "+")) {
948			activate_workspace(current_workspace() + 1);
949
950			if (IsLaunching())
951				Quit();
952		} else {
953			// some unknown arguments were specified
954			fprintf(stderr, B_TRANSLATE("Invalid argument: %s\n"), argv[i]);
955
956			if (IsLaunching())
957				Quit();
958		}
959	}
960}
961
962
963BView* instantiate_deskbar_item()
964{
965	return new WorkspacesView(BRect (0, 0, 75, 15), false);
966}
967
968
969void
970WorkspacesApp::ReadyToRun()
971{
972	fWindow->Show();
973}
974
975
976//	#pragma mark -
977
978
979int
980main(int argc, char **argv)
981{
982	WorkspacesApp app;
983	app.Run();
984
985	return 0;
986}
987