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