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 case B_ABOUT_REQUESTED: 256 be_app->PostMessage(message); 257 break; 258 default: 259 BWindow::MessageReceived(message); 260 break; 261 } 262 } 263 264 265 void 266 MainWindow::Show() 267 { 268 BWindow::Show(); 269 _GetLocation(); 270 } 271 272 273 void 274 MainWindow::ScreenChanged(BRect frame, color_space format) 275 { 276 _AdjustLocation(Frame()); 277 } 278 279 280 void 281 MainWindow::WorkspaceActivated(int32 workspace, bool active) 282 { 283 if (fShowOnAllWorkspaces) { 284 if (!active) 285 _GetLocation(); 286 else 287 _AdjustLocation(Frame()); 288 } 289 } 290 291 292 void 293 MainWindow::FrameMoved(BPoint origin) 294 { 295 if (IsActive()) { 296 _GetLocation(); 297 _NotifySettingsChanged(); 298 } 299 } 300 301 302 void 303 MainWindow::FrameResized(float width, float height) 304 { 305 if (IsActive()) { 306 _GetLocation(); 307 _NotifySettingsChanged(); 308 } 309 BWindow::FrameResized(width, height); 310 } 311 312 313 void 314 MainWindow::ToggleAutoRaise() 315 { 316 fAutoRaise = !fAutoRaise; 317 if (fAutoRaise) 318 fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 319 else 320 fPadView->SetEventMask(0); 321 322 _NotifySettingsChanged(); 323 } 324 325 326 bool 327 MainWindow::LoadSettings(const BMessage* message) 328 { 329 // restore window positioning 330 BPoint point; 331 bool useAdjust = false; 332 if (message->FindPoint("window position", &point) == B_OK) { 333 fScreenPosition = point; 334 useAdjust = true; 335 } 336 float borderDist; 337 if (message->FindFloat("border distance", &borderDist) == B_OK) { 338 fBorderDist = borderDist; 339 } 340 // restore window frame 341 BRect frame; 342 if (message->FindRect("window frame", &frame) == B_OK) { 343 if (useAdjust) { 344 _AdjustLocation(frame); 345 } else { 346 make_sure_frame_is_on_screen(frame, this); 347 MoveTo(frame.LeftTop()); 348 ResizeTo(frame.Width(), frame.Height()); 349 } 350 } 351 352 // restore name panel frame 353 if (message->FindRect("name panel frame", &frame) == B_OK) { 354 if (frame.IsValid()) { 355 make_sure_frame_is_on_screen(frame, this); 356 fNamePanelFrame = frame; 357 } 358 } 359 360 // restore window look 361 window_look look; 362 if (message->FindInt32("window look", (int32*)&look) == B_OK) 363 SetLook(look); 364 365 // restore orientation 366 int32 orientation; 367 if (message->FindInt32("orientation", &orientation) == B_OK) 368 fPadView->SetOrientation((enum orientation)orientation); 369 370 // restore icon size 371 int32 iconSize; 372 if (message->FindInt32("icon size", &iconSize) == B_OK) 373 fPadView->SetIconSize(iconSize); 374 375 // restore ignore double click 376 bool ignoreDoubleClick; 377 if (message->FindBool("ignore double click", &ignoreDoubleClick) == B_OK) 378 fPadView->SetIgnoreDoubleClick(ignoreDoubleClick); 379 380 // restore buttons 381 const char* path; 382 bool buttonAdded = false; 383 for (int32 i = 0; message->FindString("path", i, &path) >= B_OK; i++) { 384 LaunchButton* button = new LaunchButton("launch button", fLastID++, 385 NULL, new BMessage(MSG_LAUNCH)); 386 fPadView->AddButton(button); 387 BString signature; 388 if (message->FindString("signature", i, &signature) >= B_OK 389 && signature.CountChars() > 0) { 390 button->SetTo(signature.String(), true); 391 } 392 393 BEntry entry(path, true); 394 entry_ref ref; 395 if (entry.Exists() && entry.GetRef(&ref) == B_OK) 396 button->SetTo(&ref); 397 398 const char* text; 399 if (message->FindString("description", i, &text) >= B_OK) 400 button->SetDescription(text); 401 buttonAdded = true; 402 } 403 404 // restore auto raise setting 405 bool autoRaise; 406 if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise) 407 ToggleAutoRaise(); 408 409 // store workspace setting 410 bool showOnAllWorkspaces; 411 if (message->FindBool("all workspaces", &showOnAllWorkspaces) == B_OK) 412 fShowOnAllWorkspaces = showOnAllWorkspaces; 413 SetWorkspaces(showOnAllWorkspaces 414 ? B_ALL_WORKSPACES : 1L << current_workspace()); 415 if (!fShowOnAllWorkspaces) { 416 uint32 workspaces; 417 if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK) 418 SetWorkspaces(workspaces); 419 } 420 421 return buttonAdded; 422 } 423 424 425 void 426 MainWindow::SaveSettings(BMessage* message) 427 { 428 // make sure the positioning info is correct 429 _GetLocation(); 430 // store window position 431 if (message->ReplacePoint("window position", fScreenPosition) != B_OK) 432 message->AddPoint("window position", fScreenPosition); 433 434 if (message->ReplaceFloat("border distance", fBorderDist) != B_OK) 435 message->AddFloat("border distance", fBorderDist); 436 437 // store window frame 438 if (message->ReplaceRect("window frame", Frame()) != B_OK) 439 message->AddRect("window frame", Frame()); 440 441 // store name panel frame 442 if (message->ReplaceRect("name panel frame", fNamePanelFrame) != B_OK) 443 message->AddRect("name panel frame", fNamePanelFrame); 444 445 if (message->ReplaceInt32("window look", Look()) != B_OK) 446 message->AddInt32("window look", Look()); 447 448 // store orientation 449 if (message->ReplaceInt32("orientation", 450 (int32)fPadView->Orientation()) != B_OK) 451 message->AddInt32("orientation", (int32)fPadView->Orientation()); 452 453 // store icon size 454 if (message->ReplaceInt32("icon size", fPadView->IconSize()) != B_OK) 455 message->AddInt32("icon size", fPadView->IconSize()); 456 457 // store ignore double click 458 if (message->ReplaceBool("ignore double click", 459 fPadView->IgnoreDoubleClick()) != B_OK) { 460 message->AddBool("ignore double click", fPadView->IgnoreDoubleClick()); 461 } 462 463 // store buttons 464 message->RemoveName("path"); 465 message->RemoveName("description"); 466 message->RemoveName("signature"); 467 for (int32 i = 0; LaunchButton* button = fPadView->ButtonAt(i); i++) { 468 BPath path(button->Ref()); 469 if (path.InitCheck() >= B_OK) 470 message->AddString("path", path.Path()); 471 else 472 message->AddString("path", ""); 473 message->AddString("description", button->Description()); 474 475 if (button->AppSignature()) 476 message->AddString("signature", button->AppSignature()); 477 else 478 message->AddString("signature", ""); 479 } 480 481 // store auto raise setting 482 if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK) 483 message->AddBool("auto raise", fAutoRaise); 484 485 // store workspace setting 486 if (message->ReplaceBool("all workspaces", fShowOnAllWorkspaces) != B_OK) 487 message->AddBool("all workspaces", fShowOnAllWorkspaces); 488 if (message->ReplaceInt32("workspaces", Workspaces()) != B_OK) 489 message->AddInt32("workspaces", Workspaces()); 490 } 491 492 493 void 494 MainWindow::_GetLocation() 495 { 496 BRect frame = Frame(); 497 BPoint origin = frame.LeftTop(); 498 BPoint center(origin.x + frame.Width() / 2.0, 499 origin.y + frame.Height() / 2.0); 500 BScreen screen(this); 501 BRect screenFrame = screen.Frame(); 502 fScreenPosition.x = center.x / screenFrame.Width(); 503 fScreenPosition.y = center.y / screenFrame.Height(); 504 if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) { 505 // nearest to left or right border 506 if (fScreenPosition.x < 0.5) 507 fBorderDist = frame.left - screenFrame.left; 508 else 509 fBorderDist = screenFrame.right - frame.right; 510 } else { 511 // nearest to top or bottom border 512 if (fScreenPosition.y < 0.5) 513 fBorderDist = frame.top - screenFrame.top; 514 else 515 fBorderDist = screenFrame.bottom - frame.bottom; 516 } 517 } 518 519 520 void 521 MainWindow::_AdjustLocation(BRect frame) 522 { 523 BScreen screen(this); 524 BRect screenFrame = screen.Frame(); 525 BPoint center(fScreenPosition.x * screenFrame.Width(), 526 fScreenPosition.y * screenFrame.Height()); 527 BPoint frameCenter(frame.left + frame.Width() / 2.0, 528 frame.top + frame.Height() / 2.0); 529 frame.OffsetBy(center - frameCenter); 530 // ignore border dist when distance too large 531 if (fBorderDist < 10.0) { 532 // see which border we mean depending on screen position 533 BPoint offset(0.0, 0.0); 534 if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) { 535 // left or right border 536 if (fScreenPosition.x < 0.5) 537 offset.x = (screenFrame.left + fBorderDist) - frame.left; 538 else 539 offset.x = (screenFrame.right - fBorderDist) - frame.right; 540 } else { 541 // top or bottom border 542 if (fScreenPosition.y < 0.5) 543 offset.y = (screenFrame.top + fBorderDist) - frame.top; 544 else 545 offset.y = (screenFrame.bottom - fBorderDist) - frame.bottom; 546 } 547 frame.OffsetBy(offset); 548 } 549 550 make_sure_frame_is_on_screen(frame, this); 551 552 MoveTo(frame.LeftTop()); 553 ResizeTo(frame.Width(), frame.Height()); 554 } 555 556 557 void 558 MainWindow::_AddDefaultButtons() 559 { 560 // Mail 561 LaunchButton* button = new LaunchButton("launch button", fLastID++, NULL, 562 new BMessage(MSG_LAUNCH)); 563 fPadView->AddButton(button); 564 button->SetTo("application/x-vnd.Be-MAIL", true); 565 566 // StyledEdit 567 button = new LaunchButton("launch button", fLastID++, NULL, 568 new BMessage(MSG_LAUNCH)); 569 fPadView->AddButton(button); 570 button->SetTo("application/x-vnd.Haiku-StyledEdit", true); 571 572 // ShowImage 573 button = new LaunchButton("launch button", fLastID++, NULL, 574 new BMessage(MSG_LAUNCH)); 575 fPadView->AddButton(button); 576 button->SetTo("application/x-vnd.Haiku-ShowImage", true); 577 578 // MediaPlayer 579 button = new LaunchButton("launch button", fLastID++, NULL, 580 new BMessage(MSG_LAUNCH)); 581 fPadView->AddButton(button); 582 button->SetTo("application/x-vnd.Haiku-MediaPlayer", true); 583 584 // DeskCalc 585 button = new LaunchButton("launch button", fLastID++, NULL, 586 new BMessage(MSG_LAUNCH)); 587 fPadView->AddButton(button); 588 button->SetTo("application/x-vnd.Haiku-DeskCalc", true); 589 590 // Terminal 591 button = new LaunchButton("launch button", fLastID++, NULL, 592 new BMessage(MSG_LAUNCH)); 593 fPadView->AddButton(button); 594 button->SetTo("application/x-vnd.Haiku-Terminal", true); 595 } 596 597 598 void 599 MainWindow::_AddEmptyButtons() 600 { 601 LaunchButton* button = new LaunchButton("launch button", fLastID++, NULL, 602 new BMessage(MSG_LAUNCH)); 603 fPadView->AddButton(button); 604 605 button = new LaunchButton("launch button", fLastID++, NULL, 606 new BMessage(MSG_LAUNCH)); 607 fPadView->AddButton(button); 608 609 button = new LaunchButton("launch button", fLastID++, NULL, 610 new BMessage(MSG_LAUNCH)); 611 fPadView->AddButton(button); 612 } 613 614 615 void 616 MainWindow::_NotifySettingsChanged() 617 { 618 be_app->PostMessage(MSG_SETTINGS_CHANGED); 619 } 620 621