1 /* 2 * Copyright 2002-2009, Haiku, Inc. All rights reserved. 3 * Copyright 2002, François Revol, revol@free.fr. 4 * This file is distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * François Revol, revol@free.fr 8 * Axel Dörfler, axeld@pinc-software.de 9 * Oliver "Madison" Kohl, 10 * Matt Madia 11 */ 12 13 14 #include <Alert.h> 15 #include <Application.h> 16 #include <Catalog.h> 17 #include <Dragger.h> 18 #include <Entry.h> 19 #include <File.h> 20 #include <FindDirectory.h> 21 #include <Locale.h> 22 #include <MenuItem.h> 23 #include <Path.h> 24 #include <PopUpMenu.h> 25 #include <Roster.h> 26 #include <Screen.h> 27 #include <TextView.h> 28 #include <Window.h> 29 30 #include <ctype.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #include <InterfacePrivate.h> 36 #include <ViewPrivate.h> 37 #include <WindowPrivate.h> 38 39 #undef B_TRANSLATE_CONTEXT 40 #define B_TRANSLATE_CONTEXT "Workspaces" 41 42 43 static const char* kSignature = "application/x-vnd.Be-WORK"; 44 static const char* kOldSettingFile = "Workspace_data"; 45 static const char* kSettingsFile = "Workspaces_settings"; 46 47 static const uint32 kMsgChangeCount = 'chWC'; 48 static const uint32 kMsgToggleTitle = 'tgTt'; 49 static const uint32 kMsgToggleBorder = 'tgBd'; 50 static const uint32 kMsgToggleAutoRaise = 'tgAR'; 51 static const uint32 kMsgToggleAlwaysOnTop = 'tgAT'; 52 53 static const float kScreenBorderOffset = 10.0; 54 55 56 class WorkspacesSettings { 57 public: 58 WorkspacesSettings(); 59 virtual ~WorkspacesSettings(); 60 61 BRect WindowFrame() const { return fWindowFrame; } 62 BRect ScreenFrame() const { return fScreenFrame; } 63 64 bool AutoRaising() const { return fAutoRaising; } 65 bool AlwaysOnTop() const { return fAlwaysOnTop; } 66 bool HasTitle() const { return fHasTitle; } 67 bool HasBorder() const { return fHasBorder; } 68 69 void UpdateFramesForScreen(BRect screenFrame); 70 void UpdateScreenFrame(); 71 72 void SetWindowFrame(BRect); 73 void SetAutoRaising(bool enable) { fAutoRaising = enable; } 74 void SetAlwaysOnTop(bool enable) { fAlwaysOnTop = enable; } 75 void SetHasTitle(bool enable) { fHasTitle = enable; } 76 void SetHasBorder(bool enable) { fHasBorder = enable; } 77 78 private: 79 status_t _Open(BFile& file, int mode); 80 81 BRect fWindowFrame; 82 BRect fScreenFrame; 83 bool fAutoRaising; 84 bool fAlwaysOnTop; 85 bool fHasTitle; 86 bool fHasBorder; 87 }; 88 89 class WorkspacesView : public BView { 90 public: 91 WorkspacesView(BRect frame); 92 WorkspacesView(BMessage* archive); 93 ~WorkspacesView(); 94 95 static WorkspacesView* Instantiate(BMessage* archive); 96 virtual status_t Archive(BMessage* archive, bool deep = true) const; 97 98 virtual void AttachedToWindow(); 99 virtual void DetachedFromWindow(); 100 virtual void FrameMoved(BPoint newPosition); 101 virtual void FrameResized(float newWidth, float newHeight); 102 virtual void MessageReceived(BMessage* message); 103 virtual void MouseMoved(BPoint where, uint32 transit, 104 const BMessage* dragMessage); 105 virtual void MouseDown(BPoint where); 106 107 private: 108 void _AboutRequested(); 109 110 void _UpdateParentClipping(); 111 void _ExcludeFromParentClipping(); 112 void _CleanupParentClipping(); 113 114 BView* fParentWhichDrawsOnChildren; 115 BRect fCurrentFrame; 116 }; 117 118 class WorkspacesWindow : public BWindow { 119 public: 120 WorkspacesWindow(WorkspacesSettings *settings); 121 virtual ~WorkspacesWindow(); 122 123 virtual void ScreenChanged(BRect frame, color_space mode); 124 virtual void FrameMoved(BPoint origin); 125 virtual void FrameResized(float width, float height); 126 virtual void Zoom(BPoint origin, float width, float height); 127 128 virtual void MessageReceived(BMessage *msg); 129 virtual bool QuitRequested(); 130 131 void SetAutoRaise(bool enable); 132 bool IsAutoRaising() const { return fAutoRaising; } 133 134 private: 135 WorkspacesSettings *fSettings; 136 bool fAutoRaising; 137 }; 138 139 class WorkspacesApp : public BApplication { 140 public: 141 WorkspacesApp(); 142 virtual ~WorkspacesApp(); 143 144 virtual void AboutRequested(); 145 virtual void ArgvReceived(int32 argc, char **argv); 146 virtual void ReadyToRun(); 147 148 void Usage(const char *programName); 149 150 private: 151 WorkspacesWindow* fWindow; 152 }; 153 154 155 WorkspacesSettings::WorkspacesSettings() 156 : 157 fAutoRaising(false), 158 fAlwaysOnTop(false), 159 fHasTitle(true), 160 fHasBorder(true) 161 { 162 UpdateScreenFrame(); 163 164 bool loaded = false; 165 BScreen screen; 166 167 BFile file; 168 if (_Open(file, B_READ_ONLY) == B_OK) { 169 BMessage settings; 170 if (settings.Unflatten(&file) == B_OK) { 171 if (settings.FindRect("window", &fWindowFrame) == B_OK 172 && settings.FindRect("screen", &fScreenFrame) == B_OK) 173 loaded = true; 174 175 settings.FindBool("auto-raise", &fAutoRaising); 176 settings.FindBool("always on top", &fAlwaysOnTop); 177 178 if (settings.FindBool("has title", &fHasTitle) != B_OK) 179 fHasTitle = true; 180 if (settings.FindBool("has border", &fHasBorder) != B_OK) 181 fHasBorder = true; 182 } 183 } else { 184 // try reading BeOS compatible settings 185 BPath path; 186 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 187 path.Append(kOldSettingFile); 188 BFile file(path.Path(), B_READ_ONLY); 189 if (file.InitCheck() == B_OK 190 && file.Read(&fWindowFrame, sizeof(BRect)) == sizeof(BRect)) { 191 // we now also store the frame of the screen to know 192 // in which context the window frame has been chosen 193 BRect frame; 194 if (file.Read(&frame, sizeof(BRect)) == sizeof(BRect)) 195 fScreenFrame = frame; 196 else 197 fScreenFrame = screen.Frame(); 198 199 loaded = true; 200 } 201 } 202 } 203 204 if (loaded) { 205 // if the current screen frame is different from the one 206 // just loaded, we need to alter the window frame accordingly 207 if (fScreenFrame != screen.Frame()) 208 UpdateFramesForScreen(screen.Frame()); 209 } 210 211 if (!loaded 212 || !(screen.Frame().right + 5 >= fWindowFrame.right 213 && screen.Frame().bottom + 5 >= fWindowFrame.bottom 214 && screen.Frame().left - 5 <= fWindowFrame.left 215 && screen.Frame().top - 5 <= fWindowFrame.top)) { 216 // set to some usable defaults 217 float screenWidth = screen.Frame().Width(); 218 float screenHeight = screen.Frame().Height(); 219 float aspectRatio = screenWidth / screenHeight; 220 221 uint32 columns, rows; 222 BPrivate::get_workspaces_layout(&columns, &rows); 223 224 // default size of ~1/10 of screen width 225 float workspaceWidth = screenWidth / 10; 226 float workspaceHeight = workspaceWidth / aspectRatio; 227 228 float width = floor(workspaceWidth * columns); 229 float height = floor(workspaceHeight * rows); 230 231 float tabHeight = 20; 232 // TODO: find tabHeight without being a window 233 234 // shrink to fit more 235 while (width + 2 * kScreenBorderOffset > screenWidth 236 || height + 2 * kScreenBorderOffset + tabHeight > screenHeight) { 237 width = floor(0.95 * width); 238 height = floor(0.95 * height); 239 } 240 241 fWindowFrame = fScreenFrame; 242 fWindowFrame.OffsetBy(-kScreenBorderOffset, -kScreenBorderOffset); 243 fWindowFrame.left = fWindowFrame.right - width; 244 fWindowFrame.top = fWindowFrame.bottom - height; 245 } 246 } 247 248 249 WorkspacesSettings::~WorkspacesSettings() 250 { 251 // write settings file 252 BFile file; 253 if (_Open(file, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE) != B_OK) 254 return; 255 256 BMessage settings('wksp'); 257 258 if (settings.AddRect("window", fWindowFrame) == B_OK 259 && settings.AddRect("screen", fScreenFrame) == B_OK 260 && settings.AddBool("auto-raise", fAutoRaising) == B_OK 261 && settings.AddBool("always on top", fAlwaysOnTop) == B_OK 262 && settings.AddBool("has title", fHasTitle) == B_OK 263 && settings.AddBool("has border", fHasBorder) == B_OK) 264 settings.Flatten(&file); 265 } 266 267 268 status_t 269 WorkspacesSettings::_Open(BFile& file, int mode) 270 { 271 BPath path; 272 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 273 if (status != B_OK) 274 status = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path); 275 if (status != B_OK) 276 return status; 277 278 path.Append(kSettingsFile); 279 280 status = file.SetTo(path.Path(), mode); 281 if (mode == B_READ_ONLY && status == B_ENTRY_NOT_FOUND) { 282 if (find_directory(B_COMMON_SETTINGS_DIRECTORY, &path) == B_OK) { 283 path.Append(kSettingsFile); 284 status = file.SetTo(path.Path(), mode); 285 } 286 } 287 288 return status; 289 } 290 291 292 void 293 WorkspacesSettings::UpdateFramesForScreen(BRect newScreenFrame) 294 { 295 // don't change the position if the screen frame hasn't changed 296 if (newScreenFrame == fScreenFrame) 297 return; 298 299 // adjust horizontal position 300 if (fWindowFrame.right > fScreenFrame.right / 2) { 301 fWindowFrame.OffsetTo(newScreenFrame.right 302 - (fScreenFrame.right - fWindowFrame.left), fWindowFrame.top); 303 } 304 305 // adjust vertical position 306 if (fWindowFrame.bottom > fScreenFrame.bottom / 2) { 307 fWindowFrame.OffsetTo(fWindowFrame.left, 308 newScreenFrame.bottom - (fScreenFrame.bottom - fWindowFrame.top)); 309 } 310 311 fScreenFrame = newScreenFrame; 312 } 313 314 315 void 316 WorkspacesSettings::UpdateScreenFrame() 317 { 318 BScreen screen; 319 fScreenFrame = screen.Frame(); 320 } 321 322 323 void 324 WorkspacesSettings::SetWindowFrame(BRect frame) 325 { 326 fWindowFrame = frame; 327 } 328 329 330 // #pragma mark - 331 332 333 WorkspacesView::WorkspacesView(BRect frame) 334 : 335 BView(frame, "workspaces", B_FOLLOW_ALL, 336 kWorkspacesViewFlag | B_FRAME_EVENTS), 337 fParentWhichDrawsOnChildren(NULL), 338 fCurrentFrame(frame) 339 { 340 frame.OffsetTo(B_ORIGIN); 341 frame.top = frame.bottom - 7; 342 frame.left = frame.right - 7; 343 BDragger* dragger = new BDragger(frame, this, 344 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 345 AddChild(dragger); 346 } 347 348 349 WorkspacesView::WorkspacesView(BMessage* archive) 350 : 351 BView(archive), 352 fParentWhichDrawsOnChildren(NULL), 353 fCurrentFrame(Frame()) 354 { 355 // Just in case we are instantiated from an older archive... 356 SetFlags(Flags() | B_FRAME_EVENTS); 357 // Make sure the auto-raise feature didn't leave any artifacts - this is 358 // not a good idea to keep enabled for a replicant. 359 if (EventMask() != 0) 360 SetEventMask(0); 361 } 362 363 364 WorkspacesView::~WorkspacesView() 365 { 366 } 367 368 369 /*static*/ WorkspacesView* 370 WorkspacesView::Instantiate(BMessage* archive) 371 { 372 if (!validate_instantiation(archive, "WorkspacesView")) 373 return NULL; 374 375 return new WorkspacesView(archive); 376 } 377 378 379 status_t 380 WorkspacesView::Archive(BMessage* archive, bool deep) const 381 { 382 status_t status = BView::Archive(archive, deep); 383 if (status == B_OK) 384 status = archive->AddString("add_on", kSignature); 385 if (status == B_OK) 386 status = archive->AddString("class", "WorkspacesView"); 387 388 return status; 389 } 390 391 392 void 393 WorkspacesView::_AboutRequested() 394 { 395 BString text = B_TRANSLATE("Workspaces\n" 396 "written by %1, and %2.\n\n" 397 "Copyright %3, Haiku.\n\n" 398 "Send windows behind using the Option key. " 399 "Move windows to front using the Control key.\n"); 400 text.ReplaceFirst("%1", "François Revol, Axel Dörfler"); 401 text.ReplaceFirst("%2", "Matt Madia"); 402 text.ReplaceFirst("%3", "2002-2008"); 403 404 BAlert *alert = new BAlert("about", text.String(), B_TRANSLATE("OK")); 405 BTextView *view = alert->TextView(); 406 BFont font; 407 408 view->SetStylable(true); 409 410 view->GetFont(&font); 411 font.SetSize(18); 412 font.SetFace(B_BOLD_FACE); 413 view->SetFontAndColor(0, 10, &font); 414 415 alert->Go(); 416 } 417 418 419 void 420 WorkspacesView::AttachedToWindow() 421 { 422 BView* parent = Parent(); 423 if (parent != NULL && (parent->Flags() & B_DRAW_ON_CHILDREN) != 0) { 424 fParentWhichDrawsOnChildren = parent; 425 _ExcludeFromParentClipping(); 426 } 427 } 428 429 430 void 431 WorkspacesView::DetachedFromWindow() 432 { 433 if (fParentWhichDrawsOnChildren != NULL) 434 _CleanupParentClipping(); 435 } 436 437 438 void 439 WorkspacesView::FrameMoved(BPoint newPosition) 440 { 441 _UpdateParentClipping(); 442 } 443 444 445 void 446 WorkspacesView::FrameResized(float newWidth, float newHeight) 447 { 448 _UpdateParentClipping(); 449 } 450 451 452 void 453 WorkspacesView::_UpdateParentClipping() 454 { 455 if (fParentWhichDrawsOnChildren != NULL) { 456 _CleanupParentClipping(); 457 _ExcludeFromParentClipping(); 458 fParentWhichDrawsOnChildren->Invalidate(fCurrentFrame); 459 fCurrentFrame = Frame(); 460 } 461 } 462 463 464 void 465 WorkspacesView::_ExcludeFromParentClipping() 466 { 467 // Prevent the parent view to draw over us. Do so in a way that allows 468 // restoring the parent to the previous state. 469 fParentWhichDrawsOnChildren->PushState(); 470 471 BRegion clipping(fParentWhichDrawsOnChildren->Bounds()); 472 clipping.Exclude(Frame()); 473 fParentWhichDrawsOnChildren->ConstrainClippingRegion(&clipping); 474 } 475 476 477 void 478 WorkspacesView::_CleanupParentClipping() 479 { 480 // Restore the previous parent state. NOTE: This relies on views 481 // being detached in exactly the opposite order as them being 482 // attached. Otherwise we would mess up states if a sibbling view did 483 // the same thing we did in AttachedToWindow()... 484 fParentWhichDrawsOnChildren->PopState(); 485 } 486 487 488 void 489 WorkspacesView::MessageReceived(BMessage* message) 490 { 491 switch (message->what) { 492 case B_ABOUT_REQUESTED: 493 _AboutRequested(); 494 break; 495 496 case kMsgChangeCount: 497 be_roster->Launch("application/x-vnd.Be-SCRN"); 498 break; 499 500 default: 501 BView::MessageReceived(message); 502 break; 503 } 504 } 505 506 507 void 508 WorkspacesView::MouseMoved(BPoint where, uint32 transit, 509 const BMessage* dragMessage) 510 { 511 WorkspacesWindow* window = dynamic_cast<WorkspacesWindow*>(Window()); 512 if (window == NULL || !window->IsAutoRaising()) 513 return; 514 515 // Auto-Raise 516 517 where = ConvertToScreen(where); 518 BScreen screen(window); 519 BRect frame = screen.Frame(); 520 if (where.x == frame.left || where.x == frame.right 521 || where.y == frame.top || where.y == frame.bottom) { 522 // cursor is on screen edge 523 if (window->Frame().Contains(where)) 524 window->Activate(); 525 } 526 } 527 528 529 void 530 WorkspacesView::MouseDown(BPoint where) 531 { 532 // With enabled auto-raise feature, we'll get mouse messages we don't 533 // want to handle here. 534 if (!Bounds().Contains(where)) 535 return; 536 537 int32 buttons = 0; 538 if (Window() != NULL && Window()->CurrentMessage() != NULL) 539 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 540 541 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 542 return; 543 544 // open context menu 545 546 BPopUpMenu *menu = new BPopUpMenu(B_EMPTY_STRING, false, false); 547 menu->SetFont(be_plain_font); 548 549 // TODO: alternatively change the count here directly? 550 BMenuItem* changeItem = new BMenuItem(B_TRANSLATE("Change workspace count" 551 B_UTF8_ELLIPSIS), new BMessage(kMsgChangeCount)); 552 menu->AddItem(changeItem); 553 554 WorkspacesWindow* window = dynamic_cast<WorkspacesWindow*>(Window()); 555 if (window != NULL) { 556 BMenuItem* item; 557 558 menu->AddSeparatorItem(); 559 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window title"), 560 new BMessage(kMsgToggleTitle))); 561 if (window->Look() == B_TITLED_WINDOW_LOOK) 562 item->SetMarked(true); 563 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Show window border"), 564 new BMessage(kMsgToggleBorder))); 565 if (window->Look() == B_TITLED_WINDOW_LOOK 566 || window->Look() == B_MODAL_WINDOW_LOOK) 567 item->SetMarked(true); 568 569 menu->AddSeparatorItem(); 570 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Always on top"), 571 new BMessage(kMsgToggleAlwaysOnTop))); 572 if (window->Feel() == B_FLOATING_ALL_WINDOW_FEEL) 573 item->SetMarked(true); 574 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Auto-raise"), 575 new BMessage(kMsgToggleAutoRaise))); 576 if (window->IsAutoRaising()) 577 item->SetMarked(true); 578 579 menu->AddSeparatorItem(); 580 menu->AddItem(new BMenuItem(B_TRANSLATE("About Workspaces" 581 B_UTF8_ELLIPSIS), new BMessage(B_ABOUT_REQUESTED))); 582 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 583 new BMessage(B_QUIT_REQUESTED))); 584 menu->SetTargetForItems(window); 585 } 586 587 changeItem->SetTarget(this); 588 ConvertToScreen(&where); 589 menu->Go(where, true, true, true); 590 } 591 592 593 // #pragma mark - 594 595 596 WorkspacesWindow::WorkspacesWindow(WorkspacesSettings *settings) 597 : BWindow(settings->WindowFrame(), B_TRANSLATE("Workspaces"), 598 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_AVOID_FRONT 599 | B_WILL_ACCEPT_FIRST_CLICK, B_ALL_WORKSPACES), 600 fSettings(settings), 601 fAutoRaising(false) 602 { 603 AddChild(new WorkspacesView(Bounds())); 604 605 if (!fSettings->HasBorder()) 606 SetLook(B_NO_BORDER_WINDOW_LOOK); 607 else if (!fSettings->HasTitle()) 608 SetLook(B_MODAL_WINDOW_LOOK); 609 610 if (fSettings->AlwaysOnTop()) 611 SetFeel(B_FLOATING_ALL_WINDOW_FEEL); 612 else 613 SetAutoRaise(fSettings->AutoRaising()); 614 } 615 616 617 WorkspacesWindow::~WorkspacesWindow() 618 { 619 delete fSettings; 620 } 621 622 623 void 624 WorkspacesWindow::ScreenChanged(BRect rect, color_space mode) 625 { 626 fSettings->UpdateFramesForScreen(rect); 627 MoveTo(fSettings->WindowFrame().LeftTop()); 628 } 629 630 631 void 632 WorkspacesWindow::FrameMoved(BPoint origin) 633 { 634 fSettings->SetWindowFrame(Frame()); 635 } 636 637 638 void 639 WorkspacesWindow::FrameResized(float width, float height) 640 { 641 if (!modifiers() & B_SHIFT_KEY) { 642 BWindow::FrameResized(width, height); 643 return; 644 } 645 646 uint32 columns, rows; 647 BPrivate::get_workspaces_layout(&columns, &rows); 648 649 BScreen screen; 650 float screenWidth = screen.Frame().Width(); 651 float screenHeight = screen.Frame().Height(); 652 653 float windowAspectRatio 654 = (columns * screenWidth) / (rows * screenHeight); 655 656 float newHeight = width / windowAspectRatio; 657 658 if (height != newHeight) 659 ResizeTo(width, newHeight); 660 661 fSettings->SetWindowFrame(Frame()); 662 } 663 664 665 void 666 WorkspacesWindow::Zoom(BPoint origin, float width, float height) 667 { 668 BScreen screen; 669 float screenWidth = screen.Frame().Width(); 670 float screenHeight = screen.Frame().Height(); 671 float aspectRatio = screenWidth / screenHeight; 672 673 uint32 columns, rows; 674 BPrivate::get_workspaces_layout(&columns, &rows); 675 676 float workspaceWidth = screenWidth / 10; 677 float workspaceHeight = workspaceWidth / aspectRatio; 678 679 width = floor(workspaceWidth * columns); 680 height = floor(workspaceHeight * rows); 681 682 float tabHeight = Frame().top - DecoratorFrame().top; 683 684 while (width + 2 * kScreenBorderOffset > screenWidth 685 || height + 2 * kScreenBorderOffset + tabHeight > screenHeight) { 686 width = floor(0.95 * width); 687 height = floor(0.95 * height); 688 } 689 690 ResizeTo(width, height); 691 692 origin = screen.Frame().RightBottom(); 693 origin.x -= kScreenBorderOffset + width; 694 origin.y -= kScreenBorderOffset + height; 695 696 MoveTo(origin); 697 } 698 699 700 void 701 WorkspacesWindow::MessageReceived(BMessage *message) 702 { 703 switch (message->what) { 704 case B_SIMPLE_DATA: 705 { 706 // Drop from Tracker 707 entry_ref ref; 708 for (int i = 0; (message->FindRef("refs", i, &ref) == B_OK); i++) 709 be_roster->Launch(&ref); 710 break; 711 } 712 713 case B_ABOUT_REQUESTED: 714 PostMessage(message, ChildAt(0)); 715 break; 716 717 case kMsgToggleBorder: 718 { 719 bool enable = false; 720 if (Look() == B_NO_BORDER_WINDOW_LOOK) 721 enable = true; 722 723 if (enable) 724 if (fSettings->HasTitle()) 725 SetLook(B_TITLED_WINDOW_LOOK); 726 else 727 SetLook(B_MODAL_WINDOW_LOOK); 728 else 729 SetLook(B_NO_BORDER_WINDOW_LOOK); 730 731 fSettings->SetHasBorder(enable); 732 break; 733 } 734 735 case kMsgToggleTitle: 736 { 737 bool enable = false; 738 if (Look() == B_MODAL_WINDOW_LOOK 739 || Look() == B_NO_BORDER_WINDOW_LOOK) 740 enable = true; 741 742 if (enable) 743 SetLook(B_TITLED_WINDOW_LOOK); 744 else 745 SetLook(B_MODAL_WINDOW_LOOK); 746 747 // No matter what the setting for title, 748 // we must force the border on 749 fSettings->SetHasBorder(true); 750 fSettings->SetHasTitle(enable); 751 break; 752 } 753 754 case kMsgToggleAutoRaise: 755 SetAutoRaise(!IsAutoRaising()); 756 SetFeel(B_NORMAL_WINDOW_FEEL); 757 break; 758 759 case kMsgToggleAlwaysOnTop: 760 { 761 bool enable = false; 762 if (Feel() != B_FLOATING_ALL_WINDOW_FEEL) 763 enable = true; 764 765 if (enable) 766 SetFeel(B_FLOATING_ALL_WINDOW_FEEL); 767 else 768 SetFeel(B_NORMAL_WINDOW_FEEL); 769 770 fSettings->SetAlwaysOnTop(enable); 771 break; 772 } 773 774 default: 775 BWindow::MessageReceived(message); 776 break; 777 } 778 } 779 780 781 bool 782 WorkspacesWindow::QuitRequested() 783 { 784 be_app->PostMessage(B_QUIT_REQUESTED); 785 return true; 786 } 787 788 789 void 790 WorkspacesWindow::SetAutoRaise(bool enable) 791 { 792 if (enable == fAutoRaising) 793 return; 794 795 fAutoRaising = enable; 796 fSettings->SetAutoRaising(enable); 797 798 if (enable) 799 ChildAt(0)->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 800 else 801 ChildAt(0)->SetEventMask(0); 802 } 803 804 805 // #pragma mark - 806 807 808 WorkspacesApp::WorkspacesApp() 809 : BApplication(kSignature) 810 { 811 fWindow = new WorkspacesWindow(new WorkspacesSettings()); 812 } 813 814 815 WorkspacesApp::~WorkspacesApp() 816 { 817 } 818 819 820 void 821 WorkspacesApp::AboutRequested() 822 { 823 fWindow->PostMessage(B_ABOUT_REQUESTED); 824 } 825 826 827 void 828 WorkspacesApp::Usage(const char *programName) 829 { 830 printf(B_TRANSLATE("Usage: %s [options] [workspace]\n" 831 "where \"options\" is one of:\n" 832 " --notitle\t\ttitle bar removed. border and resize kept.\n" 833 " --noborder\t\ttitle, border, and resize removed.\n" 834 " --avoidfocus\t\tprevents the window from being the target of " 835 "keyboard events.\n" 836 " --alwaysontop\t\tkeeps window on top\n" 837 " --notmovable\t\twindow can't be moved around\n" 838 " --autoraise\t\tauto-raise the workspace window when it's at the " 839 "screen corner\n" 840 " --help\t\tdisplay this help and exit\n" 841 "and \"workspace\" is the number of the Workspace to which to switch " 842 "(0-31)\n"), 843 programName); 844 845 // quit only if we aren't running already 846 if (IsLaunching()) 847 Quit(); 848 } 849 850 851 void 852 WorkspacesApp::ArgvReceived(int32 argc, char **argv) 853 { 854 for (int i = 1; i < argc; i++) { 855 if (argv[i][0] == '-' && argv[i][1] == '-') { 856 // evaluate --arguments 857 if (!strcmp(argv[i], "--notitle")) 858 fWindow->SetLook(B_MODAL_WINDOW_LOOK); 859 else if (!strcmp(argv[i], "--noborder")) 860 fWindow->SetLook(B_NO_BORDER_WINDOW_LOOK); 861 else if (!strcmp(argv[i], "--avoidfocus")) 862 fWindow->SetFlags(fWindow->Flags() | B_AVOID_FOCUS); 863 else if (!strcmp(argv[i], "--notmovable")) 864 fWindow->SetFlags(fWindow->Flags() | B_NOT_MOVABLE); 865 else if (!strcmp(argv[i], "--alwaysontop")) 866 fWindow->SetFeel(B_FLOATING_ALL_WINDOW_FEEL); 867 else if (!strcmp(argv[i], "--autoraise")) 868 fWindow->SetAutoRaise(true); 869 else { 870 const char *programName = strrchr(argv[0], '/'); 871 programName = programName ? programName + 1 : argv[0]; 872 873 Usage(programName); 874 } 875 } else if (isdigit(*argv[i])) { 876 // check for a numeric arg, if not already given 877 activate_workspace(atoi(argv[i])); 878 879 // if the app is running, don't quit 880 // but if it isn't, cancel the complete run, so it doesn't 881 // open any window 882 if (IsLaunching()) 883 Quit(); 884 } else if (!strcmp(argv[i], "-")) { 885 activate_workspace(current_workspace() - 1); 886 887 if (IsLaunching()) 888 Quit(); 889 } else if (!strcmp(argv[i], "+")) { 890 activate_workspace(current_workspace() + 1); 891 892 if (IsLaunching()) 893 Quit(); 894 } else { 895 // some unknown arguments were specified 896 fprintf(stderr, B_TRANSLATE("Invalid argument: %s\n"), argv[i]); 897 898 if (IsLaunching()) 899 Quit(); 900 } 901 } 902 } 903 904 905 void 906 WorkspacesApp::ReadyToRun() 907 { 908 fWindow->Show(); 909 } 910 911 912 // #pragma mark - 913 914 915 int 916 main(int argc, char **argv) 917 { 918 WorkspacesApp app; 919 app.Run(); 920 921 return 0; 922 } 923