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