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