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