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