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