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