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