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