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* kScreenPrefletSignature = "application/x-vnd.Haiku-Screen"; 45 static const char* kOldSettingFile = "Workspace_data"; 46 static const char* kSettingsFile = "Workspaces_settings"; 47 48 static const uint32 kMsgChangeCount = 'chWC'; 49 static const uint32 kMsgToggleTitle = 'tgTt'; 50 static const uint32 kMsgToggleBorder = 'tgBd'; 51 static const uint32 kMsgToggleAutoRaise = 'tgAR'; 52 static const uint32 kMsgToggleAlwaysOnTop = 'tgAT'; 53 54 static const float kScreenBorderOffset = 10.0; 55 56 57 class WorkspacesSettings { 58 public: 59 WorkspacesSettings(); 60 virtual ~WorkspacesSettings(); 61 62 BRect WindowFrame() const { return fWindowFrame; } 63 BRect ScreenFrame() const { return fScreenFrame; } 64 65 bool AutoRaising() const { return fAutoRaising; } 66 bool AlwaysOnTop() const { return fAlwaysOnTop; } 67 bool HasTitle() const { return fHasTitle; } 68 bool HasBorder() const { return fHasBorder; } 69 70 void UpdateFramesForScreen(BRect screenFrame); 71 void UpdateScreenFrame(); 72 73 void SetWindowFrame(BRect); 74 void SetAutoRaising(bool enable) { fAutoRaising = enable; } 75 void SetAlwaysOnTop(bool enable) { fAlwaysOnTop = enable; } 76 void SetHasTitle(bool enable) { fHasTitle = enable; } 77 void SetHasBorder(bool enable) { fHasBorder = enable; } 78 79 private: 80 status_t _Open(BFile& file, int mode); 81 82 BRect fWindowFrame; 83 BRect fScreenFrame; 84 bool fAutoRaising; 85 bool fAlwaysOnTop; 86 bool fHasTitle; 87 bool fHasBorder; 88 }; 89 90 class WorkspacesView : public BView { 91 public: 92 WorkspacesView(BRect frame); 93 WorkspacesView(BMessage* archive); 94 ~WorkspacesView(); 95 96 static WorkspacesView* Instantiate(BMessage* archive); 97 virtual status_t Archive(BMessage* archive, bool deep = true) const; 98 99 virtual void AttachedToWindow(); 100 virtual void DetachedFromWindow(); 101 virtual void FrameMoved(BPoint newPosition); 102 virtual void FrameResized(float newWidth, float newHeight); 103 virtual void MessageReceived(BMessage* message); 104 virtual void MouseMoved(BPoint where, uint32 transit, 105 const BMessage* dragMessage); 106 virtual void MouseDown(BPoint where); 107 108 private: 109 void _AboutRequested(); 110 111 void _UpdateParentClipping(); 112 void _ExcludeFromParentClipping(); 113 void _CleanupParentClipping(); 114 115 BView* fParentWhichDrawsOnChildren; 116 BRect fCurrentFrame; 117 }; 118 119 class WorkspacesWindow : public BWindow { 120 public: 121 WorkspacesWindow(WorkspacesSettings *settings); 122 virtual ~WorkspacesWindow(); 123 124 virtual void ScreenChanged(BRect frame, color_space mode); 125 virtual void FrameMoved(BPoint origin); 126 virtual void FrameResized(float width, float height); 127 virtual void Zoom(BPoint origin, float width, float height); 128 129 virtual void MessageReceived(BMessage *msg); 130 virtual bool QuitRequested(); 131 132 void SetAutoRaise(bool enable); 133 bool IsAutoRaising() const { return fAutoRaising; } 134 135 private: 136 WorkspacesSettings *fSettings; 137 bool fAutoRaising; 138 }; 139 140 class WorkspacesApp : public BApplication { 141 public: 142 WorkspacesApp(); 143 virtual ~WorkspacesApp(); 144 145 virtual void AboutRequested(); 146 virtual void ArgvReceived(int32 argc, char **argv); 147 virtual void ReadyToRun(); 148 149 void Usage(const char *programName); 150 151 private: 152 WorkspacesWindow* fWindow; 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(kScreenPrefletSignature); 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 tab"), 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 : 599 BWindow(settings->WindowFrame(), B_TRANSLATE_SYSTEM_NAME("Workspaces"), 600 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 601 B_AVOID_FRONT | B_WILL_ACCEPT_FIRST_CLICK, B_ALL_WORKSPACES), 602 fSettings(settings), 603 fAutoRaising(false) 604 { 605 AddChild(new WorkspacesView(Bounds())); 606 607 if (!fSettings->HasBorder()) 608 SetLook(B_NO_BORDER_WINDOW_LOOK); 609 else if (!fSettings->HasTitle()) 610 SetLook(B_MODAL_WINDOW_LOOK); 611 612 if (fSettings->AlwaysOnTop()) 613 SetFeel(B_FLOATING_ALL_WINDOW_FEEL); 614 else 615 SetAutoRaise(fSettings->AutoRaising()); 616 } 617 618 619 WorkspacesWindow::~WorkspacesWindow() 620 { 621 delete fSettings; 622 } 623 624 625 void 626 WorkspacesWindow::ScreenChanged(BRect rect, color_space mode) 627 { 628 fSettings->UpdateFramesForScreen(rect); 629 MoveTo(fSettings->WindowFrame().LeftTop()); 630 } 631 632 633 void 634 WorkspacesWindow::FrameMoved(BPoint origin) 635 { 636 fSettings->SetWindowFrame(Frame()); 637 } 638 639 640 void 641 WorkspacesWindow::FrameResized(float width, float height) 642 { 643 if (!modifiers() & B_SHIFT_KEY) { 644 BWindow::FrameResized(width, height); 645 return; 646 } 647 648 uint32 columns, rows; 649 BPrivate::get_workspaces_layout(&columns, &rows); 650 651 BScreen screen; 652 float screenWidth = screen.Frame().Width(); 653 float screenHeight = screen.Frame().Height(); 654 655 float windowAspectRatio 656 = (columns * screenWidth) / (rows * screenHeight); 657 658 float newHeight = width / windowAspectRatio; 659 660 if (height != newHeight) 661 ResizeTo(width, newHeight); 662 663 fSettings->SetWindowFrame(Frame()); 664 } 665 666 667 void 668 WorkspacesWindow::Zoom(BPoint origin, float width, float height) 669 { 670 BScreen screen; 671 float screenWidth = screen.Frame().Width(); 672 float screenHeight = screen.Frame().Height(); 673 float aspectRatio = screenWidth / screenHeight; 674 675 uint32 columns, rows; 676 BPrivate::get_workspaces_layout(&columns, &rows); 677 678 float workspaceWidth = screenWidth / 10; 679 float workspaceHeight = workspaceWidth / aspectRatio; 680 681 width = floor(workspaceWidth * columns); 682 height = floor(workspaceHeight * rows); 683 684 float tabHeight = Frame().top - DecoratorFrame().top; 685 686 while (width + 2 * kScreenBorderOffset > screenWidth 687 || height + 2 * kScreenBorderOffset + tabHeight > screenHeight) { 688 width = floor(0.95 * width); 689 height = floor(0.95 * height); 690 } 691 692 ResizeTo(width, height); 693 694 origin = screen.Frame().RightBottom(); 695 origin.x -= kScreenBorderOffset + width; 696 origin.y -= kScreenBorderOffset + height; 697 698 MoveTo(origin); 699 } 700 701 702 void 703 WorkspacesWindow::MessageReceived(BMessage *message) 704 { 705 switch (message->what) { 706 case B_SIMPLE_DATA: 707 { 708 // Drop from Tracker 709 entry_ref ref; 710 for (int i = 0; (message->FindRef("refs", i, &ref) == B_OK); i++) 711 be_roster->Launch(&ref); 712 break; 713 } 714 715 case B_ABOUT_REQUESTED: 716 PostMessage(message, ChildAt(0)); 717 break; 718 719 case kMsgToggleBorder: 720 { 721 bool enable = false; 722 if (Look() == B_NO_BORDER_WINDOW_LOOK) 723 enable = true; 724 725 if (enable) 726 if (fSettings->HasTitle()) 727 SetLook(B_TITLED_WINDOW_LOOK); 728 else 729 SetLook(B_MODAL_WINDOW_LOOK); 730 else 731 SetLook(B_NO_BORDER_WINDOW_LOOK); 732 733 fSettings->SetHasBorder(enable); 734 break; 735 } 736 737 case kMsgToggleTitle: 738 { 739 bool enable = false; 740 if (Look() == B_MODAL_WINDOW_LOOK 741 || Look() == B_NO_BORDER_WINDOW_LOOK) 742 enable = true; 743 744 if (enable) 745 SetLook(B_TITLED_WINDOW_LOOK); 746 else 747 SetLook(B_MODAL_WINDOW_LOOK); 748 749 // No matter what the setting for title, 750 // we must force the border on 751 fSettings->SetHasBorder(true); 752 fSettings->SetHasTitle(enable); 753 break; 754 } 755 756 case kMsgToggleAutoRaise: 757 SetAutoRaise(!IsAutoRaising()); 758 SetFeel(B_NORMAL_WINDOW_FEEL); 759 break; 760 761 case kMsgToggleAlwaysOnTop: 762 { 763 bool enable = false; 764 if (Feel() != B_FLOATING_ALL_WINDOW_FEEL) 765 enable = true; 766 767 if (enable) 768 SetFeel(B_FLOATING_ALL_WINDOW_FEEL); 769 else 770 SetFeel(B_NORMAL_WINDOW_FEEL); 771 772 fSettings->SetAlwaysOnTop(enable); 773 break; 774 } 775 776 default: 777 BWindow::MessageReceived(message); 778 break; 779 } 780 } 781 782 783 bool 784 WorkspacesWindow::QuitRequested() 785 { 786 be_app->PostMessage(B_QUIT_REQUESTED); 787 return true; 788 } 789 790 791 void 792 WorkspacesWindow::SetAutoRaise(bool enable) 793 { 794 if (enable == fAutoRaising) 795 return; 796 797 fAutoRaising = enable; 798 fSettings->SetAutoRaising(enable); 799 800 if (enable) 801 ChildAt(0)->SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 802 else 803 ChildAt(0)->SetEventMask(0); 804 } 805 806 807 // #pragma mark - 808 809 810 WorkspacesApp::WorkspacesApp() 811 : BApplication(kSignature) 812 { 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