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