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