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