1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include "tracker_private.h" 37 #include "BarApp.h" 38 #include "Switcher.h" 39 #include "ResourceSet.h" 40 #include "WindowMenuItem.h" 41 #include "icons.h" 42 43 #include <Bitmap.h> 44 #include <Debug.h> 45 #include <Font.h> 46 #include <Mime.h> 47 #include <Node.h> 48 #include <NodeInfo.h> 49 #include <Roster.h> 50 #include <Screen.h> 51 #include <String.h> 52 53 #include <string.h> 54 #include <stdlib.h> 55 #include <float.h> 56 57 58 #define _ALLOW_STICKY_ 0 59 // allows you to press 's' to keep the switcher window on screen 60 61 62 static const color_space kIconFormat = B_RGBA32; 63 64 65 class TTeamGroup { 66 public: 67 TTeamGroup(); 68 TTeamGroup(BList* teams, uint32 flags, char* name, 69 const char* signature); 70 virtual ~TTeamGroup(); 71 72 void Draw(BView* view, BRect bounds, bool main); 73 74 BList* TeamList() const 75 { return fTeams; } 76 const char* Name() const 77 { return fName; } 78 const char* Signature() const 79 { return fSignature; } 80 uint32 Flags() const 81 { return fFlags; } 82 const BBitmap* SmallIcon() const 83 { return fSmallIcon; } 84 const BBitmap* LargeIcon() const 85 { return fLargeIcon; } 86 87 private: 88 BList* fTeams; 89 uint32 fFlags; 90 char fSignature[B_MIME_TYPE_LENGTH]; 91 char* fName; 92 BBitmap* fSmallIcon; 93 BBitmap* fLargeIcon; 94 }; 95 96 class TSwitcherWindow : public BWindow { 97 public: 98 TSwitcherWindow(BRect frame, 99 TSwitchManager* manager); 100 virtual ~TSwitcherWindow(); 101 102 virtual bool QuitRequested(); 103 virtual void MessageReceived(BMessage* message); 104 virtual void Show(); 105 virtual void Hide(); 106 virtual void WindowActivated(bool state); 107 108 void DoKey(uint32 key, uint32 modifiers); 109 TIconView* IconView(); 110 TWindowView* WindowView(); 111 TBox* TopView(); 112 bool HairTrigger(); 113 void Update(int32 previous, int32 current, 114 int32 prevSlot, int32 currentSlot, 115 bool forward); 116 int32 SlotOf(int32); 117 void Redraw(int32 index); 118 119 private: 120 TSwitchManager* fManager; 121 TIconView* fIconView; 122 TBox* fTopView; 123 TWindowView* fWindowView; 124 bool fHairTrigger; 125 bool fSkipKeyRepeats; 126 }; 127 128 class TWindowView : public BView { 129 public: 130 TWindowView(BRect frame, TSwitchManager* manager, 131 TSwitcherWindow* switcher); 132 133 void UpdateGroup(int32 groupIndex, int32 windowIndex); 134 135 virtual void AttachedToWindow(); 136 virtual void Draw(BRect update); 137 virtual void Pulse(); 138 virtual void GetPreferredSize(float* w, float* h); 139 void ScrollTo(float x, float y) 140 { ScrollTo(BPoint(x,y)); } 141 virtual void ScrollTo(BPoint where); 142 143 void ShowIndex(int32 windex); 144 BRect FrameOf(int32 index) const; 145 146 private: 147 int32 fCurrentToken; 148 float fItemHeight; 149 TSwitcherWindow* fSwitcher; 150 TSwitchManager* fManager; 151 bool fLocal; 152 }; 153 154 class TIconView : public BView { 155 public: 156 TIconView(BRect frame, TSwitchManager* manager, 157 TSwitcherWindow* switcher); 158 virtual ~TIconView(); 159 160 void Showing(); 161 void Hiding(); 162 163 virtual void KeyDown(const char* bytes, int32 numBytes); 164 virtual void Pulse(); 165 virtual void MouseDown(BPoint point); 166 virtual void Draw(BRect updateRect); 167 168 void ScrollTo(float x, float y) 169 { ScrollTo(BPoint(x,y)); } 170 virtual void ScrollTo(BPoint where); 171 void Update(int32 previous, int32 current, 172 int32 previousSlot, int32 currentSlot, 173 bool forward); 174 void DrawTeams(BRect update); 175 int32 SlotOf(int32) const; 176 BRect FrameOf(int32) const; 177 int32 ItemAtPoint(BPoint) const; 178 int32 IndexAt(int32 slot) const; 179 void CenterOn(int32 index); 180 181 private: 182 void CacheIcons(TTeamGroup* group); 183 void AnimateIcon(BBitmap* startIcon, BBitmap* endIcon); 184 185 bool fAutoScrolling; 186 bool fCapsState; 187 TSwitcherWindow* fSwitcher; 188 TSwitchManager* fManager; 189 BBitmap* fOffBitmap; 190 BView* fOffView; 191 BBitmap* fCurrentSmall; 192 BBitmap* fCurrentLarge; 193 }; 194 195 class TBox : public BBox { 196 public: 197 TBox(BRect bounds, TSwitchManager* manager, 198 TSwitcherWindow* window, TIconView* iconView); 199 200 virtual void Draw(BRect update); 201 virtual void AllAttached(); 202 virtual void DrawIconScrollers(bool force); 203 virtual void DrawWindowScrollers(bool force); 204 virtual void MouseDown(BPoint where); 205 206 private: 207 TSwitchManager* fManager; 208 TSwitcherWindow* fWindow; 209 TIconView* fIconView; 210 BRect fCenter; 211 bool fLeftScroller; 212 bool fRightScroller; 213 bool fUpScroller; 214 bool fDownScroller; 215 }; 216 217 218 const int32 kHorizontalMargin = 11; 219 const int32 kVerticalMargin = 10; 220 221 // SLOT_SIZE must be divisible by 4. That's because of the scrolling 222 // animation. If this needs to change then look at TIconView::Update() 223 224 const int32 kSlotSize = 36; 225 const int32 kScrollStep = kSlotSize / 2; 226 const int32 kNumSlots = 7; 227 const int32 kCenterSlot = 3; 228 229 const int32 kWindowScrollSteps = 3; 230 231 232 // #pragma mark - 233 234 235 static int32 236 LowBitIndex(uint32 value) 237 { 238 int32 result = 0; 239 int32 bitMask = 1; 240 241 if (value == 0) 242 return -1; 243 244 while (result < 32 && (value & bitMask) == 0) { 245 result++; 246 bitMask = bitMask << 1; 247 } 248 return result; 249 } 250 251 252 inline bool 253 IsVisibleInCurrentWorkspace(const window_info* windowInfo) 254 { 255 /* 256 The window list is always ordered from the top 257 front visible window (the first on the list), going down through all 258 the other visible windows, then all the hidden or non workspace 259 visible window at the end. 260 261 layer > 2 : normal visible window. 262 layer == 2 : reserved for the desktop window (visible also). 263 layer < 2 : hidden (0) and non workspace visible window (1) 264 */ 265 return windowInfo->layer > 2; 266 } 267 268 269 bool 270 IsKeyDown(int32 key) 271 { 272 key_info keyInfo; 273 274 get_key_info(&keyInfo); 275 return (keyInfo.key_states[key >> 3] & (1 << ((7 - key) & 7))) != 0; 276 } 277 278 279 bool 280 IsWindowOK(const window_info* windowInfo) 281 { 282 // is_mini (true means that the window is minimized). 283 // if not, then 284 // show_hide >= 1 means that the window is hidden. 285 // 286 // If the window is both minimized and hidden, then you get : 287 // TWindow->is_mini = false; 288 // TWindow->was_mini = true; 289 // TWindow->show_hide >= 1; 290 291 if (windowInfo->feel != _STD_W_TYPE_) 292 return false; 293 294 if (windowInfo->is_mini) 295 return true; 296 297 return windowInfo->show_hide_level <= 0; 298 } 299 300 301 bool 302 OKToUse(const TTeamGroup* teamGroup) 303 { 304 if (!teamGroup) 305 return false; 306 307 // skip background applications 308 if ((teamGroup->Flags() & B_BACKGROUND_APP) != 0) 309 return false; 310 311 // skip the Deakbar itself 312 if (strcasecmp(teamGroup->Signature(), kDeskbarSignature) == 0) 313 return false; 314 315 return true; 316 } 317 318 319 int 320 SmartStrcmp(const char* s1, const char* s2) 321 { 322 if (strcasecmp(s1, s2) == 0) 323 return 0; 324 325 // if the strings on differ in spaces or underscores they still match 326 while (*s1 && *s2) { 327 if ((*s1 == ' ') || (*s1 == '_')) { 328 s1++; 329 continue; 330 } 331 if ((*s2 == ' ') || (*s2 == '_')) { 332 s2++; 333 continue; 334 } 335 if (*s1 != *s2) 336 return 1; // they differ 337 s1++; 338 s2++; 339 } 340 341 // if one of the strings ended before the other 342 // ??? could process trailing spaces & underscores! 343 if (*s1) 344 return 1; 345 if (*s2) 346 return 1; 347 348 return 0; 349 } 350 351 352 // #pragma mark - 353 354 355 TTeamGroup::TTeamGroup() 356 : 357 fTeams(NULL), 358 fFlags(0), 359 fName(NULL), 360 fSmallIcon(NULL), 361 fLargeIcon(NULL) 362 { 363 fSignature[0] = '\0'; 364 } 365 366 367 TTeamGroup::TTeamGroup(BList* teams, uint32 flags, char* name, 368 const char* signature) 369 : 370 fTeams(teams), 371 fFlags(flags), 372 fName(name), 373 fSmallIcon(NULL), 374 fLargeIcon(NULL) 375 { 376 strcpy(fSignature, signature); 377 378 fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), kIconFormat); 379 fLargeIcon = new BBitmap(BRect(0, 0, 31, 31), kIconFormat); 380 381 app_info appInfo; 382 if (be_roster->GetAppInfo(signature, &appInfo) == B_OK) { 383 BNode node(&(appInfo.ref)); 384 if (node.InitCheck() == B_OK) { 385 BNodeInfo nodeInfo(&node); 386 if (nodeInfo.InitCheck() == B_OK) { 387 nodeInfo.GetTrackerIcon(fSmallIcon, B_MINI_ICON); 388 nodeInfo.GetTrackerIcon(fLargeIcon, B_LARGE_ICON); 389 } 390 } 391 } 392 } 393 394 395 TTeamGroup::~TTeamGroup() 396 { 397 delete fTeams; 398 free(fName); 399 delete fSmallIcon; 400 delete fLargeIcon; 401 } 402 403 404 void 405 TTeamGroup::Draw(BView* view, BRect bounds, bool main) 406 { 407 BRect rect; 408 if (main) { 409 rect = fLargeIcon->Bounds(); 410 rect.OffsetTo(bounds.LeftTop()); 411 rect.OffsetBy(2, 2); 412 view->DrawBitmap(fLargeIcon, rect); 413 } else { 414 rect = fSmallIcon->Bounds(); 415 rect.OffsetTo(bounds.LeftTop()); 416 rect.OffsetBy(10, 10); 417 view->DrawBitmap(fSmallIcon, rect); 418 } 419 } 420 421 422 // #pragma mark - 423 424 425 TSwitchManager::TSwitchManager(BPoint point) 426 : BHandler("SwitchManager"), 427 fMainMonitor(create_sem(1, "main_monitor")), 428 fBlock(false), 429 fSkipUntil(0), 430 fLastSwitch(0), 431 fQuickSwitchIndex(-1), 432 fQuickSwitchWindow(-1), 433 fGroupList(10), 434 fCurrentIndex(0), 435 fCurrentSlot(0), 436 fWindowID(-1) 437 { 438 BRect rect(point.x, point.y, 439 point.x + (kSlotSize * kNumSlots) - 1 + (2 * kHorizontalMargin), 440 point.y + 82); 441 fWindow = new TSwitcherWindow(rect, this); 442 fWindow->AddHandler(this); 443 444 fWindow->Lock(); 445 fWindow->Run(); 446 447 BList tmpList; 448 TBarApp::Subscribe(BMessenger(this), &tmpList); 449 450 for (int32 i = 0; ; i++) { 451 BarTeamInfo *barTeamInfo = (BarTeamInfo *)tmpList.ItemAt(i); 452 if (!barTeamInfo) 453 break; 454 455 TTeamGroup* tinfo = new TTeamGroup(barTeamInfo->teams, 456 barTeamInfo->flags, barTeamInfo->name, barTeamInfo->sig); 457 fGroupList.AddItem(tinfo); 458 459 barTeamInfo->teams = NULL; 460 barTeamInfo->name = NULL; 461 462 delete barTeamInfo; 463 } 464 fWindow->Unlock(); 465 } 466 467 468 TSwitchManager::~TSwitchManager() 469 { 470 for (int32 i = fGroupList.CountItems(); i-- > 0;) { 471 TTeamGroup* teamInfo = static_cast<TTeamGroup*>(fGroupList.ItemAt(i)); 472 delete teamInfo; 473 } 474 } 475 476 477 void 478 TSwitchManager::MessageReceived(BMessage* message) 479 { 480 switch (message->what) { 481 case B_SOME_APP_QUIT: 482 { 483 // This is only sent when last team of a matching set quits 484 team_id teamID; 485 int i = 0; 486 TTeamGroup* tinfo; 487 message->FindInt32("team", &teamID); 488 while ((tinfo = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) { 489 if (tinfo->TeamList()->HasItem((void*)teamID)) { 490 fGroupList.RemoveItem(i); 491 492 if (OKToUse(tinfo)) { 493 fWindow->Redraw(i); 494 if (i <= fCurrentIndex) { 495 fCurrentIndex--; 496 CycleApp(true); 497 } 498 } 499 delete tinfo; 500 break; 501 } 502 i++; 503 } 504 break; 505 } 506 507 case B_SOME_APP_LAUNCHED: 508 { 509 BList* teams; 510 const char* name; 511 BBitmap* smallIcon; 512 uint32 flags; 513 const char* signature; 514 515 if (message->FindPointer("teams", (void**)&teams) != B_OK) 516 break; 517 518 if (message->FindPointer("icon", (void**)&smallIcon) != B_OK) { 519 delete teams; 520 break; 521 } 522 delete smallIcon; 523 if (message->FindString("sig", &signature) != B_OK) { 524 delete teams; 525 break; 526 } 527 if (message->FindInt32("flags", (int32*)&flags) != B_OK) { 528 delete teams; 529 break; 530 } 531 if (message->FindString("name", &name) != B_OK) { 532 delete teams; 533 break; 534 } 535 536 TTeamGroup* tinfo = new TTeamGroup(teams, flags, strdup(name), 537 signature); 538 539 fGroupList.AddItem(tinfo); 540 if (OKToUse(tinfo)) 541 fWindow->Redraw(fGroupList.CountItems() - 1); 542 543 break; 544 } 545 546 case kAddTeam: 547 { 548 const char* signature = message->FindString("sig"); 549 team_id team = message->FindInt32("team"); 550 551 int32 numItems = fGroupList.CountItems(); 552 for (int32 i = 0; i < numItems; i++) { 553 TTeamGroup* tinfo = (TTeamGroup*)fGroupList.ItemAt(i); 554 if (strcasecmp(tinfo->Signature(), signature) == 0) { 555 if (!(tinfo->TeamList()->HasItem((void*)team))) 556 tinfo->TeamList()->AddItem((void*)team); 557 break; 558 } 559 } 560 break; 561 } 562 563 case kRemoveTeam: 564 { 565 team_id team = message->FindInt32("team"); 566 567 int32 numItems = fGroupList.CountItems(); 568 for (int32 i = 0; i < numItems; i++) { 569 TTeamGroup* tinfo = (TTeamGroup*)fGroupList.ItemAt(i); 570 if (tinfo->TeamList()->HasItem((void*)team)) { 571 tinfo->TeamList()->RemoveItem((void*)team); 572 break; 573 } 574 } 575 break; 576 } 577 578 case 'TASK': 579 { 580 // The first TASK message calls MainEntry. Subsequent ones 581 // call Process(). 582 bigtime_t time; 583 message->FindInt64("when", (int64*)&time); 584 585 // The fSkipUntil stuff can be removed once the new input_server 586 // starts differentiating initial key_downs from KeyDowns generated 587 // by auto-repeat. Until then the fSkipUntil stuff helps, but it 588 // isn't perfect. 589 590 if (time < fSkipUntil) 591 break; 592 593 status_t status = acquire_sem_etc(fMainMonitor, 1, B_TIMEOUT, 0); 594 if (status != B_OK) { 595 if (!fWindow->IsHidden() && !fBlock) { 596 // Want to skip TASK msgs posted before the window 597 // was made visible. Better UI feel if we do this. 598 if (time > fSkipUntil) { 599 uint32 modifiers = 0; 600 message->FindInt32("modifiers", (int32*)&modifiers); 601 int32 key = 0; 602 message->FindInt32("key", &key); 603 604 Process((modifiers & B_SHIFT_KEY) == 0, key == 0x11); 605 } 606 } 607 } else 608 MainEntry(message); 609 610 break; 611 } 612 613 default: 614 break; 615 } 616 } 617 618 619 void 620 TSwitchManager::_SortApps() 621 { 622 team_id* teams; 623 int32 count; 624 if (BPrivate::get_application_order(current_workspace(), &teams, &count) 625 != B_OK) 626 return; 627 628 BList groups; 629 if (!groups.AddList(&fGroupList)) { 630 free(teams); 631 return; 632 } 633 634 fGroupList.MakeEmpty(); 635 636 for (int32 i = 0; i < count; i++) { 637 // find team 638 TTeamGroup* info = NULL; 639 for (int32 j = 0; (info = (TTeamGroup*)groups.ItemAt(j)) != NULL; j++) { 640 if (info->TeamList()->HasItem((void*)teams[i])) { 641 groups.RemoveItem(j); 642 break; 643 } 644 } 645 646 if (info != NULL) 647 fGroupList.AddItem(info); 648 } 649 650 fGroupList.AddList(&groups); 651 // add the remaining entries 652 free(teams); 653 } 654 655 656 void 657 TSwitchManager::MainEntry(BMessage* message) 658 { 659 bigtime_t now = system_time(); 660 bigtime_t timeout = now + 180000; 661 // The delay above was arrived at by trial and error and 662 // has a good "feel" 663 664 app_info appInfo; 665 be_roster->GetActiveAppInfo(&appInfo); 666 667 bool resetQuickSwitch = false; 668 669 if (now > fLastSwitch + 400000) { 670 _SortApps(); 671 resetQuickSwitch = true; 672 } 673 674 fLastSwitch = now; 675 676 int32 index; 677 fCurrentIndex = FindTeam(appInfo.team, &index) != NULL ? index : 0; 678 679 if (resetQuickSwitch) { 680 fQuickSwitchIndex = fCurrentIndex; 681 fQuickSwitchWindow = fCurrentWindow; 682 } 683 684 int32 key; 685 message->FindInt32("key", (int32*)&key); 686 687 uint32 modifierKeys = 0; 688 while (system_time() < timeout) { 689 modifierKeys = modifiers(); 690 if (!IsKeyDown(key)) { 691 QuickSwitch(message); 692 return; 693 } 694 if ((modifierKeys & B_CONTROL_KEY) == 0) { 695 QuickSwitch(message); 696 return; 697 } 698 snooze(20000); 699 // Must be a multiple of the delay used above 700 } 701 702 Process((modifierKeys & B_SHIFT_KEY) == 0, key == 0x11); 703 } 704 705 706 void 707 TSwitchManager::Stop(bool do_action, uint32) 708 { 709 fWindow->Hide(); 710 if (do_action) 711 ActivateApp(true, true); 712 713 release_sem(fMainMonitor); 714 } 715 716 717 TTeamGroup* 718 TSwitchManager::FindTeam(team_id teamID, int32* index) 719 { 720 int i = 0; 721 TTeamGroup* info; 722 while ((info = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) { 723 if (info->TeamList()->HasItem((void*)teamID)) { 724 *index = i; 725 return info; 726 } 727 i++; 728 } 729 730 return NULL; 731 } 732 733 734 void 735 TSwitchManager::Process(bool forward, bool byWindow) 736 { 737 bool hidden = false; 738 if (fWindow->Lock()) { 739 hidden = fWindow->IsHidden(); 740 fWindow->Unlock(); 741 } 742 if (byWindow) { 743 // If hidden we need to get things started by switching to correct app 744 if (hidden) 745 SwitchToApp(fCurrentIndex, fCurrentIndex, forward); 746 CycleWindow(forward, true); 747 } else 748 CycleApp(forward, false); 749 750 if (hidden) { 751 // more auto keyrepeat code 752 // Because of key repeats we don't want to respond to any extraneous 753 // 'TASK' messages until the window is completely shown. So block here. 754 // the WindowActivated hook function will unblock. 755 fBlock = true; 756 757 if (fWindow->Lock()) { 758 BRect screenFrame = BScreen().Frame(); 759 BRect windowFrame = fWindow->Frame(); 760 761 if (!screenFrame.Contains(windowFrame)) { 762 // center the window 763 BPoint point((screenFrame.left + screenFrame.right) / 2, 764 (screenFrame.top + screenFrame.bottom) / 2); 765 766 point.x -= (windowFrame.Width() / 2); 767 point.y -= (windowFrame.Height() / 2); 768 fWindow->MoveTo(point); 769 } 770 771 fWindow->Show(); 772 fWindow->Unlock(); 773 } 774 } 775 } 776 777 778 void 779 TSwitchManager::QuickSwitch(BMessage* message) 780 { 781 uint32 modifiers = 0; 782 message->FindInt32("modifiers", (int32*)&modifiers); 783 int32 key = 0; 784 message->FindInt32("key", &key); 785 786 team_id team; 787 if (message->FindInt32("team", &team) == B_OK) { 788 bool forward = (modifiers & B_SHIFT_KEY) == 0; 789 790 if (key == 0x11) { 791 // TODO: add the same switch logic we have for apps! 792 SwitchWindow(team, forward, true); 793 } else { 794 if (fQuickSwitchIndex >= 0) { 795 // Switch to the first app inbetween to make it always the next 796 // app to switch to after the quick switch. 797 int32 current = fCurrentIndex; 798 SwitchToApp(current, fQuickSwitchIndex, false); 799 ActivateApp(false, false); 800 801 fCurrentIndex = current; 802 } 803 804 CycleApp(forward, true); 805 } 806 } 807 808 release_sem(fMainMonitor); 809 } 810 811 812 int32 813 TSwitchManager::CountVisibleGroups() 814 { 815 int32 result = 0; 816 817 int32 count = fGroupList.CountItems(); 818 for (int32 i = 0; i < count; i++) { 819 if (!OKToUse((TTeamGroup*)fGroupList.ItemAt(i))) 820 continue; 821 822 result++; 823 } 824 return result; 825 } 826 827 828 void 829 TSwitchManager::CycleWindow(bool forward, bool wrap) 830 { 831 int32 max = CountWindows(fCurrentIndex); 832 int32 prev = fCurrentWindow; 833 int32 next = fCurrentWindow; 834 835 if (forward) { 836 next++; 837 if (next >= max) { 838 if (!wrap) 839 return; 840 next = 0; 841 } 842 } else { 843 next--; 844 if (next < 0) { 845 if (!wrap) 846 return; 847 next = max - 1; 848 } 849 } 850 fCurrentWindow = next; 851 852 if (fCurrentWindow != prev) 853 fWindow->WindowView()->ShowIndex(fCurrentWindow); 854 } 855 856 857 void 858 TSwitchManager::CycleApp(bool forward, bool activateNow) 859 { 860 int32 startIndex = fCurrentIndex; 861 int32 max = fGroupList.CountItems(); 862 863 for (;;) { 864 if (forward) { 865 fCurrentIndex++; 866 if (fCurrentIndex >= max) 867 fCurrentIndex = 0; 868 } else { 869 fCurrentIndex--; 870 if (fCurrentIndex < 0) 871 fCurrentIndex = max - 1; 872 } 873 if (fCurrentIndex == startIndex) { 874 // we've gone completely through the list without finding 875 // a good app. Oh well. 876 return; 877 } 878 879 if (!OKToUse((TTeamGroup*)fGroupList.ItemAt(fCurrentIndex))) 880 continue; 881 882 // if we're here then we found a good one 883 SwitchToApp(startIndex, fCurrentIndex, forward); 884 885 if (!activateNow) 886 break; 887 888 if (ActivateApp(false, false)) 889 break; 890 } 891 } 892 893 894 void 895 TSwitchManager::SwitchToApp(int32 previousIndex, int32 newIndex, bool forward) 896 { 897 int32 previousSlot = fCurrentSlot; 898 899 fCurrentIndex = newIndex; 900 fCurrentSlot = fWindow->SlotOf(fCurrentIndex); 901 fCurrentWindow = 0; 902 903 fWindow->Update(previousIndex, fCurrentIndex, previousSlot, fCurrentSlot, 904 forward); 905 } 906 907 908 bool 909 TSwitchManager::ActivateApp(bool forceShow, bool allowWorkspaceSwitch) 910 { 911 // Let's get the info about the selected window. If it doesn't exist 912 // anymore then get info about first window. If that doesn't exist then 913 // do nothing. 914 client_window_info* windowInfo = WindowInfo(fCurrentIndex, fCurrentWindow); 915 if (windowInfo == NULL) { 916 windowInfo = WindowInfo(fCurrentIndex, 0); 917 if (windowInfo == NULL) 918 return false; 919 } 920 921 int32 currentWorkspace = current_workspace(); 922 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 923 // Let's handle the easy case first: There's only 1 team in the group 924 if (teamGroup->TeamList()->CountItems() == 1) { 925 bool result; 926 if (forceShow && (fCurrentWindow != 0 || windowInfo->is_mini)) { 927 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT, 928 BRect(0, 0, 0, 0), false); 929 } 930 931 if (!forceShow && windowInfo->is_mini) { 932 // we aren't unhiding minimized windows, so we can't do 933 // anything here 934 result = false; 935 } else if (!allowWorkspaceSwitch 936 && (windowInfo->workspaces & (1 << currentWorkspace)) == 0) { 937 // we're not supposed to switch workspaces so abort. 938 result = false; 939 } else { 940 result = true; 941 be_roster->ActivateApp((team_id)teamGroup->TeamList()->ItemAt(0)); 942 } 943 944 ASSERT(windowInfo); 945 free(windowInfo); 946 return result; 947 } 948 949 // Now the trickier case. We're trying to Bring to the Front a group 950 // of teams. The current window (defined by fCurrentWindow) will define 951 // which workspace we're going to. Then, once that is determined we 952 // want to bring to the front every window of the group of teams that 953 // lives in that workspace. 954 955 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) { 956 if (!allowWorkspaceSwitch) { 957 // If the first window in the list isn't in current workspace, 958 // then none are. So we can't switch to this app. 959 ASSERT(windowInfo); 960 free(windowInfo); 961 return false; 962 } 963 int32 destWorkspace = LowBitIndex(windowInfo->workspaces); 964 // now switch to that workspace 965 activate_workspace(destWorkspace); 966 } 967 968 if (!forceShow && windowInfo->is_mini) { 969 // If the first window in the list is hidden then no windows in 970 // this group are visible. So we can't switch to this app. 971 ASSERT(windowInfo); 972 free(windowInfo); 973 return false; 974 } 975 976 int32 tokenCount; 977 int32* tokens = get_token_list(-1, &tokenCount); 978 if (tokens == NULL) { 979 ASSERT(windowInfo); 980 free(windowInfo); 981 return true; // weird error, so don't try to recover 982 } 983 984 BList windowsToActivate; 985 986 // Now we go through all the windows in the current workspace list in order. 987 // As we hit member teams we build the "activate" list. 988 for (int32 i = 0; i < tokenCount; i++) { 989 client_window_info* matchWindowInfo = get_window_info(tokens[i]); 990 if (!matchWindowInfo) { 991 // That window probably closed. Just go to the next one. 992 continue; 993 } 994 if (!IsVisibleInCurrentWorkspace(matchWindowInfo)) { 995 // first non-visible in workspace window means we're done. 996 free(matchWindowInfo); 997 break; 998 } 999 if (matchWindowInfo->server_token != windowInfo->server_token 1000 && teamGroup->TeamList()->HasItem((void*)matchWindowInfo->team)) 1001 windowsToActivate.AddItem((void*)matchWindowInfo->server_token); 1002 1003 free(matchWindowInfo); 1004 } 1005 1006 free(tokens); 1007 1008 // Want to go through the list backwards to keep windows in same relative 1009 // order. 1010 int32 i = windowsToActivate.CountItems() - 1; 1011 for (; i >= 0; i--) { 1012 int32 wid = (int32) windowsToActivate.ItemAt(i); 1013 do_window_action(wid, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false); 1014 } 1015 1016 // now bring the select window on top of everything. 1017 1018 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT, 1019 BRect(0, 0, 0, 0), false); 1020 1021 free(windowInfo); 1022 return true; 1023 } 1024 1025 1026 void 1027 TSwitchManager::QuitApp() 1028 { 1029 // check if we're in the last slot already (the last usable team group) 1030 1031 TTeamGroup* teamGroup; 1032 int32 count = 0; 1033 for (int32 i = fCurrentIndex + 1; i < fGroupList.CountItems(); i++) { 1034 teamGroup = (TTeamGroup*)fGroupList.ItemAt(i); 1035 1036 if (!OKToUse(teamGroup)) 1037 continue; 1038 1039 count++; 1040 } 1041 1042 teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 1043 1044 if (count == 0) { 1045 // switch to previous app in the list so that we don't jump to 1046 // the start of the list (try to keep the same position when 1047 // the apps at the current index go away) 1048 CycleApp(false, false); 1049 } 1050 1051 // send the quit request to all teams in this group 1052 1053 for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) { 1054 team_id team = (team_id)teamGroup->TeamList()->ItemAt(i); 1055 app_info info; 1056 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) { 1057 if (!strcasecmp(info.signature, kTrackerSignature)) { 1058 // Tracker can't be quit this way 1059 continue; 1060 } 1061 1062 BMessenger messenger(NULL, team); 1063 messenger.SendMessage(B_QUIT_REQUESTED); 1064 } 1065 } 1066 } 1067 1068 1069 void 1070 TSwitchManager::HideApp() 1071 { 1072 // hide all teams in this group 1073 1074 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 1075 1076 for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) { 1077 team_id team = (team_id)teamGroup->TeamList()->ItemAt(i); 1078 app_info info; 1079 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) 1080 do_minimize_team(BRect(), team, false); 1081 } 1082 } 1083 1084 1085 client_window_info* 1086 TSwitchManager::WindowInfo(int32 groupIndex, int32 windowIndex) 1087 { 1088 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex); 1089 if (teamGroup == NULL) 1090 return NULL; 1091 1092 int32 tokenCount; 1093 int32* tokens = get_token_list(-1, &tokenCount); 1094 if (tokens == NULL) 1095 return NULL; 1096 1097 int32 matches = 0; 1098 1099 // Want to find the "windowIndex'th" window in window order that belongs 1100 // the the specified group (groupIndex). Since multiple teams can belong to 1101 // the same group (multiple-launch apps) we get the list of _every_ 1102 // window and go from there. 1103 1104 client_window_info* result = NULL; 1105 for (int32 i = 0; i < tokenCount; i++) { 1106 client_window_info* windowInfo = get_window_info(tokens[i]); 1107 if (windowInfo) { 1108 // skip hidden/special windows 1109 if (IsWindowOK(windowInfo) 1110 && (teamGroup->TeamList()->HasItem((void*)windowInfo->team))) { 1111 // this window belongs to the team! 1112 if (matches == windowIndex) { 1113 // we found it! 1114 result = windowInfo; 1115 break; 1116 } 1117 matches++; 1118 } 1119 free(windowInfo); 1120 } 1121 // else - that window probably closed. Just go to the next one. 1122 } 1123 1124 free(tokens); 1125 1126 return result; 1127 } 1128 1129 1130 int32 1131 TSwitchManager::CountWindows(int32 groupIndex, bool ) 1132 { 1133 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex); 1134 if (!teamGroup) 1135 return 0; 1136 1137 int32 result = 0; 1138 1139 for (int32 i = 0; ; i++) { 1140 team_id teamID = (team_id)teamGroup->TeamList()->ItemAt(i); 1141 if (teamID == 0) 1142 break; 1143 1144 int32 count; 1145 int32* tokens = get_token_list(teamID, &count); 1146 if (!tokens) 1147 continue; 1148 1149 for (int32 i = 0; i < count; i++) { 1150 window_info *windowInfo = get_window_info(tokens[i]); 1151 if (windowInfo) { 1152 if (IsWindowOK(windowInfo)) 1153 result++; 1154 free(windowInfo); 1155 } 1156 } 1157 free(tokens); 1158 } 1159 1160 return result; 1161 } 1162 1163 1164 void 1165 TSwitchManager::ActivateWindow(int32 windowID) 1166 { 1167 if (windowID == -1) 1168 windowID = fWindowID; 1169 1170 do_window_action(windowID, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false); 1171 } 1172 1173 1174 void 1175 TSwitchManager::SwitchWindow(team_id team, bool, bool activate) 1176 { 1177 // Find the _last_ window in the current workspace that belongs 1178 // to the group. This is the window to activate. 1179 1180 int32 index; 1181 TTeamGroup* teamGroup = FindTeam(team, &index); 1182 1183 // cycle through the windows in the active application 1184 int32 count; 1185 int32* tokens = get_token_list(-1, &count); 1186 if (tokens == NULL) 1187 return; 1188 1189 for (int32 i = count - 1; i >= 0; i--) { 1190 client_window_info* windowInfo = get_window_info(tokens[i]); 1191 if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo) 1192 && teamGroup->TeamList()->HasItem((void*)windowInfo->team)) { 1193 fWindowID = windowInfo->server_token; 1194 if (activate) 1195 ActivateWindow(windowInfo->server_token); 1196 1197 free(windowInfo); 1198 break; 1199 } 1200 free(windowInfo); 1201 } 1202 free(tokens); 1203 } 1204 1205 1206 void 1207 TSwitchManager::Unblock() 1208 { 1209 fBlock = false; 1210 fSkipUntil = system_time(); 1211 } 1212 1213 1214 int32 1215 TSwitchManager::CurrentIndex() 1216 { 1217 return fCurrentIndex; 1218 } 1219 1220 1221 int32 1222 TSwitchManager::CurrentWindow() 1223 { 1224 return fCurrentWindow; 1225 } 1226 1227 1228 int32 1229 TSwitchManager::CurrentSlot() 1230 { 1231 return fCurrentSlot; 1232 } 1233 1234 1235 BList* 1236 TSwitchManager::GroupList() 1237 { 1238 return &fGroupList; 1239 } 1240 1241 1242 // #pragma mark - 1243 1244 1245 TBox::TBox(BRect bounds, TSwitchManager* manager, TSwitcherWindow* window, 1246 TIconView* iconView) 1247 : 1248 BBox(bounds, "top", B_FOLLOW_NONE, B_WILL_DRAW, B_NO_BORDER), 1249 fManager(manager), 1250 fWindow(window), 1251 fIconView(iconView), 1252 fLeftScroller(false), 1253 fRightScroller(false) 1254 { 1255 } 1256 1257 1258 void 1259 TBox::AllAttached() 1260 { 1261 BRect centerRect(kCenterSlot * kSlotSize, 0, 1262 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1263 BRect frame = fIconView->Frame(); 1264 1265 // scroll the centerRect to correct location 1266 centerRect.OffsetBy(frame.left, frame.top); 1267 1268 // switch to local coords 1269 fIconView->ConvertToParent(¢erRect); 1270 1271 fCenter = centerRect; 1272 } 1273 1274 1275 void 1276 TBox::MouseDown(BPoint where) 1277 { 1278 if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller) 1279 return; 1280 1281 BRect frame = fIconView->Frame(); 1282 BRect bounds = Bounds(); 1283 1284 if (fLeftScroller) { 1285 BRect lhit(0, frame.top, frame.left, frame.bottom); 1286 if (lhit.Contains(where)) { 1287 // Want to scroll by NUMSLOTS-1 slots 1288 int32 previousIndex = fManager->CurrentIndex(); 1289 int32 previousSlot = fManager->CurrentSlot(); 1290 int32 newSlot = previousSlot - (kNumSlots - 1); 1291 if (newSlot < 0) 1292 newSlot = 0; 1293 int32 newIndex = fIconView->IndexAt(newSlot); 1294 1295 fManager->SwitchToApp(previousIndex, newIndex, false); 1296 } 1297 } 1298 1299 if (fRightScroller) { 1300 BRect rhit(frame.right, frame.top, bounds.right, frame.bottom); 1301 if (rhit.Contains(where)) { 1302 // Want to scroll by NUMSLOTS-1 slots 1303 int32 previousIndex = fManager->CurrentIndex(); 1304 int32 previousSlot = fManager->CurrentSlot(); 1305 int32 newSlot = previousSlot + (kNumSlots-1); 1306 int32 newIndex = fIconView->IndexAt(newSlot); 1307 1308 if (newIndex < 0) { 1309 // don't have a page full to scroll 1310 int32 valid = fManager->CountVisibleGroups(); 1311 newIndex = fIconView->IndexAt(valid-1); 1312 } 1313 fManager->SwitchToApp(previousIndex, newIndex, true); 1314 } 1315 } 1316 1317 frame = fWindow->WindowView()->Frame(); 1318 if (fUpScroller) { 1319 BRect hit1(frame.left - 10, frame.top, frame.left, 1320 (frame.top+frame.bottom)/2); 1321 BRect hit2(frame.right, frame.top, frame.right + 10, 1322 (frame.top+frame.bottom)/2); 1323 if (hit1.Contains(where) || hit2.Contains(where)) { 1324 // Want to scroll up 1 window 1325 fManager->CycleWindow(false, false); 1326 } 1327 } 1328 1329 if (fDownScroller) { 1330 BRect hit1(frame.left - 10, (frame.top+frame.bottom) / 2, frame.left, 1331 frame.bottom); 1332 BRect hit2(frame.right, (frame.top+frame.bottom) / 2, frame.right + 10, 1333 frame.bottom); 1334 if (hit1.Contains(where) || hit2.Contains(where)) { 1335 // Want to scroll down 1 window 1336 fManager->CycleWindow(true, false); 1337 } 1338 } 1339 } 1340 1341 1342 void 1343 TBox::Draw(BRect update) 1344 { 1345 static const int32 kChildInset = 7; 1346 static const int32 kWedge = 6; 1347 1348 BBox::Draw(update); 1349 1350 // The fancy border around the icon view 1351 1352 BRect bounds = Bounds(); 1353 float height = fIconView->Bounds().Height(); 1354 float center = (bounds.right + bounds.left) / 2; 1355 1356 BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2); 1357 rgb_color white = {255, 255, 255, 255}; 1358 rgb_color standardGray = ui_color(B_PANEL_BACKGROUND_COLOR); 1359 rgb_color veryDarkGray = {128, 128, 128, 255}; 1360 rgb_color darkGray = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1361 B_DARKEN_1_TINT); 1362 1363 // Fill the area with dark gray 1364 SetHighColor(darkGray); 1365 box.InsetBy(1,1); 1366 FillRect(box); 1367 1368 box.InsetBy(-1,-1); 1369 1370 BeginLineArray(50); 1371 1372 // The main frame around the icon view 1373 AddLine(box.LeftTop(), BPoint(center-kWedge, box.top), veryDarkGray); 1374 AddLine(BPoint(center+kWedge, box.top), box.RightTop(), veryDarkGray); 1375 1376 AddLine(box.LeftBottom(), BPoint(center-kWedge, box.bottom), veryDarkGray); 1377 AddLine(BPoint(center+kWedge, box.bottom), box.RightBottom(), veryDarkGray); 1378 AddLine(box.LeftBottom() + BPoint(1, 1), 1379 BPoint(center-kWedge, box.bottom + 1), white); 1380 AddLine(BPoint(center+kWedge, box.bottom) + BPoint(0, 1), 1381 box.RightBottom() + BPoint(1, 1), white); 1382 1383 AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray); 1384 AddLine(box.RightTop(), box.RightBottom(), veryDarkGray); 1385 AddLine(box.RightTop() + BPoint(1, 1), 1386 box.RightBottom() + BPoint(1, 1), white); 1387 1388 // downward pointing area at top of frame 1389 BPoint point(center - kWedge, box.top); 1390 AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray); 1391 AddLine(point + BPoint(kWedge, kWedge), 1392 BPoint(center+kWedge, point.y), veryDarkGray); 1393 1394 AddLine(point + BPoint(1, 0), 1395 point + BPoint(1, 0) + BPoint(kWedge - 1, kWedge - 1), white); 1396 1397 AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1), 1398 BPoint(center+kWedge-1, point.y), darkGray); 1399 1400 BPoint topPoint = point; 1401 1402 // upward pointing area at bottom of frame 1403 point.y = box.bottom; 1404 point.x = center - kWedge; 1405 AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray); 1406 AddLine(point + BPoint(kWedge, -kWedge), 1407 BPoint(center+kWedge, point.y), veryDarkGray); 1408 1409 AddLine(point + BPoint(1, 0), 1410 point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white); 1411 1412 AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)), 1413 BPoint(center + kWedge - 1, point.y), darkGray); 1414 1415 BPoint bottomPoint = point; 1416 1417 EndLineArray(); 1418 1419 // fill the downward pointing arrow area 1420 SetHighColor(standardGray); 1421 FillTriangle(topPoint + BPoint(2, 0), 1422 topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2), 1423 BPoint(center + kWedge - 2, topPoint.y)); 1424 1425 // fill the upward pointing arrow area 1426 SetHighColor(standardGray); 1427 FillTriangle(bottomPoint + BPoint(2,0), 1428 bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)), 1429 BPoint(center + kWedge - 2, bottomPoint.y)); 1430 1431 DrawIconScrollers(false); 1432 DrawWindowScrollers(false); 1433 1434 } 1435 1436 1437 void 1438 TBox::DrawIconScrollers(bool force) 1439 { 1440 rgb_color backgroundColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1441 B_DARKEN_1_TINT); 1442 rgb_color dark = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1443 B_DARKEN_4_TINT); 1444 bool updateLeft = false; 1445 bool updateRight = false; 1446 1447 BRect rect = fIconView->Bounds(); 1448 if (rect.left > (kSlotSize * kCenterSlot)) { 1449 updateLeft = true; 1450 fLeftScroller = true; 1451 } else { 1452 fLeftScroller = false; 1453 if (force) 1454 updateLeft = true; 1455 } 1456 1457 int32 maxIndex = fManager->GroupList()->CountItems() - 1; 1458 // last_frame is in fIconView coordinate space 1459 BRect lastFrame = fIconView->FrameOf(maxIndex); 1460 1461 if (lastFrame.right > rect.right) { 1462 updateRight = true; 1463 fRightScroller = true; 1464 } else { 1465 fRightScroller = false; 1466 if (force) 1467 updateRight = true; 1468 } 1469 1470 PushState(); 1471 SetDrawingMode(B_OP_COPY); 1472 1473 rect = fIconView->Frame(); 1474 if (updateLeft) { 1475 BPoint pt1, pt2, pt3; 1476 pt1.x = rect.left - 5; 1477 pt1.y = floorf((rect.bottom + rect.top) / 2); 1478 pt2.x = pt3.x = pt1.x + 3; 1479 pt2.y = pt1.y - 3; 1480 pt3.y = pt1.y + 3; 1481 1482 if (fLeftScroller) { 1483 SetHighColor(dark); 1484 FillTriangle(pt1, pt2, pt3); 1485 } else if (force) { 1486 SetHighColor(backgroundColor); 1487 FillRect(BRect(pt1.x, pt2.y, pt3.x, pt3.y)); 1488 } 1489 } 1490 if (updateRight) { 1491 BPoint pt1, pt2, pt3; 1492 pt1.x = rect.right + 4; 1493 pt1.y = rintf((rect.bottom + rect.top) / 2); 1494 pt2.x = pt3.x = pt1.x - 4; 1495 pt2.y = pt1.y - 4; 1496 pt3.y = pt1.y + 4; 1497 1498 if (fRightScroller) { 1499 SetHighColor(dark); 1500 FillTriangle(pt1, pt2, pt3); 1501 } else if (force) { 1502 SetHighColor(backgroundColor); 1503 FillRect(BRect(pt3.x, pt2.y, pt1.x, pt3.y)); 1504 } 1505 } 1506 1507 PopState(); 1508 } 1509 1510 1511 void 1512 TBox::DrawWindowScrollers(bool force) 1513 { 1514 rgb_color backgroundColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1515 rgb_color dark = tint_color(backgroundColor, B_DARKEN_4_TINT); 1516 bool updateUp = false; 1517 bool updateDown = false; 1518 1519 BRect rect = fWindow->WindowView()->Bounds(); 1520 if (rect.top != 0) { 1521 updateUp = true; 1522 fUpScroller = true; 1523 } else { 1524 fUpScroller = false; 1525 if (force) 1526 updateUp = true; 1527 } 1528 1529 int32 groupIndex = fManager->CurrentIndex(); 1530 int32 maxIndex = fManager->CountWindows(groupIndex) - 1; 1531 1532 BRect lastFrame(0, 0, 0, 0); 1533 if (maxIndex >= 0) 1534 lastFrame = fWindow->WindowView()->FrameOf(maxIndex); 1535 1536 if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) { 1537 updateDown = true; 1538 fDownScroller = true; 1539 } else { 1540 fDownScroller = false; 1541 if (force) 1542 updateDown = true; 1543 } 1544 1545 PushState(); 1546 SetDrawingMode(B_OP_COPY); 1547 1548 rect = fWindow->WindowView()->Frame(); 1549 rect.InsetBy(-3, 0); 1550 if (updateUp) { 1551 if (fUpScroller) { 1552 SetHighColor(dark); 1553 BPoint pt1, pt2, pt3; 1554 pt1.x = rect.left - 6; 1555 pt1.y = rect.top + 3; 1556 pt2.y = pt3.y = pt1.y + 4; 1557 pt2.x = pt1.x - 4; 1558 pt3.x = pt1.x + 4; 1559 FillTriangle(pt1, pt2, pt3); 1560 1561 pt1.x += rect.Width() + 12; 1562 pt2.x += rect.Width() + 12; 1563 pt3.x += rect.Width() + 12; 1564 FillTriangle(pt1, pt2, pt3); 1565 } else if (force) { 1566 FillRect(BRect(rect.left - 10, rect.top + 3, rect.left - 2, 1567 rect.top + 7), B_SOLID_LOW); 1568 FillRect(BRect(rect.right + 2, rect.top + 3, rect.right + 10, 1569 rect.top + 7), B_SOLID_LOW); 1570 } 1571 } 1572 if (updateDown) { 1573 if (fDownScroller) { 1574 SetHighColor(dark); 1575 BPoint pt1, pt2, pt3; 1576 pt1.x = rect.left - 6; 1577 pt1.y = rect.bottom - 3; 1578 pt2.y = pt3.y = pt1.y - 4; 1579 pt2.x = pt1.x - 4; 1580 pt3.x = pt1.x + 4; 1581 FillTriangle(pt1, pt2, pt3); 1582 1583 pt1.x += rect.Width() + 12; 1584 pt2.x += rect.Width() + 12; 1585 pt3.x += rect.Width() + 12; 1586 FillTriangle(pt1, pt2, pt3); 1587 } else if (force) { 1588 FillRect(BRect(rect.left - 10, rect.bottom - 7, rect.left - 2, 1589 rect.bottom - 3), B_SOLID_LOW); 1590 FillRect(BRect(rect.right + 2, rect.bottom - 7, rect.right + 10, 1591 rect.bottom - 3), B_SOLID_LOW); 1592 } 1593 } 1594 1595 PopState(); 1596 Sync(); 1597 } 1598 1599 1600 // #pragma mark - 1601 1602 1603 TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager* manager) 1604 : 1605 BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL, 1606 B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES), 1607 fManager(manager), 1608 fHairTrigger(true) 1609 { 1610 BRect rect = frame; 1611 rect.OffsetTo(B_ORIGIN); 1612 rect.InsetBy(kHorizontalMargin, 0); 1613 rect.top = kVerticalMargin; 1614 rect.bottom = rect.top + kSlotSize - 1; 1615 1616 fIconView = new TIconView(rect, manager, this); 1617 1618 rect.top = rect.bottom + (kVerticalMargin * 1 + 4); 1619 rect.InsetBy(9, 0); 1620 1621 fWindowView = new TWindowView(rect, manager, this); 1622 fWindowView->ResizeToPreferred(); 1623 1624 fTopView = new TBox(Bounds(), fManager, this, fIconView); 1625 AddChild(fTopView); 1626 1627 SetPulseRate(0); 1628 fTopView->AddChild(fIconView); 1629 fTopView->AddChild(fWindowView); 1630 } 1631 1632 1633 TSwitcherWindow::~TSwitcherWindow() 1634 { 1635 } 1636 1637 1638 void 1639 TSwitcherWindow::MessageReceived(BMessage* message) 1640 { 1641 switch (message->what) { 1642 case B_UNMAPPED_KEY_DOWN: 1643 case B_KEY_DOWN: 1644 { 1645 int32 repeats = 0; 1646 if (message->FindInt32("be:key_repeat", &repeats) == B_OK 1647 && (fSkipKeyRepeats || (repeats % 6) != 0)) 1648 break; 1649 1650 // The first actual key press let's us listening to repeated keys 1651 fSkipKeyRepeats = false; 1652 1653 uint32 rawChar; 1654 uint32 modifiers; 1655 message->FindInt32("raw_char", 0, (int32*)&rawChar); 1656 message->FindInt32("modifiers", 0, (int32*)&modifiers); 1657 DoKey(rawChar, modifiers); 1658 break; 1659 } 1660 1661 default: 1662 BWindow::MessageReceived(message); 1663 } 1664 } 1665 1666 1667 void 1668 TSwitcherWindow::Redraw(int32 index) 1669 { 1670 BRect frame = fIconView->FrameOf(index); 1671 frame.right = fIconView->Bounds().right; 1672 fIconView->Invalidate(frame); 1673 } 1674 1675 1676 void 1677 TSwitcherWindow::DoKey(uint32 key, uint32 modifiers) 1678 { 1679 bool forward = ((modifiers & B_SHIFT_KEY) == 0); 1680 1681 switch (key) { 1682 case B_RIGHT_ARROW: 1683 fManager->CycleApp(true, false); 1684 break; 1685 1686 case B_LEFT_ARROW: 1687 case '1': 1688 fManager->CycleApp(false, false); 1689 break; 1690 1691 case B_UP_ARROW: 1692 fManager->CycleWindow(false, false); 1693 break; 1694 1695 case B_DOWN_ARROW: 1696 fManager->CycleWindow(true, false); 1697 break; 1698 1699 case B_TAB: 1700 fManager->CycleApp(forward, false); 1701 break; 1702 1703 case B_ESCAPE: 1704 fManager->Stop(false, 0); 1705 break; 1706 1707 case B_SPACE: 1708 case B_ENTER: 1709 fManager->Stop(true, modifiers); 1710 break; 1711 1712 case 'q': 1713 case 'Q': 1714 fManager->QuitApp(); 1715 break; 1716 1717 case 'h': 1718 case 'H': 1719 fManager->HideApp(); 1720 break; 1721 1722 #if _ALLOW_STICKY_ 1723 case 's': 1724 case 'S': 1725 if (fHairTrigger) { 1726 SetLook(B_TITLED_WINDOW_LOOK); 1727 fHairTrigger = false; 1728 } else { 1729 SetLook(B_MODAL_WINDOW_LOOK); 1730 fHairTrigger = true; 1731 } 1732 break; 1733 #endif 1734 } 1735 } 1736 1737 1738 bool 1739 TSwitcherWindow::QuitRequested() 1740 { 1741 ((TBarApp*)be_app)->Settings()->switcherLoc = Frame().LeftTop(); 1742 fManager->Stop(false, 0); 1743 return false; 1744 } 1745 1746 1747 void 1748 TSwitcherWindow::WindowActivated(bool state) 1749 { 1750 if (state) 1751 fManager->Unblock(); 1752 } 1753 1754 1755 void 1756 TSwitcherWindow::Update(int32 prev, int32 current, int32 previousSlot, 1757 int32 currentSlot, bool forward) 1758 { 1759 if (!IsHidden()) 1760 fIconView->Update(prev, current, previousSlot, currentSlot, forward); 1761 else 1762 fIconView->CenterOn(current); 1763 1764 fWindowView->UpdateGroup(current, 0); 1765 } 1766 1767 1768 void 1769 TSwitcherWindow::Hide() 1770 { 1771 fIconView->Hiding(); 1772 SetPulseRate(0); 1773 BWindow::Hide(); 1774 } 1775 1776 1777 void 1778 TSwitcherWindow::Show() 1779 { 1780 fHairTrigger = true; 1781 fSkipKeyRepeats = true; 1782 fIconView->Showing(); 1783 SetPulseRate(100000); 1784 SetLook(B_MODAL_WINDOW_LOOK); 1785 BWindow::Show(); 1786 } 1787 1788 1789 TBox* 1790 TSwitcherWindow::TopView() 1791 { 1792 return fTopView; 1793 } 1794 1795 1796 bool 1797 TSwitcherWindow::HairTrigger() 1798 { 1799 return fHairTrigger; 1800 } 1801 1802 1803 inline int32 1804 TSwitcherWindow::SlotOf(int32 i) 1805 { 1806 return fIconView->SlotOf(i); 1807 } 1808 1809 1810 inline TIconView* 1811 TSwitcherWindow::IconView() 1812 { 1813 return fIconView; 1814 } 1815 1816 1817 inline TWindowView* 1818 TSwitcherWindow::WindowView() 1819 { 1820 return fWindowView; 1821 } 1822 1823 1824 // #pragma mark - 1825 1826 1827 TIconView::TIconView(BRect frame, TSwitchManager* manager, 1828 TSwitcherWindow* switcherWindow) 1829 : BView(frame, "main_view", B_FOLLOW_NONE, 1830 B_WILL_DRAW | B_PULSE_NEEDED), 1831 fAutoScrolling(false), 1832 fSwitcher(switcherWindow), 1833 fManager(manager) 1834 { 1835 BRect rect(0, 0, kSlotSize - 1, kSlotSize - 1); 1836 rgb_color color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1837 B_DARKEN_1_TINT); 1838 1839 fOffView = new BView(rect, "off_view", B_FOLLOW_NONE, B_WILL_DRAW); 1840 fOffView->SetHighColor(color); 1841 fOffBitmap = new BBitmap(rect, B_RGB32, true); 1842 fOffBitmap->AddChild(fOffView); 1843 1844 fCurrentSmall = new BBitmap(BRect(0, 0, 15, 15), kIconFormat); 1845 fCurrentLarge = new BBitmap(BRect(0, 0, 31, 31), kIconFormat); 1846 1847 SetViewColor(color); 1848 SetLowColor(color); 1849 } 1850 1851 1852 TIconView::~TIconView() 1853 { 1854 delete fCurrentSmall; 1855 delete fCurrentLarge; 1856 delete fOffBitmap; 1857 } 1858 1859 1860 void 1861 TIconView::KeyDown(const char* /*bytes*/, int32 /*numBytes*/) 1862 { 1863 } 1864 1865 1866 void 1867 TIconView::CacheIcons(TTeamGroup* teamGroup) 1868 { 1869 const BBitmap* bitmap = teamGroup->SmallIcon(); 1870 ASSERT(bitmap); 1871 fCurrentSmall->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1872 bitmap->ColorSpace()); 1873 1874 bitmap = teamGroup->LargeIcon(); 1875 ASSERT(bitmap); 1876 fCurrentLarge->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1877 bitmap->ColorSpace()); 1878 } 1879 1880 1881 void 1882 TIconView::AnimateIcon(BBitmap* startIcon, BBitmap* endIcon) 1883 { 1884 BRect centerRect(kCenterSlot*kSlotSize, 0, 1885 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1886 BRect startIconBounds = startIcon->Bounds(); 1887 BRect bounds = Bounds(); 1888 float width = startIconBounds.Width(); 1889 int32 amount = (width < 20) ? -2 : 2; 1890 1891 // center the starting icon inside of centerRect 1892 float off = (centerRect.Width() - width) / 2; 1893 startIconBounds.OffsetTo(BPoint(off, off)); 1894 1895 // scroll the centerRect to correct location 1896 centerRect.OffsetBy(bounds.left, 0); 1897 1898 BRect destRect = fOffBitmap->Bounds(); 1899 // scroll to the centerRect location 1900 destRect.OffsetTo(centerRect.left, 0); 1901 // center the destRect inside of centerRect. 1902 off = (centerRect.Width() - destRect.Width()) / 2; 1903 destRect.OffsetBy(BPoint(off, off)); 1904 1905 fOffBitmap->Lock(); 1906 fOffView->SetDrawingMode(B_OP_ALPHA); 1907 for (int i = 0; i < 2; i++) { 1908 startIconBounds.InsetBy(amount,amount); 1909 snooze(20000); 1910 fOffView->FillRect(fOffView->Bounds()); 1911 fOffView->DrawBitmap(startIcon, startIconBounds); 1912 fOffView->Sync(); 1913 DrawBitmap(fOffBitmap, destRect); 1914 } 1915 for (int i = 0; i < 2; i++) { 1916 startIconBounds.InsetBy(amount,amount); 1917 snooze(20000); 1918 fOffView->FillRect(fOffView->Bounds()); 1919 fOffView->DrawBitmap(endIcon, startIconBounds); 1920 fOffView->Sync(); 1921 DrawBitmap(fOffBitmap, destRect); 1922 } 1923 1924 fOffView->SetDrawingMode(B_OP_COPY); 1925 fOffBitmap->Unlock(); 1926 } 1927 1928 1929 void 1930 TIconView::Update(int32, int32 current, int32 previousSlot, int32 currentSlot, 1931 bool forward) 1932 { 1933 // Animate the shrinking of the currently centered icon. 1934 AnimateIcon(fCurrentLarge, fCurrentSmall); 1935 1936 int32 nslots = abs(previousSlot - currentSlot); 1937 int32 stepSize = kScrollStep; 1938 1939 if (forward && (currentSlot < previousSlot)) { 1940 // we were at the end of the list and we just moved to the start 1941 forward = false; 1942 if (previousSlot - currentSlot > 4) 1943 stepSize *= 2; 1944 } else if (!forward && (currentSlot > previousSlot)) { 1945 // we're are moving backwards and we just hit start of list and 1946 // we wrapped to the end. 1947 forward = true; 1948 if (currentSlot - previousSlot > 4) 1949 stepSize *= 2; 1950 } 1951 1952 int32 scrollValue = forward ? stepSize : -stepSize; 1953 int32 total = 0; 1954 1955 fAutoScrolling = true; 1956 while (total < (nslots * kSlotSize)) { 1957 ScrollBy(scrollValue, 0); 1958 snooze(1000); 1959 total += stepSize; 1960 Window()->UpdateIfNeeded(); 1961 } 1962 fAutoScrolling = false; 1963 1964 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(current); 1965 ASSERT(teamGroup); 1966 CacheIcons(teamGroup); 1967 1968 // Animate the expansion of the currently centered icon 1969 AnimateIcon(fCurrentSmall, fCurrentLarge); 1970 } 1971 1972 1973 void 1974 TIconView::CenterOn(int32 index) 1975 { 1976 BRect rect = FrameOf(index); 1977 ScrollTo(rect.left - (kCenterSlot * kSlotSize), 0); 1978 } 1979 1980 1981 int32 1982 TIconView::ItemAtPoint(BPoint point) const 1983 { 1984 float tmpPointVerticalIndex = (point.x / kSlotSize) - kCenterSlot; 1985 if (tmpPointVerticalIndex < 0) 1986 return -1; 1987 1988 int32 pointVerticalIndex = (int32)tmpPointVerticalIndex; 1989 1990 for (int32 i = 0, verticalIndex = 0; ; i++) { 1991 1992 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(i); 1993 if (teamGroup == NULL) 1994 break; 1995 1996 if (!OKToUse(teamGroup)) 1997 continue; 1998 1999 if (verticalIndex == pointVerticalIndex) 2000 return i; 2001 2002 verticalIndex++; 2003 } 2004 return -1; 2005 } 2006 2007 2008 void 2009 TIconView::ScrollTo(BPoint where) 2010 { 2011 BView::ScrollTo(where); 2012 fSwitcher->TopView()->DrawIconScrollers(true); 2013 } 2014 2015 2016 int32 2017 TIconView::IndexAt(int32 slot) const 2018 { 2019 BList* list = fManager->GroupList(); 2020 int32 count = list->CountItems(); 2021 int32 slotIndex = 0; 2022 2023 for (int32 i = 0; i < count; i++) { 2024 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2025 2026 if (!OKToUse(teamGroup)) 2027 continue; 2028 2029 if (slotIndex == slot) { 2030 return i; 2031 } 2032 slotIndex++; 2033 } 2034 return -1; 2035 } 2036 2037 2038 int32 2039 TIconView::SlotOf(int32 index) const 2040 { 2041 BRect rect = FrameOf(index); 2042 return (int32)(rect.left / kSlotSize) - kCenterSlot; 2043 } 2044 2045 2046 BRect 2047 TIconView::FrameOf(int32 index) const 2048 { 2049 BList* list = fManager->GroupList(); 2050 int32 visible = kCenterSlot - 1; 2051 // first few slots in view are empty 2052 2053 TTeamGroup* teamGroup; 2054 for (int32 i = 0; i <= index; i++) { 2055 teamGroup = (TTeamGroup*)list->ItemAt(i); 2056 2057 if (!OKToUse(teamGroup)) 2058 continue; 2059 2060 visible++; 2061 } 2062 2063 return BRect(visible * kSlotSize, 0, (visible + 1) * kSlotSize - 1, 2064 kSlotSize - 1); 2065 } 2066 2067 2068 void 2069 TIconView::DrawTeams(BRect update) 2070 { 2071 int32 mainIndex = fManager->CurrentIndex(); 2072 BList* list = fManager->GroupList(); 2073 int32 count = list->CountItems(); 2074 2075 BRect rect(kCenterSlot * kSlotSize, 0, 2076 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 2077 2078 for (int32 i = 0; i < count; i++) { 2079 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2080 2081 if (!OKToUse(teamGroup)) 2082 continue; 2083 2084 if (rect.Intersects(update) && teamGroup) { 2085 SetDrawingMode(B_OP_ALPHA); 2086 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 2087 2088 teamGroup->Draw(this, rect, !fAutoScrolling && (i == mainIndex)); 2089 2090 if (i == mainIndex) 2091 CacheIcons(teamGroup); 2092 2093 SetDrawingMode(B_OP_COPY); 2094 } 2095 rect.OffsetBy(kSlotSize,0); 2096 } 2097 } 2098 2099 2100 void 2101 TIconView::Draw(BRect update) 2102 { 2103 DrawTeams(update); 2104 } 2105 2106 2107 void 2108 TIconView::MouseDown(BPoint where) 2109 { 2110 int32 index = ItemAtPoint(where); 2111 if (index >= 0) { 2112 int32 previousIndex = fManager->CurrentIndex(); 2113 int32 previousSlot = fManager->CurrentSlot(); 2114 int32 currentSlot = SlotOf(index); 2115 fManager->SwitchToApp(previousIndex, index, (currentSlot 2116 > previousSlot)); 2117 } 2118 } 2119 2120 2121 void 2122 TIconView::Pulse() 2123 { 2124 uint32 modifiersKeys = modifiers(); 2125 if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) { 2126 fManager->Stop(true, modifiersKeys); 2127 return; 2128 } 2129 2130 if (!fSwitcher->HairTrigger()) { 2131 uint32 buttons; 2132 BPoint point; 2133 GetMouse(&point, &buttons); 2134 if (buttons != 0) { 2135 point = ConvertToScreen(point); 2136 if (!Window()->Frame().Contains(point)) 2137 fManager->Stop(false, 0); 2138 } 2139 } 2140 } 2141 2142 2143 void 2144 TIconView::Showing() 2145 { 2146 } 2147 2148 2149 void 2150 TIconView::Hiding() 2151 { 2152 ScrollTo(B_ORIGIN); 2153 } 2154 2155 2156 // #pragma mark - 2157 2158 2159 TWindowView::TWindowView(BRect rect, TSwitchManager* manager, 2160 TSwitcherWindow* window) 2161 : BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), 2162 fCurrentToken(-1), 2163 fSwitcher(window), 2164 fManager(manager) 2165 { 2166 SetFont(be_plain_font); 2167 } 2168 2169 2170 void 2171 TWindowView::AttachedToWindow() 2172 { 2173 if (Parent()) 2174 SetViewColor(Parent()->ViewColor()); 2175 else 2176 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2177 } 2178 2179 2180 void 2181 TWindowView::ScrollTo(BPoint where) 2182 { 2183 BView::ScrollTo(where); 2184 fSwitcher->TopView()->DrawWindowScrollers(true); 2185 } 2186 2187 2188 2189 BRect 2190 TWindowView::FrameOf(int32 index) const 2191 { 2192 return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1); 2193 } 2194 2195 2196 void 2197 TWindowView::GetPreferredSize(float* _width, float* _height) 2198 { 2199 font_height fh; 2200 be_plain_font->GetHeight(&fh); 2201 fItemHeight = (int32) fh.ascent + fh.descent; 2202 2203 // top & bottom margin 2204 fItemHeight = fItemHeight + 3 + 3; 2205 2206 // want fItemHeight to be divisible by kWindowScrollSteps. 2207 fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps) 2208 / kWindowScrollSteps) * kWindowScrollSteps; 2209 2210 *_height = fItemHeight; 2211 2212 // leave width alone 2213 *_width = Bounds().Width(); 2214 } 2215 2216 2217 void 2218 TWindowView::ShowIndex(int32 newIndex) 2219 { 2220 // convert index to scroll location 2221 BPoint point(0, newIndex * fItemHeight); 2222 BRect bounds = Bounds(); 2223 2224 int32 groupIndex = fManager->CurrentIndex(); 2225 TTeamGroup* teamGroup 2226 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2227 if (teamGroup == NULL) 2228 return; 2229 2230 window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex); 2231 if (windowInfo == NULL) 2232 return; 2233 2234 fCurrentToken = windowInfo->server_token; 2235 free(windowInfo); 2236 2237 if (bounds.top == point.y) 2238 return; 2239 2240 int32 oldIndex = (int32) (bounds.top / fItemHeight); 2241 2242 int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps); 2243 int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize; 2244 int32 total = 0; 2245 int32 nslots = abs(newIndex - oldIndex); 2246 2247 while (total < (nslots * (int32)fItemHeight)) { 2248 ScrollBy(0, scrollValue); 2249 snooze(10000); 2250 total += stepSize; 2251 Window()->UpdateIfNeeded(); 2252 } 2253 } 2254 2255 2256 void 2257 TWindowView::Draw(BRect update) 2258 { 2259 int32 groupIndex = fManager->CurrentIndex(); 2260 TTeamGroup* teamGroup 2261 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2262 if (teamGroup == NULL) 2263 return; 2264 2265 BRect bounds = Bounds(); 2266 int32 windowIndex = (int32) (bounds.top / fItemHeight); 2267 BRect windowRect = bounds; 2268 2269 windowRect.top = windowIndex * fItemHeight; 2270 windowRect.bottom = (windowIndex + 1) * fItemHeight - 1; 2271 2272 for (int32 i = 0; i < 3; i++) { 2273 if (!update.Intersects(windowRect)) { 2274 windowIndex++; 2275 windowRect.OffsetBy(0, fItemHeight); 2276 continue; 2277 } 2278 2279 // is window in current workspace? 2280 2281 bool local = true; 2282 bool minimized = false; 2283 BString title; 2284 2285 client_window_info* windowInfo 2286 = fManager->WindowInfo(groupIndex, windowIndex); 2287 if (windowInfo != NULL) { 2288 if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0) 2289 title << teamGroup->Name() << ": " << windowInfo->name; 2290 else 2291 title = teamGroup->Name(); 2292 2293 int32 currentWorkspace = current_workspace(); 2294 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) 2295 local = false; 2296 2297 minimized = windowInfo->is_mini; 2298 free(windowInfo); 2299 } else 2300 title = teamGroup->Name(); 2301 2302 if (!title.Length()) 2303 return; 2304 2305 float stringWidth = StringWidth(title.String()); 2306 float maxWidth = bounds.Width() - (14 + 5); 2307 2308 if (stringWidth > maxWidth) { 2309 // window title is too long, need to truncate 2310 TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth); 2311 stringWidth = maxWidth; 2312 } 2313 2314 BPoint point((bounds.Width() - (stringWidth + 14 + 5)) / 2, 2315 windowRect.bottom - 4); 2316 BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2); 2317 SetDrawingMode(B_OP_OVER); 2318 const BBitmap* bitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE, 2319 minimized ? R_WindowHiddenIcon : R_WindowShownIcon); 2320 p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2; 2321 DrawBitmap(bitmap, p); 2322 2323 if (!local) { 2324 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 2325 B_DARKEN_4_TINT)); 2326 p.x -= 8; 2327 p.y += 4; 2328 StrokeLine(p + BPoint(2, 2), p + BPoint(2, 2)); 2329 StrokeLine(p + BPoint(4, 2), p + BPoint(6, 2)); 2330 2331 StrokeLine(p + BPoint(0, 5), p + BPoint(0, 5)); 2332 StrokeLine(p + BPoint(2, 5), p + BPoint(6, 5)); 2333 2334 StrokeLine(p + BPoint(1, 8), p + BPoint(1, 8)); 2335 StrokeLine(p + BPoint(3, 8), p + BPoint(6, 8)); 2336 2337 SetHighColor(0, 0, 0); 2338 } 2339 2340 point.x += 21; 2341 MovePenTo(point); 2342 2343 DrawString(title.String()); 2344 SetDrawingMode(B_OP_COPY); 2345 2346 windowIndex++; 2347 windowRect.OffsetBy(0, fItemHeight); 2348 } 2349 } 2350 2351 2352 void 2353 TWindowView::UpdateGroup(int32 , int32 windowIndex) 2354 { 2355 ScrollTo(0, windowIndex * fItemHeight); 2356 Invalidate(Bounds()); 2357 } 2358 2359 2360 void 2361 TWindowView::Pulse() 2362 { 2363 // If selected window went away then reset to first window 2364 window_info *windowInfo = get_window_info(fCurrentToken); 2365 if (windowInfo == NULL) { 2366 Invalidate(); 2367 ShowIndex(0); 2368 } else 2369 free(windowInfo); 2370 } 2371 2372