MainWindow.cpp revision 5d2022c52149cb153fe09a61fb41e76eae0b21a7
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
27MainWindow::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
56MainWindow::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
79MainWindow::~MainWindow()
80{
81	delete fSettings;
82}
83
84
85bool
86MainWindow::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
107void
108MainWindow::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
259void
260MainWindow::Show()
261{
262	BWindow::Show();
263	_GetLocation();
264}
265
266
267void
268MainWindow::ScreenChanged(BRect frame, color_space format)
269{
270	_AdjustLocation(Frame());
271}
272
273
274void
275MainWindow::WorkspaceActivated(int32 workspace, bool active)
276{
277	if (fShowOnAllWorkspaces) {
278		if (!active)
279			_GetLocation();
280		else
281			_AdjustLocation(Frame());
282	}
283}
284
285
286void
287MainWindow::FrameMoved(BPoint origin)
288{
289	if (IsActive()) {
290		_GetLocation();
291		_NotifySettingsChanged();
292	}
293}
294
295
296void
297MainWindow::FrameResized(float width, float height)
298{
299	if (IsActive()) {
300		_GetLocation();
301		_NotifySettingsChanged();
302	}
303	BWindow::FrameResized(width, height);
304}
305
306
307void
308MainWindow::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
320bool
321MainWindow::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
419void
420MainWindow::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
487void
488MainWindow::_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
514void
515MainWindow::_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
551void
552MainWindow::_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
592void
593MainWindow::_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
609void
610MainWindow::_NotifySettingsChanged()
611{
612	be_app->PostMessage(MSG_SETTINGS_CHANGED);
613}
614
615