1 /* 2 * Copyright 2006 - 2011, 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 <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> 20 21 #include "support.h" 22 23 #include "App.h" 24 #include "LaunchButton.h" 25 #include "NamePanel.h" 26 #include "PadView.h" 27 28 29 #undef B_TRANSLATION_CONTEXT 30 #define B_TRANSLATION_CONTEXT "LaunchBox" 31 MainWindow::MainWindow(const char* name, BRect frame, bool addDefaultButtons) 32 : 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) 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 58 MainWindow::MainWindow(const char* name, BRect frame, BMessage* settings) 59 : 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) 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 bool canClose = true; 94 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; 106 } 107 return canClose; 108 } 109 110 111 void 112 MainWindow::MessageReceived(BMessage* message) 113 { 114 switch (message->what) { 115 case MSG_LAUNCH: 116 { 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; 157 } 158 } 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 = ""; 171 } 172 } else if (!launchedByRef) { 173 errorMessage = B_TRANSLATE("Failed to launch 'something', " 174 "error in Pad data."); 175 } 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); 181 } 182 break; 183 } 184 case MSG_ADD_SLOT: 185 { 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); 190 } 191 break; 192 } 193 case MSG_CLEAR_SLOT: 194 { 195 LaunchButton* button; 196 if (message->FindPointer("be:source", (void**)&button) >= B_OK) 197 button->SetTo((entry_ref*)NULL); 198 break; 199 } 200 case MSG_REMOVE_SLOT: 201 { 202 LaunchButton* button; 203 if (message->FindPointer("be:source", (void**)&button) >= B_OK) { 204 if (fPadView->RemoveButton(button)) 205 delete button; 206 } 207 break; 208 } 209 case MSG_SET_DESCRIPTION: 210 { 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()); 221 } 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; 250 } 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; 259 } 260 } 261 panel->MoveTo(origin); 262 panel->Show(); 263 } 264 } 265 } 266 break; 267 } 268 case MSG_ADD_WINDOW: 269 { 270 BMessage settings('sett'); 271 SaveSettings(&settings); 272 message->AddMessage("window", &settings); 273 be_app->PostMessage(message); 274 break; 275 } 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: 293 { 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); 304 } 305 } 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; 315 } 316 } 317 318 319 void 320 MainWindow::Show() 321 { 322 BWindow::Show(); 323 _GetLocation(); 324 } 325 326 327 void 328 MainWindow::ScreenChanged(BRect frame, color_space format) 329 { 330 _AdjustLocation(Frame()); 331 } 332 333 334 void 335 MainWindow::WorkspaceActivated(int32 workspace, bool active) 336 { 337 if (fShowOnAllWorkspaces) { 338 if (!active) 339 _GetLocation(); 340 else 341 _AdjustLocation(Frame()); 342 } 343 } 344 345 346 void 347 MainWindow::FrameMoved(BPoint origin) 348 { 349 if (IsActive()) { 350 _GetLocation(); 351 _NotifySettingsChanged(); 352 } 353 } 354 355 356 void 357 MainWindow::FrameResized(float width, float height) 358 { 359 if (IsActive()) { 360 _GetLocation(); 361 _NotifySettingsChanged(); 362 } 363 BWindow::FrameResized(width, height); 364 } 365 366 367 void 368 MainWindow::ToggleAutoRaise() 369 { 370 fAutoRaise = !fAutoRaise; 371 if (fAutoRaise) 372 fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 373 else 374 fPadView->SetEventMask(0); 375 376 _NotifySettingsChanged(); 377 } 378 379 380 bool 381 MainWindow::LoadSettings(const BMessage* message) 382 { 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; 389 } 390 float borderDist; 391 if (message->FindFloat("border distance", &borderDist) == B_OK) { 392 fBorderDist = borderDist; 393 } 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()); 403 } 404 } 405 406 // restore window look 407 window_look look; 408 if (message->FindInt32("window look", (int32*)&look) == B_OK) 409 SetLook(look); 410 411 // restore orientation 412 int32 orientation; 413 if (message->FindInt32("orientation", &orientation) == B_OK) 414 fPadView->SetOrientation((enum orientation)orientation); 415 416 // restore icon size 417 int32 iconSize; 418 if (message->FindInt32("icon size", &iconSize) == B_OK) 419 fPadView->SetIconSize(iconSize); 420 421 // restore ignore double click 422 bool ignoreDoubleClick; 423 if (message->FindBool("ignore double click", &ignoreDoubleClick) == B_OK) 424 fPadView->SetIgnoreDoubleClick(ignoreDoubleClick); 425 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); 437 } 438 439 BEntry entry(path, true); 440 entry_ref ref; 441 if (entry.Exists() && entry.GetRef(&ref) == B_OK) 442 button->SetTo(&ref); 443 444 const char* text; 445 if (message->FindString("description", i, &text) >= B_OK) 446 button->SetDescription(text); 447 buttonAdded = true; 448 } 449 450 // restore auto raise setting 451 bool autoRaise; 452 if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise) 453 ToggleAutoRaise(); 454 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()); 461 } 462 if (!fShowOnAllWorkspaces) { 463 uint32 workspaces; 464 if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK) 465 SetWorkspaces(workspaces); 466 } 467 468 return buttonAdded; 469 } 470 471 472 void 473 MainWindow::SaveSettings(BMessage* message) 474 { 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); 480 481 if (message->ReplaceFloat("border distance", fBorderDist) != B_OK) 482 message->AddFloat("border distance", fBorderDist); 483 484 // store window frame and look 485 if (message->ReplaceRect("window frame", Frame()) != B_OK) 486 message->AddRect("window frame", Frame()); 487 488 if (message->ReplaceInt32("window look", Look()) != B_OK) 489 message->AddInt32("window look", Look()); 490 491 // store orientation 492 if (message->ReplaceInt32("orientation", 493 (int32)fPadView->Orientation()) != B_OK) 494 message->AddInt32("orientation", (int32)fPadView->Orientation()); 495 496 // store icon size 497 if (message->ReplaceInt32("icon size", fPadView->IconSize()) != B_OK) 498 message->AddInt32("icon size", fPadView->IconSize()); 499 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()); 504 } 505 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()); 517 518 if (button->AppSignature()) 519 message->AddString("signature", button->AppSignature()); 520 else 521 message->AddString("signature", ""); 522 } 523 524 // store auto raise setting 525 if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK) 526 message->AddBool("auto raise", fAutoRaise); 527 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()); 533 } 534 535 536 void 537 MainWindow::_GetLocation() 538 { 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; 559 } 560 } 561 562 563 void 564 MainWindow::_AdjustLocation(BRect frame) 565 { 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; 589 } 590 frame.OffsetBy(offset); 591 } 592 593 make_sure_frame_is_on_screen(frame, this); 594 595 MoveTo(frame.LeftTop()); 596 ResizeTo(frame.Width(), frame.Height()); 597 } 598 599 600 void 601 MainWindow::_AddDefaultButtons() 602 { 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); 608 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); 613 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); 618 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); 623 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); 628 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); 633 } 634 635 636 void 637 MainWindow::_AddEmptyButtons() 638 { 639 LaunchButton* button = new LaunchButton("launch button", NULL, 640 new BMessage(MSG_LAUNCH)); 641 fPadView->AddButton(button); 642 643 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 644 fPadView->AddButton(button); 645 646 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 647 fPadView->AddButton(button); 648 } 649 650 651 void 652 MainWindow::_NotifySettingsChanged() 653 { 654 be_app->PostMessage(MSG_SETTINGS_CHANGED); 655 } 656 657