xref: /haiku/src/apps/workspaces/Workspaces.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
1 /*
2  * Copyright 2002-2006, 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 /**	Workspaces window trick found by Michael "Minox" Paine.
14  *	(using B_ALL_WORKSPACES as flags in BWindow)
15  *	Found out that using 0xffffffff as Flags was causing the window not to close on Alt-W
16  *	hey Workspaces get Flags of Window 0
17  *	gives 0x00008080 which makes it.
18  */
19 
20 
21 #include <Alert.h>
22 #include <Application.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <FindDirectory.h>
26 #include <Path.h>
27 #include <Roster.h>
28 #include <Screen.h>
29 #include <TextView.h>
30 #include <Window.h>
31 
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "WindowPrivate.h"
38 
39 static const char *kWorkspacesSignature = "application/x-vnd.Be-WORK";
40 static const char *kWorkspacesSettingFile = "Workspace_data";
41 
42 static const float kScreenBorderOffset = 10.0;
43 
44 
45 class WorkspacesPreferences {
46 	public:
47 		WorkspacesPreferences();
48 		virtual ~WorkspacesPreferences();
49 
50 		BRect WindowFrame() const { return fWindowFrame; }
51 		BRect ScreenFrame() const { return fScreenFrame; }
52 
53 		void UpdateFramesForScreen(BRect screenFrame);
54 		void UpdateScreenFrame();
55 		void SetWindowFrame(BRect);
56 
57 	private:
58 		BRect	fWindowFrame, fScreenFrame;
59 };
60 
61 class WorkspacesWindow : public BWindow {
62 	public:
63 		WorkspacesWindow(WorkspacesPreferences *fPreferences);
64 		virtual ~WorkspacesWindow();
65 
66 		virtual void ScreenChanged(BRect frame, color_space mode);
67 		virtual void FrameMoved(BPoint origin);
68 		virtual void FrameResized(float width, float height);
69 		virtual void Zoom(BPoint origin, float width, float height);
70 
71 		virtual void MessageReceived(BMessage *msg);
72 		virtual bool QuitRequested();
73 
74 	private:
75 		WorkspacesPreferences *fPreferences;
76 		BRect	fPreviousFrame;
77 };
78 
79 class WorkspacesApp : public BApplication {
80 	public:
81 		WorkspacesApp();
82 		virtual ~WorkspacesApp();
83 
84 		virtual void AboutRequested();
85 		virtual void ArgvReceived(int32 argc, char **argv);
86 		virtual void ReadyToRun();
87 
88 		void Usage(const char *programName);
89 
90 	private:
91 		BWindow		*fWindow;
92 };
93 
94 
95 WorkspacesPreferences::WorkspacesPreferences()
96 {
97 	UpdateScreenFrame();
98 
99 	bool settingsValid = false;
100 	BPath path;
101 
102 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
103 		path.Append(kWorkspacesSettingFile);
104 		BFile file(path.Path(), B_READ_ONLY);
105 		if (file.InitCheck() == B_OK
106 			&& file.Read(&fWindowFrame, sizeof(BRect)) == sizeof(BRect)) {
107 			// we now also store the frame of the screen to know
108 			// in which context the window frame has been chosen
109 			BScreen screen;
110 			BRect frame;
111 			if (file.Read(&frame, sizeof(BRect)) == sizeof(BRect)) {
112 				fScreenFrame = frame;
113 				// if the current screen frame is different from the one
114 				// just loaded, we need to alter the window frame accordingly
115 				if (fScreenFrame != screen.Frame())
116 					UpdateFramesForScreen(screen.Frame());
117 			}
118 
119 			// check if loaded values are valid
120 			if (screen.Frame().right >= fWindowFrame.right
121 				&& screen.Frame().bottom >= fWindowFrame.bottom
122 				&& fWindowFrame.right > 0 && fWindowFrame.bottom > 0)
123 				settingsValid = true;
124 		}
125 	}
126 
127 	if (!settingsValid) {
128 		// set to some usable defaults
129 		fWindowFrame = fScreenFrame;
130 		fWindowFrame.OffsetBy(-kScreenBorderOffset, -kScreenBorderOffset);
131 		fWindowFrame.left = fWindowFrame.right - 160;
132 		fWindowFrame.top = fWindowFrame.bottom - 140;
133 	}
134 }
135 
136 
137 WorkspacesPreferences::~WorkspacesPreferences()
138 {
139 	// write settings file
140 	BPath path;
141 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) < B_OK)
142 		return;
143 
144 	path.Append(kWorkspacesSettingFile);
145 
146 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
147 	if (file.InitCheck() == B_OK) {
148 		file.Write(&fWindowFrame, sizeof(BRect));
149 		file.Write(&fScreenFrame, sizeof(BRect));
150 	}
151 }
152 
153 
154 void
155 WorkspacesPreferences::UpdateFramesForScreen(BRect newScreenFrame)
156 {
157 	// don't change the position if the screen frame hasn't changed
158 	if (newScreenFrame == fScreenFrame)
159 		return;
160 
161 	// adjust horizontal position
162 	if (fWindowFrame.right > fScreenFrame.right / 2)
163 		fWindowFrame.OffsetTo(newScreenFrame.right
164 			- (fScreenFrame.right - fWindowFrame.left), fWindowFrame.top);
165 
166 	// adjust vertical position
167 	if (fWindowFrame.bottom > fScreenFrame.bottom / 2)
168 		fWindowFrame.OffsetTo(fWindowFrame.left,
169 			newScreenFrame.bottom - (fScreenFrame.bottom - fWindowFrame.top));
170 
171 	fScreenFrame = newScreenFrame;
172 }
173 
174 
175 void
176 WorkspacesPreferences::UpdateScreenFrame()
177 {
178 	BScreen screen;
179 	fScreenFrame = screen.Frame();
180 }
181 
182 
183 void
184 WorkspacesPreferences::SetWindowFrame(BRect frame)
185 {
186 	fWindowFrame = frame;
187 }
188 
189 
190 //	#pragma mark -
191 
192 
193 WorkspacesWindow::WorkspacesWindow(WorkspacesPreferences *preferences)
194 	: BWindow(preferences->WindowFrame(), "Workspaces", B_TITLED_WINDOW_LOOK,
195  			B_NORMAL_WINDOW_FEEL,
196 			kWorkspacesWindowFlag | B_AVOID_FRONT | B_WILL_ACCEPT_FIRST_CLICK,
197  			B_ALL_WORKSPACES),
198  	fPreferences(preferences)
199 {
200 	fPreviousFrame = Frame();
201 }
202 
203 
204 WorkspacesWindow::~WorkspacesWindow()
205 {
206 	delete fPreferences;
207 }
208 
209 
210 void
211 WorkspacesWindow::ScreenChanged(BRect rect, color_space mode)
212 {
213 	fPreviousFrame = fPreferences->WindowFrame();
214 		// work-around for a bug in BeOS, see explanation in FrameMoved()
215 
216 	fPreferences->UpdateFramesForScreen(rect);
217 	MoveTo(fPreferences->WindowFrame().LeftTop());
218 }
219 
220 
221 void
222 WorkspacesWindow::FrameMoved(BPoint origin)
223 {
224 	if (origin == fPreviousFrame.LeftTop()) {
225 		// This works around a bug in BeOS; when you change the window
226 		// position in WorkspaceActivated() or ScreenChanged(), it will
227 		// send an old repositioning message *after* the FrameMoved()
228 		// that originated your change has arrived
229 		return;
230 	}
231 
232 	fPreferences->SetWindowFrame(Frame());
233 }
234 
235 
236 void
237 WorkspacesWindow::FrameResized(float width, float height)
238 {
239 	fPreferences->SetWindowFrame(Frame());
240 }
241 
242 
243 void
244 WorkspacesWindow::Zoom(BPoint origin, float width, float height)
245 {
246 	BScreen screen;
247 	origin = screen.Frame().RightBottom();
248 	origin.x -= kScreenBorderOffset + fPreferences->WindowFrame().Width();
249 	origin.y -= kScreenBorderOffset + fPreferences->WindowFrame().Height();
250 
251 	MoveTo(origin);
252 }
253 
254 
255 void
256 WorkspacesWindow::MessageReceived(BMessage *msg)
257 {
258 	if (msg->what == 'DATA') {
259 		// Drop from Tracker
260 		entry_ref ref;
261 		for (int i = 0; (msg->FindRef("refs", i, &ref) == B_OK); i++)
262 			be_roster->Launch(&ref);
263 	} else
264 		BWindow::MessageReceived(msg);
265 }
266 
267 
268 bool
269 WorkspacesWindow::QuitRequested()
270 {
271 	be_app->PostMessage(B_QUIT_REQUESTED);
272 	return true;
273 }
274 
275 
276 //	#pragma mark -
277 
278 
279 WorkspacesApp::WorkspacesApp()
280 	: BApplication(kWorkspacesSignature)
281 {
282 	fWindow = new WorkspacesWindow(new WorkspacesPreferences());
283 }
284 
285 
286 WorkspacesApp::~WorkspacesApp()
287 {
288 }
289 
290 
291 void
292 WorkspacesApp::AboutRequested()
293 {
294 	BAlert *alert = new BAlert("about", "Workspaces\n"
295 		"\twritten by François Revol, Axel Dörfler,\n"
296 		"\t\tand Matt Madia.\n"
297 		"\tCopyright 2002-2006, Haiku.\n", "Ok");
298 	BTextView *view = alert->TextView();
299 	BFont font;
300 
301 	view->SetStylable(true);
302 
303 	view->GetFont(&font);
304 	font.SetSize(18);
305 	font.SetFace(B_BOLD_FACE);
306 	view->SetFontAndColor(0, 10, &font);
307 
308 	alert->Go();
309 }
310 
311 
312 void
313 WorkspacesApp::Usage(const char *programName)
314 {
315 	printf("Usage: %s [options] [workspace]\n"
316 		"where \"options\" is one of:\n"
317 		"  --notitle\t\ttitle bar removed.  border and resize kept.\n"
318 		"  --noborder\t\ttitle, border, and resize removed.\n"
319 		"  --avoidfocus\t\tprevents the window from being the target of keyboard events.\n"
320 		"  --alwaysontop\t\tkeeps window on top\n"
321 		"  --notmovable\t\twindow can't be moved around\n"
322 		"  --help\t\tdisplay this help and exit\n"
323 		"and \"workspace\" is the number of the Workspace to which to switch (0-31)\n",
324 		programName);
325 
326 	// quit only if we aren't running already
327 	if (IsLaunching())
328 		Quit();
329 }
330 
331 
332 void
333 WorkspacesApp::ArgvReceived(int32 argc, char **argv)
334 {
335 	for (int i = 1;  i < argc;  i++) {
336 		if (argv[i][0] == '-' && argv[i][1] == '-') {
337 			// evaluate --arguments
338 			if (!strcmp(argv[i], "--notitle"))
339 				fWindow->SetLook(B_MODAL_WINDOW_LOOK);
340 			else if (!strcmp(argv[i], "--noborder"))
341 				fWindow->SetLook(B_NO_BORDER_WINDOW_LOOK);
342 			else if (!strcmp(argv[i], "--avoidfocus"))
343 				fWindow->SetFlags(fWindow->Flags() | B_AVOID_FOCUS);
344 			else if (!strcmp(argv[i], "--notmovable"))
345 				fWindow->SetFlags(fWindow->Flags() | B_NOT_MOVABLE);
346 			else if (!strcmp(argv[i], "--alwaysontop"))
347 				fWindow->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
348 			else {
349 				const char *programName = strrchr(argv[0], '/');
350 				programName = programName ? programName + 1 : argv[0];
351 
352 				Usage(programName);
353 			}
354 		} else if (isdigit(*argv[i])) {
355 			// check for a numeric arg, if not already given
356 			activate_workspace(atoi(argv[i]));
357 
358 			// if the app is running, don't quit
359 			// but if it isn't, cancel the complete run, so it doesn't
360 			// open any window
361 			if (IsLaunching())
362 				Quit();
363 		}
364 	}
365 }
366 
367 
368 void
369 WorkspacesApp::ReadyToRun()
370 {
371 	fWindow->Show();
372 }
373 
374 
375 //	#pragma mark -
376 
377 
378 int
379 main(int32 argc, char **argv)
380 {
381 	WorkspacesApp app;
382 	app.Run();
383 
384 	return 0;
385 }
386