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