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