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