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