xref: /haiku/src/apps/launchbox/MainWindow.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
1 /*
2  * Copyright 2006-2008, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 
9 #include "MainWindow.h"
10 
11 #include <stdio.h>
12 
13 #include <Alert.h>
14 #include <Application.h>
15 #include <GroupLayout.h>
16 #include <Menu.h>
17 #include <MenuItem.h>
18 #include <Messenger.h>
19 #include <Path.h>
20 #include <Roster.h>
21 #include <Screen.h>
22 
23 #include "support.h"
24 
25 #include "LaunchButton.h"
26 #include "NamePanel.h"
27 #include "PadView.h"
28 
29 // constructor
30 MainWindow::MainWindow(const char* name, BRect frame, bool addDefaultButtons)
31 	: BWindow(frame, name, B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
32 			B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
33 			| B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION
34 			| B_AUTO_UPDATE_SIZE_LIMITS | B_SAME_POSITION_IN_ALL_WORKSPACES,
35 			B_ALL_WORKSPACES),
36 	  fSettings(new BMessage('sett')),
37 	  fPadView(new PadView("pad view")),
38 	  fLastID(0),
39 	  fNamePanelFrame(-1000.0, -1000.0, -800.0, -900.0),
40 	  fAutoRaise(false),
41 	  fShowOnAllWorkspaces(true)
42 {
43 	bool buttonsAdded = false;
44 	if (load_settings(fSettings, "main_settings", "LaunchBox") >= B_OK)
45 		buttonsAdded = LoadSettings(fSettings);
46 	if (!buttonsAdded) {
47 		if (addDefaultButtons)
48 			_AddDefaultButtons();
49 		else
50 			_AddEmptyButtons();
51 	}
52 
53 	SetLayout(new BGroupLayout(B_HORIZONTAL));
54 	AddChild(fPadView);
55 }
56 
57 // constructor
58 MainWindow::MainWindow(const char* name, BRect frame, BMessage* settings)
59 	: BWindow(frame, name,
60 			B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
61 			B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
62 			| B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION
63 			| B_AUTO_UPDATE_SIZE_LIMITS | B_SAME_POSITION_IN_ALL_WORKSPACES,
64 			B_ALL_WORKSPACES),
65 	  fSettings(settings),
66 	  fPadView(new PadView("pad view")),
67 	  fLastID(0),
68 	  fNamePanelFrame(-1000.0, -1000.0, -900.0, -900.0),
69 	  fAutoRaise(false),
70 	  fShowOnAllWorkspaces(true)
71 {
72 	if (!LoadSettings(settings))
73 		_AddEmptyButtons();
74 
75 	SetLayout(new BGroupLayout(B_HORIZONTAL));
76 	AddChild(fPadView);
77 }
78 
79 
80 // destructor
81 MainWindow::~MainWindow()
82 {
83 	delete fSettings;
84 }
85 
86 // QuitRequested
87 bool
88 MainWindow::QuitRequested()
89 {
90 	int32 padWindowCount = 0;
91 	for (int32 i = 0; BWindow* window = be_app->WindowAt(i); i++) {
92 		if (dynamic_cast<MainWindow*>(window))
93 			padWindowCount++;
94 	}
95 	if (padWindowCount == 1) {
96 		be_app->PostMessage(B_QUIT_REQUESTED);
97 		return false;
98 	} else {
99 		BAlert* alert = new BAlert("last chance", "Really close this pad?\n"
100 			"(The pad will not be remembered.)",
101 			"Close", "Cancel", NULL);
102 		if (alert->Go() == 1)
103 			return false;
104 	}
105 	return true;
106 }
107 
108 // MessageReceived
109 void
110 MainWindow::MessageReceived(BMessage* message)
111 {
112 	switch (message->what) {
113 		case MSG_LAUNCH: {
114 			BView* pointer;
115 			if (message->FindPointer("be:source", (void**)&pointer) < B_OK)
116 				break;
117 			LaunchButton* button = dynamic_cast<LaunchButton*>(pointer);
118 			if (button == NULL)
119 				break;
120 			BString errorMessage;
121 			bool launchedByRef = false;
122 			if (button->Ref()) {
123 				BEntry entry(button->Ref(), true);
124 				if (entry.IsDirectory()) {
125 					// open in Tracker
126 					BMessenger messenger("application/x-vnd.Be-TRAK");
127 					if (messenger.IsValid()) {
128 						BMessage trackerMessage(B_REFS_RECEIVED);
129 						trackerMessage.AddRef("refs", button->Ref());
130 						status_t ret = messenger.SendMessage(&trackerMessage);
131 						if (ret < B_OK) {
132 							errorMessage = "Failed to send 'open folder' "
133 								"command to Tracker.\n\nError: ";
134 							errorMessage << strerror(ret);
135 						} else
136 							launchedByRef = true;
137 					} else
138 						errorMessage = "Failed to open folder - is Tracker "
139 							"running?";
140 				} else {
141 					status_t ret = be_roster->Launch(button->Ref());
142 					if (ret < B_OK && ret != B_ALREADY_RUNNING) {
143 						errorMessage = "Failed to launch '";
144 						BPath path(button->Ref());
145 						if (path.InitCheck() >= B_OK)
146 							errorMessage << path.Path();
147 						else
148 							errorMessage << button->Ref()->name;
149 						errorMessage << "'.\n\nError: ";
150 						errorMessage << strerror(ret);
151 					} else
152 						launchedByRef = true;
153 				}
154 			}
155 			if (!launchedByRef && button->AppSignature()) {
156 				status_t ret = be_roster->Launch(button->AppSignature());
157 				if (ret != B_OK && ret != B_ALREADY_RUNNING) {
158 					errorMessage = "Failed to launch application with "
159 						"signature '";
160 					errorMessage << button->AppSignature() << "'.\n\nError: ";
161 					errorMessage << strerror(ret);
162 				} else {
163 					// clear error message on success (might have been
164 					// filled when trying to launch by ref)
165 					errorMessage = "";
166 				}
167 			} else if (!launchedByRef) {
168 				errorMessage = "Failed to launch 'something', error in "
169 					"Pad data.";
170 			}
171 			if (errorMessage.Length() > 0) {
172 				BAlert* alert = new BAlert("error", errorMessage.String(),
173 					"Bummer", NULL, NULL, B_WIDTH_FROM_WIDEST);
174 				alert->Go(NULL);
175 			}
176 			break;
177 		}
178 		case MSG_ADD_SLOT: {
179 			LaunchButton* button;
180 			if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
181 				fPadView->AddButton(new LaunchButton("launch button", fLastID++,
182 					NULL, new BMessage(MSG_LAUNCH)), button);
183 			}
184 			break;
185 		}
186 		case MSG_CLEAR_SLOT: {
187 			LaunchButton* button;
188 			if (message->FindPointer("be:source", (void**)&button) >= B_OK)
189 				button->SetTo((entry_ref*)NULL);
190 			break;
191 		}
192 		case MSG_REMOVE_SLOT: {
193 			LaunchButton* button;
194 			if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
195 				if (fPadView->RemoveButton(button))
196 					delete button;
197 			}
198 			break;
199 		}
200 		case MSG_SET_DESCRIPTION: {
201 			LaunchButton* button;
202 			if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
203 				const char* name;
204 				if (message->FindString("name", &name) >= B_OK) {
205 					// message comes from a previous name panel
206 					button->SetDescription(name);
207 					message->FindRect("frame", &fNamePanelFrame);
208 				} else {
209 					// message comes from pad view
210 					entry_ref* ref = button->Ref();
211 					if (ref) {
212 						BString helper("Description for '");
213 						helper << ref->name << "'";
214 //						BRect* frame = fNamePanelFrame.IsValid() ? &fNamePanelFrame : NULL;
215 						new NamePanel(helper.String(), button->Description(),
216 							this, this, new BMessage(*message),
217 							fNamePanelFrame);
218 					}
219 				}
220 			}
221 			break;
222 		}
223 		case MSG_ADD_WINDOW: {
224 			BMessage settings('sett');
225 			SaveSettings(&settings);
226 			message->AddMessage("window", &settings);
227 			be_app->PostMessage(message);
228 			break;
229 		}
230 		case MSG_SHOW_BORDER:
231 			SetLook(B_TITLED_WINDOW_LOOK);
232 			break;
233 		case MSG_HIDE_BORDER:
234 			SetLook(B_BORDERED_WINDOW_LOOK);
235 			break;
236 		case MSG_TOGGLE_AUTORAISE:
237 			ToggleAutoRaise();
238 			break;
239 		case MSG_SHOW_ON_ALL_WORKSPACES:
240 			fShowOnAllWorkspaces = !fShowOnAllWorkspaces;
241 			if (fShowOnAllWorkspaces)
242 				SetWorkspaces(B_ALL_WORKSPACES);
243 			else
244 				SetWorkspaces(1L << current_workspace());
245 			break;
246 		case B_SIMPLE_DATA:
247 		case B_REFS_RECEIVED:
248 		case B_PASTE:
249 		case B_MODIFIERS_CHANGED:
250 			break;
251 		case B_ABOUT_REQUESTED:
252 			be_app->PostMessage(message);
253 			break;
254 		default:
255 			BWindow::MessageReceived(message);
256 			break;
257 	}
258 }
259 
260 // Show
261 void
262 MainWindow::Show()
263 {
264 	BWindow::Show();
265 	_GetLocation();
266 }
267 
268 // ScreenChanged
269 void
270 MainWindow::ScreenChanged(BRect frame, color_space format)
271 {
272 	_AdjustLocation(Frame());
273 }
274 
275 // WorkspaceActivated
276 void
277 MainWindow::WorkspaceActivated(int32 workspace, bool active)
278 {
279 	if (fShowOnAllWorkspaces) {
280 		if (!active)
281 			_GetLocation();
282 		else
283 			_AdjustLocation(Frame());
284 	}
285 }
286 
287 // FrameMoved
288 void
289 MainWindow::FrameMoved(BPoint origin)
290 {
291 	if (IsActive())
292 		_GetLocation();
293 }
294 
295 // FrameResized
296 void
297 MainWindow::FrameResized(float width, float height)
298 {
299 	if (IsActive())
300 		_GetLocation();
301 	BWindow::FrameResized(width, height);
302 }
303 
304 // ToggleAutoRaise
305 void
306 MainWindow::ToggleAutoRaise()
307 {
308 	fAutoRaise = !fAutoRaise;
309 	if (fAutoRaise)
310 		fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
311 	else
312 		fPadView->SetEventMask(0);
313 }
314 
315 // LoadSettings
316 bool
317 MainWindow::LoadSettings(const BMessage* message)
318 {
319 	// restore window positioning
320 	BPoint point;
321 	bool useAdjust = false;
322 	if (message->FindPoint("window position", &point) == B_OK) {
323 		fScreenPosition = point;
324 		useAdjust = true;
325 	}
326 	float borderDist;
327 	if (message->FindFloat("border distance", &borderDist) == B_OK) {
328 		fBorderDist = borderDist;
329 	}
330 	// restore window frame
331 	BRect frame;
332 	if (message->FindRect("window frame", &frame) == B_OK) {
333 		if (useAdjust) {
334 			_AdjustLocation(frame);
335 		} else {
336 			make_sure_frame_is_on_screen(frame, this);
337 			MoveTo(frame.LeftTop());
338 			ResizeTo(frame.Width(), frame.Height());
339 		}
340 	}
341 
342 	// restore name panel frame
343 	if (message->FindRect("name panel frame", &frame) == B_OK) {
344 		if (frame.IsValid()) {
345 			make_sure_frame_is_on_screen(frame, this);
346 			fNamePanelFrame = frame;
347 		}
348 	}
349 
350 	// restore window look
351 	window_look look;
352 	if (message->FindInt32("window look", (int32*)&look) == B_OK)
353 		SetLook(look);
354 
355 	// restore orientation
356 	int32 orientation;
357 	if (message->FindInt32("orientation", &orientation) == B_OK)
358 		fPadView->SetOrientation((enum orientation)orientation);
359 
360 	// restore icon size
361 	int32 iconSize;
362 	if (message->FindInt32("icon size", &iconSize) == B_OK)
363 		fPadView->SetIconSize(iconSize);
364 
365 	// restore buttons
366 	const char* path;
367 	bool buttonAdded = false;
368 	for (int32 i = 0; message->FindString("path", i, &path) >= B_OK; i++) {
369 		LaunchButton* button = new LaunchButton("launch button", fLastID++,
370 			NULL, new BMessage(MSG_LAUNCH));
371 		fPadView->AddButton(button);
372 		BString signature;
373 		if (message->FindString("signature", i, &signature) >= B_OK
374 			&& signature.CountChars() > 0)  {
375 			button->SetTo(signature.String(), true);
376 		}
377 
378 		entry_ref ref;
379 		BEntry entry(&ref, true);
380 		if (entry.Exists())
381 			button->SetTo(&ref);
382 
383 		const char* text;
384 		if (message->FindString("description", i, &text) >= B_OK)
385 			button->SetDescription(text);
386 		buttonAdded = true;
387 	}
388 
389 	// restore auto raise setting
390 	bool autoRaise;
391 	if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise)
392 		ToggleAutoRaise();
393 
394 	// store workspace setting
395 	bool showOnAllWorkspaces;
396 	if (message->FindBool("all workspaces", &showOnAllWorkspaces) == B_OK)
397 		fShowOnAllWorkspaces = showOnAllWorkspaces;
398 		SetWorkspaces(showOnAllWorkspaces
399 			? B_ALL_WORKSPACES : 1L << current_workspace());
400 	if (!fShowOnAllWorkspaces) {
401 		uint32 workspaces;
402 		if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK)
403 			SetWorkspaces(workspaces);
404 	}
405 
406 	return buttonAdded;
407 }
408 
409 // SaveSettings
410 void
411 MainWindow::SaveSettings(BMessage* message)
412 {
413 	// make sure the positioning info is correct
414 	_GetLocation();
415 	// store window position
416 	if (message->ReplacePoint("window position", fScreenPosition) != B_OK)
417 		message->AddPoint("window position", fScreenPosition);
418 
419 	if (message->ReplaceFloat("border distance", fBorderDist) != B_OK)
420 		message->AddFloat("border distance", fBorderDist);
421 
422 	// store window frame
423 	if (message->ReplaceRect("window frame", Frame()) != B_OK)
424 		message->AddRect("window frame", Frame());
425 
426 	// store name panel frame
427 	if (message->ReplaceRect("name panel frame", fNamePanelFrame) != B_OK)
428 		message->AddRect("name panel frame", fNamePanelFrame);
429 
430 	if (message->ReplaceInt32("window look", Look()) != B_OK)
431 		message->AddInt32("window look", Look());
432 
433 	// store orientation
434 	if (message->ReplaceInt32("orientation",
435 			(int32)fPadView->Orientation()) != B_OK)
436 		message->AddInt32("orientation", (int32)fPadView->Orientation());
437 
438 	// store icon size
439 	if (message->ReplaceInt32("icon size", fPadView->IconSize()) != B_OK)
440 		message->AddInt32("icon size", fPadView->IconSize());
441 
442 	// store buttons
443 	message->RemoveName("path");
444 	message->RemoveName("description");
445 	message->RemoveName("signature");
446 	for (int32 i = 0; LaunchButton* button = fPadView->ButtonAt(i); i++) {
447 		BPath path(button->Ref());
448 		if (path.InitCheck() >= B_OK)
449 			message->AddString("path", path.Path());
450 		else
451 			message->AddString("path", "");
452 		message->AddString("description", button->Description());
453 
454 		if (button->AppSignature())
455 			message->AddString("signature", button->AppSignature());
456 		else
457 			message->AddString("signature", "");
458 	}
459 
460 	// store auto raise setting
461 	if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK)
462 		message->AddBool("auto raise", fAutoRaise);
463 
464 	// store workspace setting
465 	if (message->ReplaceBool("all workspaces", fShowOnAllWorkspaces) != B_OK)
466 		message->AddBool("all workspaces", fShowOnAllWorkspaces);
467 	if (message->ReplaceInt32("workspaces", Workspaces()) != B_OK)
468 		message->AddInt32("workspaces", Workspaces());
469 }
470 
471 // _GetLocation
472 void
473 MainWindow::_GetLocation()
474 {
475 	BRect frame = Frame();
476 	BPoint origin = frame.LeftTop();
477 	BPoint center(origin.x + frame.Width() / 2.0,
478 		origin.y + frame.Height() / 2.0);
479 	BScreen screen(this);
480 	BRect screenFrame = screen.Frame();
481 	fScreenPosition.x = center.x / screenFrame.Width();
482 	fScreenPosition.y = center.y / screenFrame.Height();
483 	if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) {
484 		// nearest to left or right border
485 		if (fScreenPosition.x < 0.5)
486 			fBorderDist = frame.left - screenFrame.left;
487 		else
488 			fBorderDist = screenFrame.right - frame.right;
489 	} else {
490 		// nearest to top or bottom border
491 		if (fScreenPosition.y < 0.5)
492 			fBorderDist = frame.top - screenFrame.top;
493 		else
494 			fBorderDist = screenFrame.bottom - frame.bottom;
495 	}
496 }
497 
498 // _AdjustLocation
499 void
500 MainWindow::_AdjustLocation(BRect frame)
501 {
502 	BScreen screen(this);
503 	BRect screenFrame = screen.Frame();
504 	BPoint center(fScreenPosition.x * screenFrame.Width(),
505 		fScreenPosition.y * screenFrame.Height());
506 	BPoint frameCenter(frame.left + frame.Width() / 2.0,
507 		frame.top + frame.Height() / 2.0);
508 	frame.OffsetBy(center - frameCenter);
509 	// ignore border dist when distance too large
510 	if (fBorderDist < 10.0) {
511 		// see which border we mean depending on screen position
512 		BPoint offset(0.0, 0.0);
513 		if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) {
514 			// left or right border
515 			if (fScreenPosition.x < 0.5)
516 				offset.x = (screenFrame.left + fBorderDist) - frame.left;
517 			else
518 				offset.x = (screenFrame.right - fBorderDist) - frame.right;
519 		} else {
520 			// top or bottom border
521 			if (fScreenPosition.y < 0.5)
522 				offset.y = (screenFrame.top + fBorderDist) - frame.top;
523 			else
524 				offset.y = (screenFrame.bottom - fBorderDist) - frame.bottom;
525 		}
526 		frame.OffsetBy(offset);
527 	}
528 
529 	make_sure_frame_is_on_screen(frame, this);
530 
531 	MoveTo(frame.LeftTop());
532 	ResizeTo(frame.Width(), frame.Height());
533 }
534 
535 // _AddDefaultButtons
536 void
537 MainWindow::_AddDefaultButtons()
538 {
539 	// Mail
540 	LaunchButton* button = new LaunchButton("launch button", fLastID++, NULL,
541 		new BMessage(MSG_LAUNCH));
542 	fPadView->AddButton(button);
543 	button->SetTo("application/x-vnd.Be-MAIL", true);
544 
545 	// StyledEdit
546 	button = new LaunchButton("launch button", fLastID++, NULL,
547 		new BMessage(MSG_LAUNCH));
548 	fPadView->AddButton(button);
549 	button->SetTo("application/x-vnd.Haiku-StyledEdit", true);
550 
551 	// ShowImage
552 	button = new LaunchButton("launch button", fLastID++, NULL,
553 		new BMessage(MSG_LAUNCH));
554 	fPadView->AddButton(button);
555 	button->SetTo("application/x-vnd.Haiku-ShowImage", true);
556 
557 	// MediaPlayer
558 	button = new LaunchButton("launch button", fLastID++, NULL,
559 		new BMessage(MSG_LAUNCH));
560 	fPadView->AddButton(button);
561 	button->SetTo("application/x-vnd.Haiku-MediaPlayer", true);
562 
563 	// DeskCalc
564 	button = new LaunchButton("launch button", fLastID++, NULL,
565 		new BMessage(MSG_LAUNCH));
566 	fPadView->AddButton(button);
567 	button->SetTo("application/x-vnd.Haiku-DeskCalc", true);
568 
569 	// Terminal
570 	button = new LaunchButton("launch button", fLastID++, NULL,
571 		new BMessage(MSG_LAUNCH));
572 	fPadView->AddButton(button);
573 	button->SetTo("application/x-vnd.Haiku-Terminal", true);
574 }
575 
576 // _AddEmptyButtons
577 void
578 MainWindow::_AddEmptyButtons()
579 {
580 	LaunchButton* button = new LaunchButton("launch button", fLastID++, NULL,
581 		new BMessage(MSG_LAUNCH));
582 	fPadView->AddButton(button);
583 
584 	button = new LaunchButton("launch button", fLastID++, NULL,
585 		new BMessage(MSG_LAUNCH));
586 	fPadView->AddButton(button);
587 
588 	button = new LaunchButton("launch button", fLastID++, NULL,
589 		new BMessage(MSG_LAUNCH));
590 	fPadView->AddButton(button);
591 }
592 
593 
594