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 Deskbar 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 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 if (fCurrentIndex == startIndex) { 891 // we've gone completely through the list without finding 892 // a good app. Oh well. 893 break; 894 } 895 896 if (OKToUse((TTeamGroup*)fGroupList.ItemAt(fCurrentIndex))) 897 return true; 898 } 899 900 return false; 901 } 902 903 void 904 TSwitchManager::SwitchToApp(int32 previousIndex, int32 newIndex, bool forward) 905 { 906 int32 previousSlot = fCurrentSlot; 907 908 fCurrentIndex = newIndex; 909 if (!OKToUse((TTeamGroup *)fGroupList.ItemAt(fCurrentIndex))) 910 _FindNextValidApp(forward); 911 912 fCurrentSlot = fWindow->SlotOf(fCurrentIndex); 913 fCurrentWindow = 0; 914 915 fWindow->Update(previousIndex, fCurrentIndex, previousSlot, fCurrentSlot, 916 forward); 917 } 918 919 920 bool 921 TSwitchManager::ActivateApp(bool forceShow, bool allowWorkspaceSwitch) 922 { 923 // Let's get the info about the selected window. If it doesn't exist 924 // anymore then get info about first window. If that doesn't exist then 925 // do nothing. 926 client_window_info* windowInfo = WindowInfo(fCurrentIndex, fCurrentWindow); 927 if (windowInfo == NULL) { 928 windowInfo = WindowInfo(fCurrentIndex, 0); 929 if (windowInfo == NULL) 930 return false; 931 } 932 933 int32 currentWorkspace = current_workspace(); 934 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 935 // Let's handle the easy case first: There's only 1 team in the group 936 if (teamGroup->TeamList()->CountItems() == 1) { 937 bool result; 938 if (forceShow && (fCurrentWindow != 0 || windowInfo->is_mini)) { 939 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT, 940 BRect(0, 0, 0, 0), false); 941 } 942 943 if (!forceShow && windowInfo->is_mini) { 944 // we aren't unhiding minimized windows, so we can't do 945 // anything here 946 result = false; 947 } else if (!allowWorkspaceSwitch 948 && (windowInfo->workspaces & (1 << currentWorkspace)) == 0) { 949 // we're not supposed to switch workspaces so abort. 950 result = false; 951 } else { 952 result = true; 953 be_roster->ActivateApp((team_id)teamGroup->TeamList()->ItemAt(0)); 954 } 955 956 ASSERT(windowInfo); 957 free(windowInfo); 958 return result; 959 } 960 961 // Now the trickier case. We're trying to Bring to the Front a group 962 // of teams. The current window (defined by fCurrentWindow) will define 963 // which workspace we're going to. Then, once that is determined we 964 // want to bring to the front every window of the group of teams that 965 // lives in that workspace. 966 967 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) { 968 if (!allowWorkspaceSwitch) { 969 // If the first window in the list isn't in current workspace, 970 // then none are. So we can't switch to this app. 971 ASSERT(windowInfo); 972 free(windowInfo); 973 return false; 974 } 975 int32 destWorkspace = LowBitIndex(windowInfo->workspaces); 976 // now switch to that workspace 977 activate_workspace(destWorkspace); 978 } 979 980 if (!forceShow && windowInfo->is_mini) { 981 // If the first window in the list is hidden then no windows in 982 // this group are visible. So we can't switch to this app. 983 ASSERT(windowInfo); 984 free(windowInfo); 985 return false; 986 } 987 988 int32 tokenCount; 989 int32* tokens = get_token_list(-1, &tokenCount); 990 if (tokens == NULL) { 991 ASSERT(windowInfo); 992 free(windowInfo); 993 return true; // weird error, so don't try to recover 994 } 995 996 BList windowsToActivate; 997 998 // Now we go through all the windows in the current workspace list in order. 999 // As we hit member teams we build the "activate" list. 1000 for (int32 i = 0; i < tokenCount; i++) { 1001 client_window_info* matchWindowInfo = get_window_info(tokens[i]); 1002 if (!matchWindowInfo) { 1003 // That window probably closed. Just go to the next one. 1004 continue; 1005 } 1006 if (!IsVisibleInCurrentWorkspace(matchWindowInfo)) { 1007 // first non-visible in workspace window means we're done. 1008 free(matchWindowInfo); 1009 break; 1010 } 1011 if (matchWindowInfo->server_token != windowInfo->server_token 1012 && teamGroup->TeamList()->HasItem((void*)matchWindowInfo->team)) 1013 windowsToActivate.AddItem((void*)matchWindowInfo->server_token); 1014 1015 free(matchWindowInfo); 1016 } 1017 1018 free(tokens); 1019 1020 // Want to go through the list backwards to keep windows in same relative 1021 // order. 1022 int32 i = windowsToActivate.CountItems() - 1; 1023 for (; i >= 0; i--) { 1024 int32 wid = (int32) windowsToActivate.ItemAt(i); 1025 do_window_action(wid, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false); 1026 } 1027 1028 // now bring the select window on top of everything. 1029 1030 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT, 1031 BRect(0, 0, 0, 0), false); 1032 1033 free(windowInfo); 1034 return true; 1035 } 1036 1037 1038 void 1039 TSwitchManager::QuitApp() 1040 { 1041 // check if we're in the last slot already (the last usable team group) 1042 1043 TTeamGroup* teamGroup; 1044 int32 count = 0; 1045 for (int32 i = fCurrentIndex + 1; i < fGroupList.CountItems(); i++) { 1046 teamGroup = (TTeamGroup*)fGroupList.ItemAt(i); 1047 1048 if (!OKToUse(teamGroup)) 1049 continue; 1050 1051 count++; 1052 } 1053 1054 teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 1055 1056 if (count == 0) { 1057 // switch to previous app in the list so that we don't jump to 1058 // the start of the list (try to keep the same position when 1059 // the apps at the current index go away) 1060 CycleApp(false, false); 1061 } 1062 1063 // send the quit request to all teams in this group 1064 1065 for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) { 1066 team_id team = (team_id)teamGroup->TeamList()->ItemAt(i); 1067 app_info info; 1068 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) { 1069 if (!strcasecmp(info.signature, kTrackerSignature)) { 1070 // Tracker can't be quit this way 1071 continue; 1072 } 1073 1074 BMessenger messenger(NULL, team); 1075 messenger.SendMessage(B_QUIT_REQUESTED); 1076 } 1077 } 1078 } 1079 1080 1081 void 1082 TSwitchManager::HideApp() 1083 { 1084 // hide all teams in this group 1085 1086 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 1087 1088 for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) { 1089 team_id team = (team_id)teamGroup->TeamList()->ItemAt(i); 1090 app_info info; 1091 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) 1092 do_minimize_team(BRect(), team, false); 1093 } 1094 } 1095 1096 1097 client_window_info* 1098 TSwitchManager::WindowInfo(int32 groupIndex, int32 windowIndex) 1099 { 1100 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex); 1101 if (teamGroup == NULL) 1102 return NULL; 1103 1104 int32 tokenCount; 1105 int32* tokens = get_token_list(-1, &tokenCount); 1106 if (tokens == NULL) 1107 return NULL; 1108 1109 int32 matches = 0; 1110 1111 // Want to find the "windowIndex'th" window in window order that belongs 1112 // the the specified group (groupIndex). Since multiple teams can belong to 1113 // the same group (multiple-launch apps) we get the list of _every_ 1114 // window and go from there. 1115 1116 client_window_info* result = NULL; 1117 for (int32 i = 0; i < tokenCount; i++) { 1118 client_window_info* windowInfo = get_window_info(tokens[i]); 1119 if (windowInfo) { 1120 // skip hidden/special windows 1121 if (IsWindowOK(windowInfo) 1122 && (teamGroup->TeamList()->HasItem((void*)windowInfo->team))) { 1123 // this window belongs to the team! 1124 if (matches == windowIndex) { 1125 // we found it! 1126 result = windowInfo; 1127 break; 1128 } 1129 matches++; 1130 } 1131 free(windowInfo); 1132 } 1133 // else - that window probably closed. Just go to the next one. 1134 } 1135 1136 free(tokens); 1137 1138 return result; 1139 } 1140 1141 1142 int32 1143 TSwitchManager::CountWindows(int32 groupIndex, bool ) 1144 { 1145 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex); 1146 if (!teamGroup) 1147 return 0; 1148 1149 int32 result = 0; 1150 1151 for (int32 i = 0; ; i++) { 1152 team_id teamID = (team_id)teamGroup->TeamList()->ItemAt(i); 1153 if (teamID == 0) 1154 break; 1155 1156 int32 count; 1157 int32* tokens = get_token_list(teamID, &count); 1158 if (!tokens) 1159 continue; 1160 1161 for (int32 i = 0; i < count; i++) { 1162 window_info *windowInfo = get_window_info(tokens[i]); 1163 if (windowInfo) { 1164 if (IsWindowOK(windowInfo)) 1165 result++; 1166 free(windowInfo); 1167 } 1168 } 1169 free(tokens); 1170 } 1171 1172 return result; 1173 } 1174 1175 1176 void 1177 TSwitchManager::ActivateWindow(int32 windowID) 1178 { 1179 if (windowID == -1) 1180 windowID = fWindowID; 1181 1182 do_window_action(windowID, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false); 1183 } 1184 1185 1186 void 1187 TSwitchManager::SwitchWindow(team_id team, bool, bool activate) 1188 { 1189 // Find the _last_ window in the current workspace that belongs 1190 // to the group. This is the window to activate. 1191 1192 int32 index; 1193 TTeamGroup* teamGroup = FindTeam(team, &index); 1194 1195 // cycle through the windows in the active application 1196 int32 count; 1197 int32* tokens = get_token_list(-1, &count); 1198 if (tokens == NULL) 1199 return; 1200 1201 for (int32 i = count - 1; i >= 0; i--) { 1202 client_window_info* windowInfo = get_window_info(tokens[i]); 1203 if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo) 1204 && teamGroup->TeamList()->HasItem((void*)windowInfo->team)) { 1205 fWindowID = windowInfo->server_token; 1206 if (activate) 1207 ActivateWindow(windowInfo->server_token); 1208 1209 free(windowInfo); 1210 break; 1211 } 1212 free(windowInfo); 1213 } 1214 free(tokens); 1215 } 1216 1217 1218 void 1219 TSwitchManager::Unblock() 1220 { 1221 fBlock = false; 1222 fSkipUntil = system_time(); 1223 } 1224 1225 1226 int32 1227 TSwitchManager::CurrentIndex() 1228 { 1229 return fCurrentIndex; 1230 } 1231 1232 1233 int32 1234 TSwitchManager::CurrentWindow() 1235 { 1236 return fCurrentWindow; 1237 } 1238 1239 1240 int32 1241 TSwitchManager::CurrentSlot() 1242 { 1243 return fCurrentSlot; 1244 } 1245 1246 1247 BList* 1248 TSwitchManager::GroupList() 1249 { 1250 return &fGroupList; 1251 } 1252 1253 1254 // #pragma mark - 1255 1256 1257 TBox::TBox(BRect bounds, TSwitchManager* manager, TSwitcherWindow* window, 1258 TIconView* iconView) 1259 : 1260 BBox(bounds, "top", B_FOLLOW_NONE, B_WILL_DRAW, B_NO_BORDER), 1261 fManager(manager), 1262 fWindow(window), 1263 fIconView(iconView), 1264 fLeftScroller(false), 1265 fRightScroller(false) 1266 { 1267 } 1268 1269 1270 void 1271 TBox::AllAttached() 1272 { 1273 BRect centerRect(kCenterSlot * kSlotSize, 0, 1274 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1275 BRect frame = fIconView->Frame(); 1276 1277 // scroll the centerRect to correct location 1278 centerRect.OffsetBy(frame.left, frame.top); 1279 1280 // switch to local coords 1281 fIconView->ConvertToParent(¢erRect); 1282 1283 fCenter = centerRect; 1284 } 1285 1286 1287 void 1288 TBox::MouseDown(BPoint where) 1289 { 1290 if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller) 1291 return; 1292 1293 BRect frame = fIconView->Frame(); 1294 BRect bounds = Bounds(); 1295 1296 if (fLeftScroller) { 1297 BRect lhit(0, frame.top, frame.left, frame.bottom); 1298 if (lhit.Contains(where)) { 1299 // Want to scroll by NUMSLOTS-1 slots 1300 int32 previousIndex = fManager->CurrentIndex(); 1301 int32 previousSlot = fManager->CurrentSlot(); 1302 int32 newSlot = previousSlot - (kNumSlots - 1); 1303 if (newSlot < 0) 1304 newSlot = 0; 1305 int32 newIndex = fIconView->IndexAt(newSlot); 1306 1307 fManager->SwitchToApp(previousIndex, newIndex, false); 1308 } 1309 } 1310 1311 if (fRightScroller) { 1312 BRect rhit(frame.right, frame.top, bounds.right, frame.bottom); 1313 if (rhit.Contains(where)) { 1314 // Want to scroll by NUMSLOTS-1 slots 1315 int32 previousIndex = fManager->CurrentIndex(); 1316 int32 previousSlot = fManager->CurrentSlot(); 1317 int32 newSlot = previousSlot + (kNumSlots-1); 1318 int32 newIndex = fIconView->IndexAt(newSlot); 1319 1320 if (newIndex < 0) { 1321 // don't have a page full to scroll 1322 int32 valid = fManager->CountVisibleGroups(); 1323 newIndex = fIconView->IndexAt(valid-1); 1324 } 1325 fManager->SwitchToApp(previousIndex, newIndex, true); 1326 } 1327 } 1328 1329 frame = fWindow->WindowView()->Frame(); 1330 if (fUpScroller) { 1331 BRect hit1(frame.left - 10, frame.top, frame.left, 1332 (frame.top+frame.bottom)/2); 1333 BRect hit2(frame.right, frame.top, frame.right + 10, 1334 (frame.top+frame.bottom)/2); 1335 if (hit1.Contains(where) || hit2.Contains(where)) { 1336 // Want to scroll up 1 window 1337 fManager->CycleWindow(false, false); 1338 } 1339 } 1340 1341 if (fDownScroller) { 1342 BRect hit1(frame.left - 10, (frame.top+frame.bottom) / 2, frame.left, 1343 frame.bottom); 1344 BRect hit2(frame.right, (frame.top+frame.bottom) / 2, frame.right + 10, 1345 frame.bottom); 1346 if (hit1.Contains(where) || hit2.Contains(where)) { 1347 // Want to scroll down 1 window 1348 fManager->CycleWindow(true, false); 1349 } 1350 } 1351 } 1352 1353 1354 void 1355 TBox::Draw(BRect update) 1356 { 1357 static const int32 kChildInset = 7; 1358 static const int32 kWedge = 6; 1359 1360 BBox::Draw(update); 1361 1362 // The fancy border around the icon view 1363 1364 BRect bounds = Bounds(); 1365 float height = fIconView->Bounds().Height(); 1366 float center = (bounds.right + bounds.left) / 2; 1367 1368 BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2); 1369 rgb_color white = {255, 255, 255, 255}; 1370 rgb_color standardGray = ui_color(B_PANEL_BACKGROUND_COLOR); 1371 rgb_color veryDarkGray = {128, 128, 128, 255}; 1372 rgb_color darkGray = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1373 B_DARKEN_1_TINT); 1374 1375 // Fill the area with dark gray 1376 SetHighColor(darkGray); 1377 box.InsetBy(1,1); 1378 FillRect(box); 1379 1380 box.InsetBy(-1,-1); 1381 1382 BeginLineArray(50); 1383 1384 // The main frame around the icon view 1385 AddLine(box.LeftTop(), BPoint(center-kWedge, box.top), veryDarkGray); 1386 AddLine(BPoint(center+kWedge, box.top), box.RightTop(), veryDarkGray); 1387 1388 AddLine(box.LeftBottom(), BPoint(center-kWedge, box.bottom), veryDarkGray); 1389 AddLine(BPoint(center+kWedge, box.bottom), box.RightBottom(), veryDarkGray); 1390 AddLine(box.LeftBottom() + BPoint(1, 1), 1391 BPoint(center-kWedge, box.bottom + 1), white); 1392 AddLine(BPoint(center+kWedge, box.bottom) + BPoint(0, 1), 1393 box.RightBottom() + BPoint(1, 1), white); 1394 1395 AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray); 1396 AddLine(box.RightTop(), box.RightBottom(), veryDarkGray); 1397 AddLine(box.RightTop() + BPoint(1, 1), 1398 box.RightBottom() + BPoint(1, 1), white); 1399 1400 // downward pointing area at top of frame 1401 BPoint point(center - kWedge, box.top); 1402 AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray); 1403 AddLine(point + BPoint(kWedge, kWedge), 1404 BPoint(center+kWedge, point.y), veryDarkGray); 1405 1406 AddLine(point + BPoint(1, 0), 1407 point + BPoint(1, 0) + BPoint(kWedge - 1, kWedge - 1), white); 1408 1409 AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1), 1410 BPoint(center+kWedge-1, point.y), darkGray); 1411 1412 BPoint topPoint = point; 1413 1414 // upward pointing area at bottom of frame 1415 point.y = box.bottom; 1416 point.x = center - kWedge; 1417 AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray); 1418 AddLine(point + BPoint(kWedge, -kWedge), 1419 BPoint(center+kWedge, point.y), veryDarkGray); 1420 1421 AddLine(point + BPoint(1, 0), 1422 point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white); 1423 1424 AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)), 1425 BPoint(center + kWedge - 1, point.y), darkGray); 1426 1427 BPoint bottomPoint = point; 1428 1429 EndLineArray(); 1430 1431 // fill the downward pointing arrow area 1432 SetHighColor(standardGray); 1433 FillTriangle(topPoint + BPoint(2, 0), 1434 topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2), 1435 BPoint(center + kWedge - 2, topPoint.y)); 1436 1437 // fill the upward pointing arrow area 1438 SetHighColor(standardGray); 1439 FillTriangle(bottomPoint + BPoint(2,0), 1440 bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)), 1441 BPoint(center + kWedge - 2, bottomPoint.y)); 1442 1443 DrawIconScrollers(false); 1444 DrawWindowScrollers(false); 1445 1446 } 1447 1448 1449 void 1450 TBox::DrawIconScrollers(bool force) 1451 { 1452 rgb_color backgroundColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1453 B_DARKEN_1_TINT); 1454 rgb_color dark = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1455 B_DARKEN_4_TINT); 1456 bool updateLeft = false; 1457 bool updateRight = false; 1458 1459 BRect rect = fIconView->Bounds(); 1460 if (rect.left > (kSlotSize * kCenterSlot)) { 1461 updateLeft = true; 1462 fLeftScroller = true; 1463 } else { 1464 fLeftScroller = false; 1465 if (force) 1466 updateLeft = true; 1467 } 1468 1469 int32 maxIndex = fManager->GroupList()->CountItems() - 1; 1470 // last_frame is in fIconView coordinate space 1471 BRect lastFrame = fIconView->FrameOf(maxIndex); 1472 1473 if (lastFrame.right > rect.right) { 1474 updateRight = true; 1475 fRightScroller = true; 1476 } else { 1477 fRightScroller = false; 1478 if (force) 1479 updateRight = true; 1480 } 1481 1482 PushState(); 1483 SetDrawingMode(B_OP_COPY); 1484 1485 rect = fIconView->Frame(); 1486 if (updateLeft) { 1487 BPoint pt1, pt2, pt3; 1488 pt1.x = rect.left - 5; 1489 pt1.y = floorf((rect.bottom + rect.top) / 2); 1490 pt2.x = pt3.x = pt1.x + 3; 1491 pt2.y = pt1.y - 3; 1492 pt3.y = pt1.y + 3; 1493 1494 if (fLeftScroller) { 1495 SetHighColor(dark); 1496 FillTriangle(pt1, pt2, pt3); 1497 } else if (force) { 1498 SetHighColor(backgroundColor); 1499 FillRect(BRect(pt1.x, pt2.y, pt3.x, pt3.y)); 1500 } 1501 } 1502 if (updateRight) { 1503 BPoint pt1, pt2, pt3; 1504 pt1.x = rect.right + 4; 1505 pt1.y = rintf((rect.bottom + rect.top) / 2); 1506 pt2.x = pt3.x = pt1.x - 4; 1507 pt2.y = pt1.y - 4; 1508 pt3.y = pt1.y + 4; 1509 1510 if (fRightScroller) { 1511 SetHighColor(dark); 1512 FillTriangle(pt1, pt2, pt3); 1513 } else if (force) { 1514 SetHighColor(backgroundColor); 1515 FillRect(BRect(pt3.x, pt2.y, pt1.x, pt3.y)); 1516 } 1517 } 1518 1519 PopState(); 1520 } 1521 1522 1523 void 1524 TBox::DrawWindowScrollers(bool force) 1525 { 1526 rgb_color backgroundColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1527 rgb_color dark = tint_color(backgroundColor, B_DARKEN_4_TINT); 1528 bool updateUp = false; 1529 bool updateDown = false; 1530 1531 BRect rect = fWindow->WindowView()->Bounds(); 1532 if (rect.top != 0) { 1533 updateUp = true; 1534 fUpScroller = true; 1535 } else { 1536 fUpScroller = false; 1537 if (force) 1538 updateUp = true; 1539 } 1540 1541 int32 groupIndex = fManager->CurrentIndex(); 1542 int32 maxIndex = fManager->CountWindows(groupIndex) - 1; 1543 1544 BRect lastFrame(0, 0, 0, 0); 1545 if (maxIndex >= 0) 1546 lastFrame = fWindow->WindowView()->FrameOf(maxIndex); 1547 1548 if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) { 1549 updateDown = true; 1550 fDownScroller = true; 1551 } else { 1552 fDownScroller = false; 1553 if (force) 1554 updateDown = true; 1555 } 1556 1557 PushState(); 1558 SetDrawingMode(B_OP_COPY); 1559 1560 rect = fWindow->WindowView()->Frame(); 1561 rect.InsetBy(-3, 0); 1562 if (updateUp) { 1563 if (fUpScroller) { 1564 SetHighColor(dark); 1565 BPoint pt1, pt2, pt3; 1566 pt1.x = rect.left - 6; 1567 pt1.y = rect.top + 3; 1568 pt2.y = pt3.y = pt1.y + 4; 1569 pt2.x = pt1.x - 4; 1570 pt3.x = pt1.x + 4; 1571 FillTriangle(pt1, pt2, pt3); 1572 1573 pt1.x += rect.Width() + 12; 1574 pt2.x += rect.Width() + 12; 1575 pt3.x += rect.Width() + 12; 1576 FillTriangle(pt1, pt2, pt3); 1577 } else if (force) { 1578 FillRect(BRect(rect.left - 10, rect.top + 3, rect.left - 2, 1579 rect.top + 7), B_SOLID_LOW); 1580 FillRect(BRect(rect.right + 2, rect.top + 3, rect.right + 10, 1581 rect.top + 7), B_SOLID_LOW); 1582 } 1583 } 1584 if (updateDown) { 1585 if (fDownScroller) { 1586 SetHighColor(dark); 1587 BPoint pt1, pt2, pt3; 1588 pt1.x = rect.left - 6; 1589 pt1.y = rect.bottom - 3; 1590 pt2.y = pt3.y = pt1.y - 4; 1591 pt2.x = pt1.x - 4; 1592 pt3.x = pt1.x + 4; 1593 FillTriangle(pt1, pt2, pt3); 1594 1595 pt1.x += rect.Width() + 12; 1596 pt2.x += rect.Width() + 12; 1597 pt3.x += rect.Width() + 12; 1598 FillTriangle(pt1, pt2, pt3); 1599 } else if (force) { 1600 FillRect(BRect(rect.left - 10, rect.bottom - 7, rect.left - 2, 1601 rect.bottom - 3), B_SOLID_LOW); 1602 FillRect(BRect(rect.right + 2, rect.bottom - 7, rect.right + 10, 1603 rect.bottom - 3), B_SOLID_LOW); 1604 } 1605 } 1606 1607 PopState(); 1608 Sync(); 1609 } 1610 1611 1612 // #pragma mark - 1613 1614 1615 TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager* manager) 1616 : 1617 BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL, 1618 B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES), 1619 fManager(manager), 1620 fHairTrigger(true) 1621 { 1622 BRect rect = frame; 1623 rect.OffsetTo(B_ORIGIN); 1624 rect.InsetBy(kHorizontalMargin, 0); 1625 rect.top = kVerticalMargin; 1626 rect.bottom = rect.top + kSlotSize - 1; 1627 1628 fIconView = new TIconView(rect, manager, this); 1629 1630 rect.top = rect.bottom + (kVerticalMargin * 1 + 4); 1631 rect.InsetBy(9, 0); 1632 1633 fWindowView = new TWindowView(rect, manager, this); 1634 fWindowView->ResizeToPreferred(); 1635 1636 fTopView = new TBox(Bounds(), fManager, this, fIconView); 1637 AddChild(fTopView); 1638 1639 SetPulseRate(0); 1640 fTopView->AddChild(fIconView); 1641 fTopView->AddChild(fWindowView); 1642 } 1643 1644 1645 TSwitcherWindow::~TSwitcherWindow() 1646 { 1647 } 1648 1649 1650 void 1651 TSwitcherWindow::MessageReceived(BMessage* message) 1652 { 1653 switch (message->what) { 1654 case B_UNMAPPED_KEY_DOWN: 1655 case B_KEY_DOWN: 1656 { 1657 int32 repeats = 0; 1658 if (message->FindInt32("be:key_repeat", &repeats) == B_OK 1659 && (fSkipKeyRepeats || (repeats % 6) != 0)) 1660 break; 1661 1662 // The first actual key press let's us listening to repeated keys 1663 fSkipKeyRepeats = false; 1664 1665 uint32 rawChar; 1666 uint32 modifiers; 1667 message->FindInt32("raw_char", 0, (int32*)&rawChar); 1668 message->FindInt32("modifiers", 0, (int32*)&modifiers); 1669 DoKey(rawChar, modifiers); 1670 break; 1671 } 1672 1673 default: 1674 BWindow::MessageReceived(message); 1675 } 1676 } 1677 1678 1679 void 1680 TSwitcherWindow::Redraw(int32 index) 1681 { 1682 BRect frame = fIconView->FrameOf(index); 1683 frame.right = fIconView->Bounds().right; 1684 fIconView->Invalidate(frame); 1685 } 1686 1687 1688 void 1689 TSwitcherWindow::DoKey(uint32 key, uint32 modifiers) 1690 { 1691 bool forward = ((modifiers & B_SHIFT_KEY) == 0); 1692 1693 switch (key) { 1694 case B_RIGHT_ARROW: 1695 fManager->CycleApp(true, false); 1696 break; 1697 1698 case B_LEFT_ARROW: 1699 case '1': 1700 fManager->CycleApp(false, false); 1701 break; 1702 1703 case B_UP_ARROW: 1704 fManager->CycleWindow(false, false); 1705 break; 1706 1707 case B_DOWN_ARROW: 1708 fManager->CycleWindow(true, false); 1709 break; 1710 1711 case B_TAB: 1712 fManager->CycleApp(forward, false); 1713 break; 1714 1715 case B_ESCAPE: 1716 fManager->Stop(false, 0); 1717 break; 1718 1719 case B_SPACE: 1720 case B_ENTER: 1721 fManager->Stop(true, modifiers); 1722 break; 1723 1724 case 'q': 1725 case 'Q': 1726 fManager->QuitApp(); 1727 break; 1728 1729 case 'h': 1730 case 'H': 1731 fManager->HideApp(); 1732 break; 1733 1734 #if _ALLOW_STICKY_ 1735 case 's': 1736 case 'S': 1737 if (fHairTrigger) { 1738 SetLook(B_TITLED_WINDOW_LOOK); 1739 fHairTrigger = false; 1740 } else { 1741 SetLook(B_MODAL_WINDOW_LOOK); 1742 fHairTrigger = true; 1743 } 1744 break; 1745 #endif 1746 } 1747 } 1748 1749 1750 bool 1751 TSwitcherWindow::QuitRequested() 1752 { 1753 ((TBarApp*)be_app)->Settings()->switcherLoc = Frame().LeftTop(); 1754 fManager->Stop(false, 0); 1755 return false; 1756 } 1757 1758 1759 void 1760 TSwitcherWindow::WindowActivated(bool state) 1761 { 1762 if (state) 1763 fManager->Unblock(); 1764 } 1765 1766 1767 void 1768 TSwitcherWindow::Update(int32 prev, int32 current, int32 previousSlot, 1769 int32 currentSlot, bool forward) 1770 { 1771 if (!IsHidden()) 1772 fIconView->Update(prev, current, previousSlot, currentSlot, forward); 1773 else 1774 fIconView->CenterOn(current); 1775 1776 fWindowView->UpdateGroup(current, 0); 1777 } 1778 1779 1780 void 1781 TSwitcherWindow::Hide() 1782 { 1783 fIconView->Hiding(); 1784 SetPulseRate(0); 1785 BWindow::Hide(); 1786 } 1787 1788 1789 void 1790 TSwitcherWindow::Show() 1791 { 1792 fHairTrigger = true; 1793 fSkipKeyRepeats = true; 1794 fIconView->Showing(); 1795 SetPulseRate(100000); 1796 SetLook(B_MODAL_WINDOW_LOOK); 1797 BWindow::Show(); 1798 } 1799 1800 1801 TBox* 1802 TSwitcherWindow::TopView() 1803 { 1804 return fTopView; 1805 } 1806 1807 1808 bool 1809 TSwitcherWindow::HairTrigger() 1810 { 1811 return fHairTrigger; 1812 } 1813 1814 1815 inline int32 1816 TSwitcherWindow::SlotOf(int32 i) 1817 { 1818 return fIconView->SlotOf(i); 1819 } 1820 1821 1822 inline TIconView* 1823 TSwitcherWindow::IconView() 1824 { 1825 return fIconView; 1826 } 1827 1828 1829 inline TWindowView* 1830 TSwitcherWindow::WindowView() 1831 { 1832 return fWindowView; 1833 } 1834 1835 1836 // #pragma mark - 1837 1838 1839 TIconView::TIconView(BRect frame, TSwitchManager* manager, 1840 TSwitcherWindow* switcherWindow) 1841 : BView(frame, "main_view", B_FOLLOW_NONE, 1842 B_WILL_DRAW | B_PULSE_NEEDED), 1843 fAutoScrolling(false), 1844 fSwitcher(switcherWindow), 1845 fManager(manager) 1846 { 1847 BRect rect(0, 0, kSlotSize - 1, kSlotSize - 1); 1848 rgb_color color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 1849 B_DARKEN_1_TINT); 1850 1851 fOffView = new BView(rect, "off_view", B_FOLLOW_NONE, B_WILL_DRAW); 1852 fOffView->SetHighColor(color); 1853 fOffBitmap = new BBitmap(rect, B_RGB32, true); 1854 fOffBitmap->AddChild(fOffView); 1855 1856 fCurrentSmall = new BBitmap(BRect(0, 0, 15, 15), kIconFormat); 1857 fCurrentLarge = new BBitmap(BRect(0, 0, 31, 31), kIconFormat); 1858 1859 SetViewColor(color); 1860 SetLowColor(color); 1861 } 1862 1863 1864 TIconView::~TIconView() 1865 { 1866 delete fCurrentSmall; 1867 delete fCurrentLarge; 1868 delete fOffBitmap; 1869 } 1870 1871 1872 void 1873 TIconView::KeyDown(const char* /*bytes*/, int32 /*numBytes*/) 1874 { 1875 } 1876 1877 1878 void 1879 TIconView::CacheIcons(TTeamGroup* teamGroup) 1880 { 1881 const BBitmap* bitmap = teamGroup->SmallIcon(); 1882 ASSERT(bitmap); 1883 fCurrentSmall->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1884 bitmap->ColorSpace()); 1885 1886 bitmap = teamGroup->LargeIcon(); 1887 ASSERT(bitmap); 1888 fCurrentLarge->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1889 bitmap->ColorSpace()); 1890 } 1891 1892 1893 void 1894 TIconView::AnimateIcon(BBitmap* startIcon, BBitmap* endIcon) 1895 { 1896 BRect centerRect(kCenterSlot*kSlotSize, 0, 1897 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1898 BRect startIconBounds = startIcon->Bounds(); 1899 BRect bounds = Bounds(); 1900 float width = startIconBounds.Width(); 1901 int32 amount = (width < 20) ? -2 : 2; 1902 1903 // center the starting icon inside of centerRect 1904 float off = (centerRect.Width() - width) / 2; 1905 startIconBounds.OffsetTo(BPoint(off, off)); 1906 1907 // scroll the centerRect to correct location 1908 centerRect.OffsetBy(bounds.left, 0); 1909 1910 BRect destRect = fOffBitmap->Bounds(); 1911 // scroll to the centerRect location 1912 destRect.OffsetTo(centerRect.left, 0); 1913 // center the destRect inside of centerRect. 1914 off = (centerRect.Width() - destRect.Width()) / 2; 1915 destRect.OffsetBy(BPoint(off, off)); 1916 1917 fOffBitmap->Lock(); 1918 1919 for (int i = 0; i < 2; i++) { 1920 startIconBounds.InsetBy(amount,amount); 1921 snooze(20000); 1922 fOffView->SetDrawingMode(B_OP_COPY); 1923 fOffView->FillRect(fOffView->Bounds()); 1924 fOffView->SetDrawingMode(B_OP_ALPHA); 1925 fOffView->DrawBitmap(startIcon, startIconBounds); 1926 fOffView->Sync(); 1927 DrawBitmap(fOffBitmap, destRect); 1928 } 1929 for (int i = 0; i < 2; i++) { 1930 startIconBounds.InsetBy(amount,amount); 1931 snooze(20000); 1932 fOffView->SetDrawingMode(B_OP_COPY); 1933 fOffView->FillRect(fOffView->Bounds()); 1934 fOffView->SetDrawingMode(B_OP_ALPHA); 1935 fOffView->DrawBitmap(endIcon, startIconBounds); 1936 fOffView->Sync(); 1937 DrawBitmap(fOffBitmap, destRect); 1938 } 1939 1940 fOffBitmap->Unlock(); 1941 } 1942 1943 1944 void 1945 TIconView::Update(int32, int32 current, int32 previousSlot, int32 currentSlot, 1946 bool forward) 1947 { 1948 // Animate the shrinking of the currently centered icon. 1949 AnimateIcon(fCurrentLarge, fCurrentSmall); 1950 1951 int32 nslots = abs(previousSlot - currentSlot); 1952 int32 stepSize = kScrollStep; 1953 1954 if (forward && (currentSlot < previousSlot)) { 1955 // we were at the end of the list and we just moved to the start 1956 forward = false; 1957 if (previousSlot - currentSlot > 4) 1958 stepSize *= 2; 1959 } else if (!forward && (currentSlot > previousSlot)) { 1960 // we're are moving backwards and we just hit start of list and 1961 // we wrapped to the end. 1962 forward = true; 1963 if (currentSlot - previousSlot > 4) 1964 stepSize *= 2; 1965 } 1966 1967 int32 scrollValue = forward ? stepSize : -stepSize; 1968 int32 total = 0; 1969 1970 fAutoScrolling = true; 1971 while (total < (nslots * kSlotSize)) { 1972 ScrollBy(scrollValue, 0); 1973 snooze(1000); 1974 total += stepSize; 1975 Window()->UpdateIfNeeded(); 1976 } 1977 fAutoScrolling = false; 1978 1979 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(current); 1980 ASSERT(teamGroup); 1981 CacheIcons(teamGroup); 1982 1983 // Animate the expansion of the currently centered icon 1984 AnimateIcon(fCurrentSmall, fCurrentLarge); 1985 } 1986 1987 1988 void 1989 TIconView::CenterOn(int32 index) 1990 { 1991 BRect rect = FrameOf(index); 1992 ScrollTo(rect.left - (kCenterSlot * kSlotSize), 0); 1993 } 1994 1995 1996 int32 1997 TIconView::ItemAtPoint(BPoint point) const 1998 { 1999 float tmpPointVerticalIndex = (point.x / kSlotSize) - kCenterSlot; 2000 if (tmpPointVerticalIndex < 0) 2001 return -1; 2002 2003 int32 pointVerticalIndex = (int32)tmpPointVerticalIndex; 2004 2005 for (int32 i = 0, verticalIndex = 0; ; i++) { 2006 2007 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(i); 2008 if (teamGroup == NULL) 2009 break; 2010 2011 if (!OKToUse(teamGroup)) 2012 continue; 2013 2014 if (verticalIndex == pointVerticalIndex) 2015 return i; 2016 2017 verticalIndex++; 2018 } 2019 return -1; 2020 } 2021 2022 2023 void 2024 TIconView::ScrollTo(BPoint where) 2025 { 2026 BView::ScrollTo(where); 2027 fSwitcher->TopView()->DrawIconScrollers(true); 2028 } 2029 2030 2031 int32 2032 TIconView::IndexAt(int32 slot) const 2033 { 2034 BList* list = fManager->GroupList(); 2035 int32 count = list->CountItems(); 2036 int32 slotIndex = 0; 2037 2038 for (int32 i = 0; i < count; i++) { 2039 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2040 2041 if (!OKToUse(teamGroup)) 2042 continue; 2043 2044 if (slotIndex == slot) { 2045 return i; 2046 } 2047 slotIndex++; 2048 } 2049 return -1; 2050 } 2051 2052 2053 int32 2054 TIconView::SlotOf(int32 index) const 2055 { 2056 BRect rect = FrameOf(index); 2057 return (int32)(rect.left / kSlotSize) - kCenterSlot; 2058 } 2059 2060 2061 BRect 2062 TIconView::FrameOf(int32 index) const 2063 { 2064 BList* list = fManager->GroupList(); 2065 int32 visible = kCenterSlot - 1; 2066 // first few slots in view are empty 2067 2068 TTeamGroup* teamGroup; 2069 for (int32 i = 0; i <= index; i++) { 2070 teamGroup = (TTeamGroup*)list->ItemAt(i); 2071 2072 if (!OKToUse(teamGroup)) 2073 continue; 2074 2075 visible++; 2076 } 2077 2078 return BRect(visible * kSlotSize, 0, (visible + 1) * kSlotSize - 1, 2079 kSlotSize - 1); 2080 } 2081 2082 2083 void 2084 TIconView::DrawTeams(BRect update) 2085 { 2086 int32 mainIndex = fManager->CurrentIndex(); 2087 BList* list = fManager->GroupList(); 2088 int32 count = list->CountItems(); 2089 2090 BRect rect(kCenterSlot * kSlotSize, 0, 2091 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 2092 2093 for (int32 i = 0; i < count; i++) { 2094 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2095 2096 if (!OKToUse(teamGroup)) 2097 continue; 2098 2099 if (rect.Intersects(update) && teamGroup) { 2100 SetDrawingMode(B_OP_ALPHA); 2101 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 2102 2103 teamGroup->Draw(this, rect, !fAutoScrolling && (i == mainIndex)); 2104 2105 if (i == mainIndex) 2106 CacheIcons(teamGroup); 2107 2108 SetDrawingMode(B_OP_COPY); 2109 } 2110 rect.OffsetBy(kSlotSize,0); 2111 } 2112 } 2113 2114 2115 void 2116 TIconView::Draw(BRect update) 2117 { 2118 DrawTeams(update); 2119 } 2120 2121 2122 void 2123 TIconView::MouseDown(BPoint where) 2124 { 2125 int32 index = ItemAtPoint(where); 2126 if (index >= 0) { 2127 int32 previousIndex = fManager->CurrentIndex(); 2128 int32 previousSlot = fManager->CurrentSlot(); 2129 int32 currentSlot = SlotOf(index); 2130 fManager->SwitchToApp(previousIndex, index, (currentSlot 2131 > previousSlot)); 2132 } 2133 } 2134 2135 2136 void 2137 TIconView::Pulse() 2138 { 2139 uint32 modifiersKeys = modifiers(); 2140 if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) { 2141 fManager->Stop(true, modifiersKeys); 2142 return; 2143 } 2144 2145 if (!fSwitcher->HairTrigger()) { 2146 uint32 buttons; 2147 BPoint point; 2148 GetMouse(&point, &buttons); 2149 if (buttons != 0) { 2150 point = ConvertToScreen(point); 2151 if (!Window()->Frame().Contains(point)) 2152 fManager->Stop(false, 0); 2153 } 2154 } 2155 } 2156 2157 2158 void 2159 TIconView::Showing() 2160 { 2161 } 2162 2163 2164 void 2165 TIconView::Hiding() 2166 { 2167 ScrollTo(B_ORIGIN); 2168 } 2169 2170 2171 // #pragma mark - 2172 2173 2174 TWindowView::TWindowView(BRect rect, TSwitchManager* manager, 2175 TSwitcherWindow* window) 2176 : BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), 2177 fCurrentToken(-1), 2178 fSwitcher(window), 2179 fManager(manager) 2180 { 2181 SetFont(be_plain_font); 2182 } 2183 2184 2185 void 2186 TWindowView::AttachedToWindow() 2187 { 2188 if (Parent()) 2189 SetViewColor(Parent()->ViewColor()); 2190 else 2191 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2192 } 2193 2194 2195 void 2196 TWindowView::ScrollTo(BPoint where) 2197 { 2198 BView::ScrollTo(where); 2199 fSwitcher->TopView()->DrawWindowScrollers(true); 2200 } 2201 2202 2203 2204 BRect 2205 TWindowView::FrameOf(int32 index) const 2206 { 2207 return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1); 2208 } 2209 2210 2211 void 2212 TWindowView::GetPreferredSize(float* _width, float* _height) 2213 { 2214 font_height fh; 2215 be_plain_font->GetHeight(&fh); 2216 fItemHeight = (int32) fh.ascent + fh.descent; 2217 2218 // top & bottom margin 2219 fItemHeight = fItemHeight + 3 + 3; 2220 2221 // want fItemHeight to be divisible by kWindowScrollSteps. 2222 fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps) 2223 / kWindowScrollSteps) * kWindowScrollSteps; 2224 2225 *_height = fItemHeight; 2226 2227 // leave width alone 2228 *_width = Bounds().Width(); 2229 } 2230 2231 2232 void 2233 TWindowView::ShowIndex(int32 newIndex) 2234 { 2235 // convert index to scroll location 2236 BPoint point(0, newIndex * fItemHeight); 2237 BRect bounds = Bounds(); 2238 2239 int32 groupIndex = fManager->CurrentIndex(); 2240 TTeamGroup* teamGroup 2241 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2242 if (teamGroup == NULL) 2243 return; 2244 2245 window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex); 2246 if (windowInfo == NULL) 2247 return; 2248 2249 fCurrentToken = windowInfo->server_token; 2250 free(windowInfo); 2251 2252 if (bounds.top == point.y) 2253 return; 2254 2255 int32 oldIndex = (int32) (bounds.top / fItemHeight); 2256 2257 int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps); 2258 int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize; 2259 int32 total = 0; 2260 int32 nslots = abs(newIndex - oldIndex); 2261 2262 while (total < (nslots * (int32)fItemHeight)) { 2263 ScrollBy(0, scrollValue); 2264 snooze(10000); 2265 total += stepSize; 2266 Window()->UpdateIfNeeded(); 2267 } 2268 } 2269 2270 2271 void 2272 TWindowView::Draw(BRect update) 2273 { 2274 int32 groupIndex = fManager->CurrentIndex(); 2275 TTeamGroup* teamGroup 2276 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2277 if (teamGroup == NULL) 2278 return; 2279 2280 BRect bounds = Bounds(); 2281 int32 windowIndex = (int32) (bounds.top / fItemHeight); 2282 BRect windowRect = bounds; 2283 2284 windowRect.top = windowIndex * fItemHeight; 2285 windowRect.bottom = (windowIndex + 1) * fItemHeight - 1; 2286 2287 for (int32 i = 0; i < 3; i++) { 2288 if (!update.Intersects(windowRect)) { 2289 windowIndex++; 2290 windowRect.OffsetBy(0, fItemHeight); 2291 continue; 2292 } 2293 2294 // is window in current workspace? 2295 2296 bool local = true; 2297 bool minimized = false; 2298 BString title; 2299 2300 client_window_info* windowInfo 2301 = fManager->WindowInfo(groupIndex, windowIndex); 2302 if (windowInfo != NULL) { 2303 if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0) 2304 title << teamGroup->Name() << ": " << windowInfo->name; 2305 else 2306 title = teamGroup->Name(); 2307 2308 int32 currentWorkspace = current_workspace(); 2309 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) 2310 local = false; 2311 2312 minimized = windowInfo->is_mini; 2313 free(windowInfo); 2314 } else 2315 title = teamGroup->Name(); 2316 2317 if (!title.Length()) 2318 return; 2319 2320 float stringWidth = StringWidth(title.String()); 2321 float maxWidth = bounds.Width() - (14 + 5); 2322 2323 if (stringWidth > maxWidth) { 2324 // window title is too long, need to truncate 2325 TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth); 2326 stringWidth = maxWidth; 2327 } 2328 2329 BPoint point((bounds.Width() - (stringWidth + 14 + 5)) / 2, 2330 windowRect.bottom - 4); 2331 BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2); 2332 SetDrawingMode(B_OP_OVER); 2333 const BBitmap* bitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE, 2334 minimized ? R_WindowHiddenIcon : R_WindowShownIcon); 2335 p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2; 2336 DrawBitmap(bitmap, p); 2337 2338 if (!local) { 2339 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 2340 B_DARKEN_4_TINT)); 2341 p.x -= 8; 2342 p.y += 4; 2343 StrokeLine(p + BPoint(2, 2), p + BPoint(2, 2)); 2344 StrokeLine(p + BPoint(4, 2), p + BPoint(6, 2)); 2345 2346 StrokeLine(p + BPoint(0, 5), p + BPoint(0, 5)); 2347 StrokeLine(p + BPoint(2, 5), p + BPoint(6, 5)); 2348 2349 StrokeLine(p + BPoint(1, 8), p + BPoint(1, 8)); 2350 StrokeLine(p + BPoint(3, 8), p + BPoint(6, 8)); 2351 2352 SetHighColor(0, 0, 0); 2353 } 2354 2355 point.x += 21; 2356 MovePenTo(point); 2357 2358 DrawString(title.String()); 2359 SetDrawingMode(B_OP_COPY); 2360 2361 windowIndex++; 2362 windowRect.OffsetBy(0, fItemHeight); 2363 } 2364 } 2365 2366 2367 void 2368 TWindowView::UpdateGroup(int32 , int32 windowIndex) 2369 { 2370 ScrollTo(0, windowIndex * fItemHeight); 2371 Invalidate(Bounds()); 2372 } 2373 2374 2375 void 2376 TWindowView::Pulse() 2377 { 2378 // If selected window went away then reset to first window 2379 window_info *windowInfo = get_window_info(fCurrentToken); 2380 if (windowInfo == NULL) { 2381 Invalidate(); 2382 ShowIndex(0); 2383 } else 2384 free(windowInfo); 2385 } 2386 2387