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 fNamePanelFrame(-1000.0, -1000.0, -800.0, -900.0), 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 fNamePanelFrame(-1000.0, -1000.0, -900.0, -900.0), 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 if (padWindowCount == 1) { 95 be_app->PostMessage(B_QUIT_REQUESTED); 96 return false; 97 } else { 98 BAlert* alert = new BAlert(B_TRANSLATE("last chance"), 99 B_TRANSLATE("Really close this pad?\n" 100 "(The pad will not be remembered.)"), 101 B_TRANSLATE("Close"), B_TRANSLATE("Cancel"), NULL); 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("Failed 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->Go(NULL); 177 } 178 break; 179 } 180 case MSG_ADD_SLOT: { 181 LaunchButton* button; 182 if (message->FindPointer("be:source", (void**)&button) >= B_OK) { 183 fPadView->AddButton(new LaunchButton("launch button", 184 NULL, new BMessage(MSG_LAUNCH)), button); 185 } 186 break; 187 } 188 case MSG_CLEAR_SLOT: { 189 LaunchButton* button; 190 if (message->FindPointer("be:source", (void**)&button) >= B_OK) 191 button->SetTo((entry_ref*)NULL); 192 break; 193 } 194 case MSG_REMOVE_SLOT: { 195 LaunchButton* button; 196 if (message->FindPointer("be:source", (void**)&button) >= B_OK) { 197 if (fPadView->RemoveButton(button)) 198 delete button; 199 } 200 break; 201 } 202 case MSG_SET_DESCRIPTION: { 203 LaunchButton* button; 204 if (message->FindPointer("be:source", (void**)&button) >= B_OK) { 205 const char* name; 206 if (message->FindString("name", &name) >= B_OK) { 207 // message comes from a previous name panel 208 button->SetDescription(name); 209 message->FindRect("frame", &fNamePanelFrame); 210 } else { 211 // message comes from pad view 212 entry_ref* ref = button->Ref(); 213 if (ref) { 214 BString helper(B_TRANSLATE("Description for '%3'")); 215 helper.ReplaceFirst("%3", ref->name); 216 make_sure_frame_is_on_screen(fNamePanelFrame, this); 217 new NamePanel(helper.String(), button->Description(), 218 this, this, new BMessage(*message), 219 fNamePanelFrame); 220 } 221 } 222 } 223 break; 224 } 225 case MSG_ADD_WINDOW: { 226 BMessage settings('sett'); 227 SaveSettings(&settings); 228 message->AddMessage("window", &settings); 229 be_app->PostMessage(message); 230 break; 231 } 232 case MSG_SHOW_BORDER: 233 SetLook(B_TITLED_WINDOW_LOOK); 234 break; 235 case MSG_HIDE_BORDER: 236 SetLook(B_BORDERED_WINDOW_LOOK); 237 break; 238 case MSG_TOGGLE_AUTORAISE: 239 ToggleAutoRaise(); 240 break; 241 case MSG_SHOW_ON_ALL_WORKSPACES: 242 fShowOnAllWorkspaces = !fShowOnAllWorkspaces; 243 if (fShowOnAllWorkspaces) 244 SetWorkspaces(B_ALL_WORKSPACES); 245 else 246 SetWorkspaces(1L << current_workspace()); 247 break; 248 case B_SIMPLE_DATA: 249 case B_REFS_RECEIVED: 250 case B_PASTE: 251 case B_MODIFIERS_CHANGED: 252 break; 253 default: 254 BWindow::MessageReceived(message); 255 break; 256 } 257 } 258 259 260 void 261 MainWindow::Show() 262 { 263 BWindow::Show(); 264 _GetLocation(); 265 } 266 267 268 void 269 MainWindow::ScreenChanged(BRect frame, color_space format) 270 { 271 _AdjustLocation(Frame()); 272 } 273 274 275 void 276 MainWindow::WorkspaceActivated(int32 workspace, bool active) 277 { 278 if (fShowOnAllWorkspaces) { 279 if (!active) 280 _GetLocation(); 281 else 282 _AdjustLocation(Frame()); 283 } 284 } 285 286 287 void 288 MainWindow::FrameMoved(BPoint origin) 289 { 290 if (IsActive()) { 291 _GetLocation(); 292 _NotifySettingsChanged(); 293 } 294 } 295 296 297 void 298 MainWindow::FrameResized(float width, float height) 299 { 300 if (IsActive()) { 301 _GetLocation(); 302 _NotifySettingsChanged(); 303 } 304 BWindow::FrameResized(width, height); 305 } 306 307 308 void 309 MainWindow::ToggleAutoRaise() 310 { 311 fAutoRaise = !fAutoRaise; 312 if (fAutoRaise) 313 fPadView->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 314 else 315 fPadView->SetEventMask(0); 316 317 _NotifySettingsChanged(); 318 } 319 320 321 bool 322 MainWindow::LoadSettings(const BMessage* message) 323 { 324 // restore window positioning 325 BPoint point; 326 bool useAdjust = false; 327 if (message->FindPoint("window position", &point) == B_OK) { 328 fScreenPosition = point; 329 useAdjust = true; 330 } 331 float borderDist; 332 if (message->FindFloat("border distance", &borderDist) == B_OK) { 333 fBorderDist = borderDist; 334 } 335 // restore window frame 336 BRect frame; 337 if (message->FindRect("window frame", &frame) == B_OK) { 338 if (useAdjust) { 339 _AdjustLocation(frame); 340 } else { 341 make_sure_frame_is_on_screen(frame, this); 342 MoveTo(frame.LeftTop()); 343 ResizeTo(frame.Width(), frame.Height()); 344 } 345 } 346 347 // restore name panel frame 348 if (message->FindRect("name panel frame", &frame) == B_OK) { 349 if (frame.IsValid()) { 350 make_sure_frame_is_on_screen(frame, this); 351 fNamePanelFrame = frame; 352 } 353 } 354 355 // restore window look 356 window_look look; 357 if (message->FindInt32("window look", (int32*)&look) == B_OK) 358 SetLook(look); 359 360 // restore orientation 361 int32 orientation; 362 if (message->FindInt32("orientation", &orientation) == B_OK) 363 fPadView->SetOrientation((enum orientation)orientation); 364 365 // restore icon size 366 int32 iconSize; 367 if (message->FindInt32("icon size", &iconSize) == B_OK) 368 fPadView->SetIconSize(iconSize); 369 370 // restore ignore double click 371 bool ignoreDoubleClick; 372 if (message->FindBool("ignore double click", &ignoreDoubleClick) == B_OK) 373 fPadView->SetIgnoreDoubleClick(ignoreDoubleClick); 374 375 // restore buttons 376 const char* path; 377 bool buttonAdded = false; 378 for (int32 i = 0; message->FindString("path", i, &path) >= B_OK; i++) { 379 LaunchButton* button = new LaunchButton("launch button", 380 NULL, new BMessage(MSG_LAUNCH)); 381 fPadView->AddButton(button); 382 BString signature; 383 if (message->FindString("signature", i, &signature) >= B_OK 384 && signature.CountChars() > 0) { 385 button->SetTo(signature.String(), true); 386 } 387 388 BEntry entry(path, true); 389 entry_ref ref; 390 if (entry.Exists() && entry.GetRef(&ref) == B_OK) 391 button->SetTo(&ref); 392 393 const char* text; 394 if (message->FindString("description", i, &text) >= B_OK) 395 button->SetDescription(text); 396 buttonAdded = true; 397 } 398 399 // restore auto raise setting 400 bool autoRaise; 401 if (message->FindBool("auto raise", &autoRaise) == B_OK && autoRaise) 402 ToggleAutoRaise(); 403 404 // store workspace setting 405 bool showOnAllWorkspaces; 406 if (message->FindBool("all workspaces", &showOnAllWorkspaces) == B_OK) 407 fShowOnAllWorkspaces = showOnAllWorkspaces; 408 SetWorkspaces(showOnAllWorkspaces 409 ? B_ALL_WORKSPACES : 1L << current_workspace()); 410 if (!fShowOnAllWorkspaces) { 411 uint32 workspaces; 412 if (message->FindInt32("workspaces", (int32*)&workspaces) == B_OK) 413 SetWorkspaces(workspaces); 414 } 415 416 return buttonAdded; 417 } 418 419 420 void 421 MainWindow::SaveSettings(BMessage* message) 422 { 423 // make sure the positioning info is correct 424 _GetLocation(); 425 // store window position 426 if (message->ReplacePoint("window position", fScreenPosition) != B_OK) 427 message->AddPoint("window position", fScreenPosition); 428 429 if (message->ReplaceFloat("border distance", fBorderDist) != B_OK) 430 message->AddFloat("border distance", fBorderDist); 431 432 // store window frame 433 if (message->ReplaceRect("window frame", Frame()) != B_OK) 434 message->AddRect("window frame", Frame()); 435 436 // store name panel frame 437 if (message->ReplaceRect("name panel frame", fNamePanelFrame) != B_OK) 438 message->AddRect("name panel frame", fNamePanelFrame); 439 440 if (message->ReplaceInt32("window look", Look()) != B_OK) 441 message->AddInt32("window look", Look()); 442 443 // store orientation 444 if (message->ReplaceInt32("orientation", 445 (int32)fPadView->Orientation()) != B_OK) 446 message->AddInt32("orientation", (int32)fPadView->Orientation()); 447 448 // store icon size 449 if (message->ReplaceInt32("icon size", fPadView->IconSize()) != B_OK) 450 message->AddInt32("icon size", fPadView->IconSize()); 451 452 // store ignore double click 453 if (message->ReplaceBool("ignore double click", 454 fPadView->IgnoreDoubleClick()) != B_OK) { 455 message->AddBool("ignore double click", fPadView->IgnoreDoubleClick()); 456 } 457 458 // store buttons 459 message->RemoveName("path"); 460 message->RemoveName("description"); 461 message->RemoveName("signature"); 462 for (int32 i = 0; LaunchButton* button = fPadView->ButtonAt(i); i++) { 463 BPath path(button->Ref()); 464 if (path.InitCheck() >= B_OK) 465 message->AddString("path", path.Path()); 466 else 467 message->AddString("path", ""); 468 message->AddString("description", button->Description()); 469 470 if (button->AppSignature()) 471 message->AddString("signature", button->AppSignature()); 472 else 473 message->AddString("signature", ""); 474 } 475 476 // store auto raise setting 477 if (message->ReplaceBool("auto raise", fAutoRaise) != B_OK) 478 message->AddBool("auto raise", fAutoRaise); 479 480 // store workspace setting 481 if (message->ReplaceBool("all workspaces", fShowOnAllWorkspaces) != B_OK) 482 message->AddBool("all workspaces", fShowOnAllWorkspaces); 483 if (message->ReplaceInt32("workspaces", Workspaces()) != B_OK) 484 message->AddInt32("workspaces", Workspaces()); 485 } 486 487 488 void 489 MainWindow::_GetLocation() 490 { 491 BRect frame = Frame(); 492 BPoint origin = frame.LeftTop(); 493 BPoint center(origin.x + frame.Width() / 2.0, 494 origin.y + frame.Height() / 2.0); 495 BScreen screen(this); 496 BRect screenFrame = screen.Frame(); 497 fScreenPosition.x = center.x / screenFrame.Width(); 498 fScreenPosition.y = center.y / screenFrame.Height(); 499 if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) { 500 // nearest to left or right border 501 if (fScreenPosition.x < 0.5) 502 fBorderDist = frame.left - screenFrame.left; 503 else 504 fBorderDist = screenFrame.right - frame.right; 505 } else { 506 // nearest to top or bottom border 507 if (fScreenPosition.y < 0.5) 508 fBorderDist = frame.top - screenFrame.top; 509 else 510 fBorderDist = screenFrame.bottom - frame.bottom; 511 } 512 } 513 514 515 void 516 MainWindow::_AdjustLocation(BRect frame) 517 { 518 BScreen screen(this); 519 BRect screenFrame = screen.Frame(); 520 BPoint center(fScreenPosition.x * screenFrame.Width(), 521 fScreenPosition.y * screenFrame.Height()); 522 BPoint frameCenter(frame.left + frame.Width() / 2.0, 523 frame.top + frame.Height() / 2.0); 524 frame.OffsetBy(center - frameCenter); 525 // ignore border dist when distance too large 526 if (fBorderDist < 10.0) { 527 // see which border we mean depending on screen position 528 BPoint offset(0.0, 0.0); 529 if (fabs(0.5 - fScreenPosition.x) > fabs(0.5 - fScreenPosition.y)) { 530 // left or right border 531 if (fScreenPosition.x < 0.5) 532 offset.x = (screenFrame.left + fBorderDist) - frame.left; 533 else 534 offset.x = (screenFrame.right - fBorderDist) - frame.right; 535 } else { 536 // top or bottom border 537 if (fScreenPosition.y < 0.5) 538 offset.y = (screenFrame.top + fBorderDist) - frame.top; 539 else 540 offset.y = (screenFrame.bottom - fBorderDist) - frame.bottom; 541 } 542 frame.OffsetBy(offset); 543 } 544 545 make_sure_frame_is_on_screen(frame, this); 546 547 MoveTo(frame.LeftTop()); 548 ResizeTo(frame.Width(), frame.Height()); 549 } 550 551 552 void 553 MainWindow::_AddDefaultButtons() 554 { 555 // Mail 556 LaunchButton* button = new LaunchButton("launch button", NULL, 557 new BMessage(MSG_LAUNCH)); 558 fPadView->AddButton(button); 559 button->SetTo("application/x-vnd.Be-MAIL", true); 560 561 // StyledEdit 562 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 563 fPadView->AddButton(button); 564 button->SetTo("application/x-vnd.Haiku-StyledEdit", true); 565 566 // ShowImage 567 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 568 fPadView->AddButton(button); 569 button->SetTo("application/x-vnd.Haiku-ShowImage", true); 570 571 // MediaPlayer 572 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 573 fPadView->AddButton(button); 574 button->SetTo("application/x-vnd.Haiku-MediaPlayer", true); 575 576 // DeskCalc 577 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 578 fPadView->AddButton(button); 579 button->SetTo("application/x-vnd.Haiku-DeskCalc", true); 580 581 // Terminal 582 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 583 fPadView->AddButton(button); 584 button->SetTo("application/x-vnd.Haiku-Terminal", true); 585 } 586 587 588 void 589 MainWindow::_AddEmptyButtons() 590 { 591 LaunchButton* button = new LaunchButton("launch button", NULL, 592 new BMessage(MSG_LAUNCH)); 593 fPadView->AddButton(button); 594 595 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 596 fPadView->AddButton(button); 597 598 button = new LaunchButton("launch button", NULL, new BMessage(MSG_LAUNCH)); 599 fPadView->AddButton(button); 600 } 601 602 603 void 604 MainWindow::_NotifySettingsChanged() 605 { 606 be_app->PostMessage(MSG_SETTINGS_CHANGED); 607 } 608 609