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