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