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