vfs: check userland buffers before reading them.
[haiku.git] / src / apps / launchbox / MainWindow.cpp
blob404ba148669c6506048c6562edd567aaa0c64637
1 /*
2 * Copyright 2006 - 2011, Stephan Aßmus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "MainWindow.h"
8 #include <stdio.h>
10 #include <Alert.h>
11 #include <Application.h>
12 #include <Catalog.h>
13 #include <GroupLayout.h>
14 #include <Menu.h>
15 #include <MenuItem.h>
16 #include <Messenger.h>
17 #include <Path.h>
18 #include <Roster.h>
19 #include <Screen.h>
21 #include "support.h"
23 #include "App.h"
24 #include "LaunchButton.h"
25 #include "NamePanel.h"
26 #include "PadView.h"
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "LaunchBox"
31 MainWindow::MainWindow(const char* name, BRect frame, bool addDefaultButtons)
33 BWindow(frame, name, B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
34 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
35 | B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION
36 | B_AUTO_UPDATE_SIZE_LIMITS | B_SAME_POSITION_IN_ALL_WORKSPACES,
37 B_ALL_WORKSPACES),
38 fSettings(new BMessage('sett')),
39 fPadView(new PadView("pad view")),
40 fAutoRaise(false),
41 fShowOnAllWorkspaces(true)
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();
53 SetLayout(new BGroupLayout(B_HORIZONTAL));
54 AddChild(fPadView);
58 MainWindow::MainWindow(const char* name, BRect frame, BMessage* settings)
60 BWindow(frame, name,
61 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
62 B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
63 | B_WILL_ACCEPT_FIRST_CLICK | B_NO_WORKSPACE_ACTIVATION
64 | B_AUTO_UPDATE_SIZE_LIMITS | B_SAME_POSITION_IN_ALL_WORKSPACES,
65 B_ALL_WORKSPACES),
66 fSettings(settings),
67 fPadView(new PadView("pad view")),
68 fAutoRaise(false),
69 fShowOnAllWorkspaces(true)
71 if (!LoadSettings(settings))
72 _AddEmptyButtons();
74 SetLayout(new BGroupLayout(B_HORIZONTAL));
75 AddChild(fPadView);
79 MainWindow::~MainWindow()
81 delete fSettings;
85 bool
86 MainWindow::QuitRequested()
88 int32 padWindowCount = 0;
89 for (int32 i = 0; BWindow* window = be_app->WindowAt(i); i++) {
90 if (dynamic_cast<MainWindow*>(window))
91 padWindowCount++;
93 bool canClose = true;
95 if (padWindowCount == 1) {
96 be_app->PostMessage(B_QUIT_REQUESTED);
97 canClose = false;
98 } else {
99 BAlert* alert = new BAlert(B_TRANSLATE("last chance"),
100 B_TRANSLATE("Really close this pad?\n"
101 "(The pad will not be remembered.)"),
102 B_TRANSLATE("Close"), B_TRANSLATE("Cancel"), NULL);
103 alert->SetShortcut(1, B_ESCAPE);
104 if (alert->Go() == 1)
105 canClose = false;
107 return canClose;
111 void
112 MainWindow::MessageReceived(BMessage* message)
114 switch (message->what) {
115 case MSG_LAUNCH:
117 BView* pointer;
118 if (message->FindPointer("be:source", (void**)&pointer) < B_OK)
119 break;
120 LaunchButton* button = dynamic_cast<LaunchButton*>(pointer);
121 if (button == NULL)
122 break;
123 BString errorMessage;
124 bool launchedByRef = false;
125 if (button->Ref()) {
126 BEntry entry(button->Ref(), true);
127 if (entry.IsDirectory()) {
128 // open in Tracker
129 BMessenger messenger("application/x-vnd.Be-TRAK");
130 if (messenger.IsValid()) {
131 BMessage trackerMessage(B_REFS_RECEIVED);
132 trackerMessage.AddRef("refs", button->Ref());
133 status_t ret = messenger.SendMessage(&trackerMessage);
134 if (ret < B_OK) {
135 errorMessage = B_TRANSLATE("Failed to send "
136 "'open folder' command to Tracker.\n\nError: ");
137 errorMessage << strerror(ret);
138 } else
139 launchedByRef = true;
140 } else
141 errorMessage = ("Failed to open folder - is Tracker "
142 "running?");
143 } else {
144 status_t ret = be_roster->Launch(button->Ref());
145 if (ret < B_OK && ret != B_ALREADY_RUNNING) {
146 BString errStr(B_TRANSLATE("Failed to launch '%1'.\n"
147 "\nError:"));
148 BPath path(button->Ref());
149 if (path.InitCheck() >= B_OK)
150 errStr.ReplaceFirst("%1", path.Path());
151 else
152 errStr.ReplaceFirst("%1", button->Ref()->name);
153 errorMessage << errStr.String() << " ";
154 errorMessage << strerror(ret);
155 } else
156 launchedByRef = true;
159 if (!launchedByRef && button->AppSignature()) {
160 status_t ret = be_roster->Launch(button->AppSignature());
161 if (ret != B_OK && ret != B_ALREADY_RUNNING) {
162 BString errStr(B_TRANSLATE("\n\nFailed to launch application "
163 "with signature '%2'.\n\nError:"));
164 errStr.ReplaceFirst("%2", button->AppSignature());
165 errorMessage << errStr.String() << " ";
166 errorMessage << strerror(ret);
167 } else {
168 // clear error message on success (might have been
169 // filled when trying to launch by ref)
170 errorMessage = "";
172 } else if (!launchedByRef) {
173 errorMessage = B_TRANSLATE("Failed to launch 'something', "
174 "error in Pad data.");
176 if (errorMessage.Length() > 0) {
177 BAlert* alert = new BAlert("error", errorMessage.String(),
178 B_TRANSLATE("Bummer"), NULL, NULL, B_WIDTH_FROM_WIDEST);
179 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
180 alert->Go(NULL);
182 break;
184 case MSG_ADD_SLOT:
186 LaunchButton* button;
187 if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
188 fPadView->AddButton(new LaunchButton("launch button",
189 NULL, new BMessage(MSG_LAUNCH)), button);
191 break;
193 case MSG_CLEAR_SLOT:
195 LaunchButton* button;
196 if (message->FindPointer("be:source", (void**)&button) >= B_OK)
197 button->SetTo((entry_ref*)NULL);
198 break;
200 case MSG_REMOVE_SLOT:
202 LaunchButton* button;
203 if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
204 if (fPadView->RemoveButton(button))
205 delete button;
207 break;
209 case MSG_SET_DESCRIPTION:
211 LaunchButton* button;
212 if (message->FindPointer("be:source", (void**)&button) >= B_OK) {
213 const char* name;
214 if (message->FindString("name", &name) >= B_OK) {
215 // message comes from a previous name panel
216 button->SetDescription(name);
217 BRect namePanelFrame;
218 if (message->FindRect("frame", &namePanelFrame) == B_OK) {
219 ((App*)be_app)->SetNamePanelSize(
220 namePanelFrame.Size());
222 } else {
223 // message comes from pad view
224 entry_ref* ref = button->Ref();
225 if (ref) {
226 BString helper(B_TRANSLATE("Description for '%3'"));
227 helper.ReplaceFirst("%3", ref->name);
228 // Place the name panel besides the pad, but give it
229 // the user configured size.
230 BPoint origin = B_ORIGIN;
231 BSize size = ((App*)be_app)->NamePanelSize();
232 NamePanel* panel = new NamePanel(helper.String(),
233 button->Description(), this, this,
234 new BMessage(*message), size);
235 panel->Layout(true);
236 size = panel->Frame().Size();
237 BScreen screen(this);
238 BPoint mousePos;
239 uint32 buttons;
240 fPadView->GetMouse(&mousePos, &buttons, false);
241 fPadView->ConvertToScreen(&mousePos);
242 if (fPadView->Orientation() == B_HORIZONTAL) {
243 // Place above or below the pad
244 origin.x = mousePos.x - size.width / 2;
245 if (screen.Frame().bottom - Frame().bottom
246 > size.height + 20) {
247 origin.y = Frame().bottom + 10;
248 } else {
249 origin.y = Frame().top - 10 - size.height;
251 } else {
252 // Place left or right of the pad
253 origin.y = mousePos.y - size.height / 2;
254 if (screen.Frame().right - Frame().right
255 > size.width + 20) {
256 origin.x = Frame().right + 10;
257 } else {
258 origin.x = Frame().left - 10 - size.width;
261 panel->MoveTo(origin);
262 panel->Show();
266 break;
268 case MSG_ADD_WINDOW:
270 BMessage settings('sett');
271 SaveSettings(&settings);
272 message->AddMessage("window", &settings);
273 be_app->PostMessage(message);
274 break;
276 case MSG_SHOW_BORDER:
277 SetLook(B_TITLED_WINDOW_LOOK);
278 break;
279 case MSG_HIDE_BORDER:
280 SetLook(B_BORDERED_WINDOW_LOOK);
281 break;
282 case MSG_TOGGLE_AUTORAISE:
283 ToggleAutoRaise();
284 break;
285 case MSG_SHOW_ON_ALL_WORKSPACES:
286 fShowOnAllWorkspaces = !fShowOnAllWorkspaces;
287 if (fShowOnAllWorkspaces)
288 SetWorkspaces(B_ALL_WORKSPACES);
289 else
290 SetWorkspaces(1L << current_workspace());
291 break;
292 case MSG_OPEN_CONTAINING_FOLDER:
294 LaunchButton* button;
295 if (message->FindPointer("be:source", (void**)&button) == B_OK && button->Ref() != NULL) {
296 entry_ref target = *button->Ref();
297 BEntry openTarget(&target);
298 BMessage openMsg(B_REFS_RECEIVED);
299 BMessenger tracker("application/x-vnd.Be-TRAK");
300 openTarget.GetParent(&openTarget);
301 openTarget.GetRef(&target);
302 openMsg.AddRef("refs",&target);
303 tracker.SendMessage(&openMsg);
306 break;
307 case B_SIMPLE_DATA:
308 case B_REFS_RECEIVED:
309 case B_PASTE:
310 case B_MODIFIERS_CHANGED:
311 break;
312 default:
313 BWindow::MessageReceived(message);
314 break;
319 void
320 MainWindow::Show()
322 BWindow::Show();
323 _GetLocation();
327 void
328 MainWindow::ScreenChanged(BRect frame, color_space format)
330 _AdjustLocation(Frame());
334 void
335 MainWindow::WorkspaceActivated(int32 workspace, bool active)
337 if (fShowOnAllWorkspaces) {
338 if (!active)
339 _GetLocation();
340 else
341 _AdjustLocation(Frame());
346 void
347 MainWindow::FrameMoved(BPoint origin)
349 if (IsActive()) {
350 _GetLocation();
351 _NotifySettingsChanged();
356 void
357 MainWindow::FrameResized(float width, float height)
359 if (IsActive()) {
360 _GetLocation();
361 _NotifySettingsChanged();
363 BWindow::FrameResized(width, height);
367 void
368 MainWindow::ToggleAutoRaise()
370 fAutoRaise = !fAutoRaise;
371 if (fAutoRaise)
372 fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
373 else
374 fPadView->SetEventMask(0);
376 _NotifySettingsChanged();
380 bool
381 MainWindow::LoadSettings(const BMessage* message)
383 // restore window positioning
384 BPoint point;
385 bool useAdjust = false;
386 if (message->FindPoint("window position", &point) == B_OK) {
387 fScreenPosition = point;
388 useAdjust = true;
390 float borderDist;
391 if (message->FindFloat("border distance", &borderDist) == B_OK) {
392 fBorderDist = borderDist;
394 // restore window frame
395 BRect frame;
396 if (message->FindRect("window frame", &frame) == B_OK) {
397 if (useAdjust) {
398 _AdjustLocation(frame);
399 } else {
400 make_sure_frame_is_on_screen(frame, this);
401 MoveTo(frame.LeftTop());
402 ResizeTo(frame.Width(), frame.Height());
406 // restore window look
407 window_look look;
408 if (message->FindInt32("window look", (int32*)&look) == B_OK)
409 SetLook(look);
411 // restore orientation
412 int32 orientation;
413 if (message->FindInt32("orientation", &orientation) == B_OK)
414 fPadView->SetOrientation((enum orientation)orientation);
416 // restore icon size
417 int32 iconSize;
418 if (message->FindInt32("icon size", &iconSize) == B_OK)
419 fPadView->SetIconSize(iconSize);
421 // restore ignore double click
422 bool ignoreDoubleClick;
423 if (message->FindBool("ignore double click", &ignoreDoubleClick) == B_OK)
424 fPadView->SetIgnoreDoubleClick(ignoreDoubleClick);
426 // restore buttons
427 const char* path;
428 bool buttonAdded = false;
429 for (int32 i = 0; message->FindString("path", i, &path) >= B_OK; i++) {
430 LaunchButton* button = new LaunchButton("launch button",
431 NULL, new BMessage(MSG_LAUNCH));
432 fPadView->AddButton(button);
433 BString signature;
434 if (message->FindString("signature", i, &signature) >= B_OK
435 && signature.CountChars() > 0) {
436 button->SetTo(signature.String(), true);
439 BEntry entry(path, true);
440 entry_ref ref;
441 if (entry.Exists() && entry.GetRef(&ref) == B_OK)
442 button->SetTo(&ref);
444 const char* text;
445 if (message->FindString("description", i, &text) >= B_OK)
446 button->SetDescription(text);
447 buttonAdded = true;
450 // restore auto raise setting
451 bool autoRaise;
452 if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise)
453 ToggleAutoRaise();
455 // store workspace setting
456 bool showOnAllWorkspaces;
457 if (message->FindBool("all workspaces", &showOnAllWorkspaces) == B_OK) {
458 fShowOnAllWorkspaces = showOnAllWorkspaces;
459 SetWorkspaces(showOnAllWorkspaces
460 ? B_ALL_WORKSPACES : 1L << current_workspace());
462 if (!fShowOnAllWorkspaces) {
463 uint32 workspaces;
464 if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK)
465 SetWorkspaces(workspaces);
468 return buttonAdded;
472 void
473 MainWindow::SaveSettings(BMessage* message)
475 // make sure the positioning info is correct
476 _GetLocation();
477 // store window position
478 if (message->ReplacePoint("window position", fScreenPosition) != B_OK)
479 message->AddPoint("window position", fScreenPosition);
481 if (message->ReplaceFloat("border distance", fBorderDist) != B_OK)
482 message->AddFloat("border distance", fBorderDist);
484 // store window frame and look
485 if (message->ReplaceRect("window frame", Frame()) != B_OK)
486 message->AddRect("window frame", Frame());
488 if (message->ReplaceInt32("window look", Look()) != B_OK)
489 message->AddInt32("window look", Look());
491 // store orientation
492 if (message->ReplaceInt32("orientation",
493 (int32)fPadView->Orientation()) != B_OK)
494 message->AddInt32("orientation", (int32)fPadView->Orientation());
496 // store icon size
497 if (message->ReplaceInt32("icon size", fPadView->IconSize()) != B_OK)
498 message->AddInt32("icon size", fPadView->IconSize());
500 // store ignore double click
501 if (message->ReplaceBool("ignore double click",
502 fPadView->IgnoreDoubleClick()) != B_OK) {
503 message->AddBool("ignore double click", fPadView->IgnoreDoubleClick());
506 // store buttons
507 message->RemoveName("path");
508 message->RemoveName("description");
509 message->RemoveName("signature");
510 for (int32 i = 0; LaunchButton* button = fPadView->ButtonAt(i); i++) {
511 BPath path(button->Ref());
512 if (path.InitCheck() >= B_OK)
513 message->AddString("path", path.Path());
514 else
515 message->AddString("path", "");
516 message->AddString("description", button->Description());
518 if (button->AppSignature())
519 message->AddString("signature", button->AppSignature());
520 else
521 message->AddString("signature", "");
524 // store auto raise setting
525 if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK)
526 message->AddBool("auto raise", fAutoRaise);
528 // store workspace setting
529 if (message->ReplaceBool("all workspaces", fShowOnAllWorkspaces) != B_OK)
530 message->AddBool("all workspaces", fShowOnAllWorkspaces);
531 if (message->ReplaceInt32("workspaces", Workspaces()) != B_OK)
532 message->AddInt32("workspaces", Workspaces());
536 void
537 MainWindow::_GetLocation()
539 BRect frame = Frame();
540 BPoint origin = frame.LeftTop();
541 BPoint center(origin.x + frame.Width() / 2.0,
542 origin.y + frame.Height() / 2.0);
543 BScreen screen(this);
544 BRect screenFrame = screen.Frame();
545 fScreenPosition.x = center.x / screenFrame.Width();
546 fScreenPosition.y = center.y / screenFrame.Height();
547 if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) {
548 // nearest to left or right border
549 if (fScreenPosition.x < 0.5)
550 fBorderDist = frame.left - screenFrame.left;
551 else
552 fBorderDist = screenFrame.right - frame.right;
553 } else {
554 // nearest to top or bottom border
555 if (fScreenPosition.y < 0.5)
556 fBorderDist = frame.top - screenFrame.top;
557 else
558 fBorderDist = screenFrame.bottom - frame.bottom;
563 void
564 MainWindow::_AdjustLocation(BRect frame)
566 BScreen screen(this);
567 BRect screenFrame = screen.Frame();
568 BPoint center(fScreenPosition.x * screenFrame.Width(),
569 fScreenPosition.y * screenFrame.Height());
570 BPoint frameCenter(frame.left + frame.Width() / 2.0,
571 frame.top + frame.Height() / 2.0);
572 frame.OffsetBy(center - frameCenter);
573 // ignore border dist when distance too large
574 if (fBorderDist < 10.0) {
575 // see which border we mean depending on screen position
576 BPoint offset(0.0, 0.0);
577 if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) {
578 // left or right border
579 if (fScreenPosition.x < 0.5)
580 offset.x = (screenFrame.left + fBorderDist) - frame.left;
581 else
582 offset.x = (screenFrame.right - fBorderDist) - frame.right;
583 } else {
584 // top or bottom border
585 if (fScreenPosition.y < 0.5)
586 offset.y = (screenFrame.top + fBorderDist) - frame.top;
587 else
588 offset.y = (screenFrame.bottom - fBorderDist) - frame.bottom;
590 frame.OffsetBy(offset);
593 make_sure_frame_is_on_screen(frame, this);
595 MoveTo(frame.LeftTop());
596 ResizeTo(frame.Width(), frame.Height());
600 void
601 MainWindow::_AddDefaultButtons()
603 // Mail
604 LaunchButton* button = new LaunchButton("launch button", NULL,
605 new BMessage(MSG_LAUNCH));
606 fPadView->AddButton(button);
607 button->SetTo("application/x-vnd.Be-MAIL", true);
609 // StyledEdit
610 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
611 fPadView->AddButton(button);
612 button->SetTo("application/x-vnd.Haiku-StyledEdit", true);
614 // ShowImage
615 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
616 fPadView->AddButton(button);
617 button->SetTo("application/x-vnd.Haiku-ShowImage", true);
619 // MediaPlayer
620 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
621 fPadView->AddButton(button);
622 button->SetTo("application/x-vnd.Haiku-MediaPlayer", true);
624 // DeskCalc
625 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
626 fPadView->AddButton(button);
627 button->SetTo("application/x-vnd.Haiku-DeskCalc", true);
629 // Terminal
630 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
631 fPadView->AddButton(button);
632 button->SetTo("application/x-vnd.Haiku-Terminal", true);
636 void
637 MainWindow::_AddEmptyButtons()
639 LaunchButton* button = new LaunchButton("launch button", NULL,
640 new BMessage(MSG_LAUNCH));
641 fPadView->AddButton(button);
643 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
644 fPadView->AddButton(button);
646 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH));
647 fPadView->AddButton(button);
651 void
652 MainWindow::_NotifySettingsChanged()
654 be_app->PostMessage(MSG_SETTINGS_CHANGED);