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 == NULL) 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 if (teamGroup == NULL) 1150 return; 1151 1152 // cycle through the windows in the active application 1153 int32 count; 1154 int32* tokens = get_token_list(-1, &count); 1155 if (tokens == NULL) 1156 return; 1157 1158 for (int32 i = count - 1; i >= 0; i--) { 1159 client_window_info* windowInfo = get_window_info(tokens[i]); 1160 if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo) 1161 && teamGroup->TeamList()->HasItem((void*)(addr_t)windowInfo->team)) { 1162 fWindowID = windowInfo->server_token; 1163 if (activate) 1164 ActivateWindow(windowInfo->server_token); 1165 1166 free(windowInfo); 1167 break; 1168 } 1169 free(windowInfo); 1170 } 1171 free(tokens); 1172 } 1173 1174 1175 void 1176 TSwitchManager::Unblock() 1177 { 1178 fBlock = false; 1179 fSkipUntil = system_time(); 1180 } 1181 1182 1183 int32 1184 TSwitchManager::CurrentIndex() 1185 { 1186 return fCurrentIndex; 1187 } 1188 1189 1190 int32 1191 TSwitchManager::CurrentWindow() 1192 { 1193 return fCurrentWindow; 1194 } 1195 1196 1197 int32 1198 TSwitchManager::CurrentSlot() 1199 { 1200 return fCurrentSlot; 1201 } 1202 1203 1204 BList* 1205 TSwitchManager::GroupList() 1206 { 1207 return &fGroupList; 1208 } 1209 1210 1211 // #pragma mark - 1212 1213 1214 TBox::TBox(BRect bounds, TSwitchManager* manager, TSwitcherWindow* window, 1215 TIconView* iconView) 1216 : 1217 BBox(bounds, "top", B_FOLLOW_ALL, B_WILL_DRAW, B_NO_BORDER), 1218 fManager(manager), 1219 fWindow(window), 1220 fIconView(iconView), 1221 fLeftScroller(false), 1222 fRightScroller(false), 1223 fUpScroller(false), 1224 fDownScroller(false) 1225 { 1226 } 1227 1228 1229 void 1230 TBox::AllAttached() 1231 { 1232 BRect centerRect(kCenterSlot * kSlotSize, 0, 1233 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1234 BRect frame = fIconView->Frame(); 1235 1236 // scroll the centerRect to correct location 1237 centerRect.OffsetBy(frame.left, frame.top); 1238 1239 // switch to local coords 1240 fIconView->ConvertToParent(¢erRect); 1241 1242 fCenter = centerRect; 1243 } 1244 1245 1246 void 1247 TBox::MouseDown(BPoint where) 1248 { 1249 if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller) 1250 return; 1251 1252 BRect frame = fIconView->Frame(); 1253 BRect bounds = Bounds(); 1254 1255 if (fLeftScroller) { 1256 BRect lhit(0, frame.top, frame.left, frame.bottom); 1257 if (lhit.Contains(where)) { 1258 // Want to scroll by NUMSLOTS - 1 slots 1259 int32 previousIndex = fManager->CurrentIndex(); 1260 int32 previousSlot = fManager->CurrentSlot(); 1261 int32 newSlot = previousSlot - (kNumSlots - 1); 1262 if (newSlot < 0) 1263 newSlot = 0; 1264 1265 int32 newIndex = fIconView->IndexAt(newSlot); 1266 fManager->SwitchToApp(previousIndex, newIndex, false); 1267 } 1268 } 1269 1270 if (fRightScroller) { 1271 BRect rhit(frame.right, frame.top, bounds.right, frame.bottom); 1272 if (rhit.Contains(where)) { 1273 // Want to scroll by NUMSLOTS - 1 slots 1274 int32 previousIndex = fManager->CurrentIndex(); 1275 int32 previousSlot = fManager->CurrentSlot(); 1276 int32 newSlot = previousSlot + (kNumSlots - 1); 1277 int32 newIndex = fIconView->IndexAt(newSlot); 1278 1279 if (newIndex < 0) { 1280 // don't have a page full to scroll 1281 newIndex = fManager->GroupList()->CountItems() - 1; 1282 } 1283 fManager->SwitchToApp(previousIndex, newIndex, true); 1284 } 1285 } 1286 1287 frame = fWindow->WindowView()->Frame(); 1288 if (fUpScroller) { 1289 BRect hit1(frame.left - 10, frame.top, frame.left, 1290 (frame.top + frame.bottom) / 2); 1291 BRect hit2(frame.right, frame.top, frame.right + 10, 1292 (frame.top + frame.bottom) / 2); 1293 if (hit1.Contains(where) || hit2.Contains(where)) { 1294 // Want to scroll up 1 window 1295 fManager->CycleWindow(false, false); 1296 } 1297 } 1298 1299 if (fDownScroller) { 1300 BRect hit1(frame.left - 10, (frame.top + frame.bottom) / 2, 1301 frame.left, frame.bottom); 1302 BRect hit2(frame.right, (frame.top + frame.bottom) / 2, 1303 frame.right + 10, frame.bottom); 1304 if (hit1.Contains(where) || hit2.Contains(where)) { 1305 // Want to scroll down 1 window 1306 fManager->CycleWindow(true, false); 1307 } 1308 } 1309 } 1310 1311 1312 void 1313 TBox::Draw(BRect update) 1314 { 1315 static const int32 kChildInset = 7; 1316 static const int32 kWedge = 6; 1317 1318 BBox::Draw(update); 1319 1320 // The fancy border around the icon view 1321 1322 BRect bounds = Bounds(); 1323 float height = fIconView->Bounds().Height(); 1324 float center = (bounds.right + bounds.left) / 2; 1325 1326 BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2); 1327 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1328 rgb_color white = {255, 255, 255, 255}; 1329 rgb_color standardGray = panelColor; 1330 rgb_color veryDarkGray = {128, 128, 128, 255}; 1331 rgb_color darkGray = tint_color(panelColor, B_DARKEN_1_TINT); 1332 1333 if (panelColor.Brightness() < 100) { 1334 standardGray = tint_color(panelColor, 0.8); 1335 darkGray = tint_color(panelColor, 0.85); 1336 white = make_color(200, 200, 200, 255); 1337 veryDarkGray = make_color(0, 0, 0, 255); 1338 } 1339 1340 // Fill the area with dark gray 1341 SetHighColor(darkGray); 1342 box.InsetBy(1, 1); 1343 FillRect(box); 1344 1345 box.InsetBy(-1, -1); 1346 1347 BeginLineArray(50); 1348 1349 // The main frame around the icon view 1350 AddLine(box.LeftTop(), BPoint(center - kWedge, box.top), veryDarkGray); 1351 AddLine(BPoint(center + kWedge, box.top), box.RightTop(), veryDarkGray); 1352 1353 AddLine(box.LeftBottom(), BPoint(center - kWedge, box.bottom), 1354 veryDarkGray); 1355 AddLine(BPoint(center + kWedge, box.bottom), box.RightBottom(), 1356 veryDarkGray); 1357 AddLine(box.LeftBottom() + BPoint(1, 1), 1358 BPoint(center - kWedge, box.bottom + 1), white); 1359 AddLine(BPoint(center + kWedge, box.bottom) + BPoint(0, 1), 1360 box.RightBottom() + BPoint(1, 1), white); 1361 1362 AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray); 1363 AddLine(box.RightTop(), box.RightBottom(), veryDarkGray); 1364 AddLine(box.RightTop() + BPoint(1, 1), box.RightBottom() + BPoint(1, 1), 1365 white); 1366 1367 // downward pointing area at top of frame 1368 BPoint point(center - kWedge, box.top); 1369 AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray); 1370 AddLine(point + BPoint(kWedge, kWedge), BPoint(center + kWedge, point.y), 1371 veryDarkGray); 1372 1373 AddLine(point + BPoint(1, 0), point + BPoint(1, 0) 1374 + BPoint(kWedge - 1, kWedge - 1), white); 1375 1376 AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1), 1377 BPoint(center + kWedge - 1, point.y), darkGray); 1378 1379 BPoint topPoint = point; 1380 1381 // upward pointing area at bottom of frame 1382 point.y = box.bottom; 1383 point.x = center - kWedge; 1384 AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray); 1385 AddLine(point + BPoint(kWedge, -kWedge), 1386 BPoint(center + kWedge, point.y), veryDarkGray); 1387 1388 AddLine(point + BPoint(1, 0), 1389 point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white); 1390 1391 AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)), 1392 BPoint(center + kWedge - 1, point.y), darkGray); 1393 1394 BPoint bottomPoint = point; 1395 1396 EndLineArray(); 1397 1398 // fill the downward pointing arrow area 1399 SetHighColor(standardGray); 1400 FillTriangle(topPoint + BPoint(2, 0), 1401 topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2), 1402 BPoint(center + kWedge - 2, topPoint.y)); 1403 1404 // fill the upward pointing arrow area 1405 SetHighColor(standardGray); 1406 FillTriangle(bottomPoint + BPoint(2, 0), 1407 bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)), 1408 BPoint(center + kWedge - 2, bottomPoint.y)); 1409 1410 DrawIconScrollers(false); 1411 DrawWindowScrollers(false); 1412 1413 } 1414 1415 1416 void 1417 TBox::DrawIconScrollers(bool force) 1418 { 1419 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1420 rgb_color backgroundColor; 1421 rgb_color dark; 1422 1423 if (panelColor.Brightness() > 100) { 1424 backgroundColor = tint_color(panelColor, B_DARKEN_1_TINT); 1425 dark = tint_color(backgroundColor, B_DARKEN_3_TINT); 1426 } else { 1427 backgroundColor = tint_color(panelColor, 0.85); 1428 dark = tint_color(panelColor, B_LIGHTEN_1_TINT); 1429 } 1430 1431 bool updateLeft = false; 1432 bool updateRight = false; 1433 1434 BRect rect = fIconView->Bounds(); 1435 if (rect.left > (kSlotSize * kCenterSlot)) { 1436 updateLeft = true; 1437 fLeftScroller = true; 1438 } else { 1439 fLeftScroller = false; 1440 if (force) 1441 updateLeft = true; 1442 } 1443 1444 int32 maxIndex = fManager->GroupList()->CountItems() - 1; 1445 // last_frame is in fIconView coordinate space 1446 BRect lastFrame = fIconView->FrameOf(maxIndex); 1447 1448 if (lastFrame.right > rect.right) { 1449 updateRight = true; 1450 fRightScroller = true; 1451 } else { 1452 fRightScroller = false; 1453 if (force) 1454 updateRight = true; 1455 } 1456 1457 PushState(); 1458 SetDrawingMode(B_OP_COPY); 1459 1460 rect = fIconView->Frame(); 1461 if (updateLeft) { 1462 BPoint pt1, pt2, pt3; 1463 pt1.x = rect.left - 5; 1464 pt1.y = floorf((rect.bottom + rect.top) / 2); 1465 pt2.x = pt3.x = pt1.x + 3; 1466 pt2.y = pt1.y - 3; 1467 pt3.y = pt1.y + 3; 1468 1469 if (fLeftScroller) { 1470 SetHighColor(dark); 1471 FillTriangle(pt1, pt2, pt3); 1472 } else if (force) { 1473 SetHighColor(backgroundColor); 1474 FillRect(BRect(pt1.x, pt2.y, pt3.x, pt3.y)); 1475 } 1476 } 1477 if (updateRight) { 1478 BPoint pt1, pt2, pt3; 1479 pt1.x = rect.right + 4; 1480 pt1.y = rintf((rect.bottom + rect.top) / 2); 1481 pt2.x = pt3.x = pt1.x - 4; 1482 pt2.y = pt1.y - 4; 1483 pt3.y = pt1.y + 4; 1484 1485 if (fRightScroller) { 1486 SetHighColor(dark); 1487 FillTriangle(pt1, pt2, pt3); 1488 } else if (force) { 1489 SetHighColor(backgroundColor); 1490 FillRect(BRect(pt3.x, pt2.y, pt1.x, pt3.y)); 1491 } 1492 } 1493 1494 PopState(); 1495 } 1496 1497 1498 void 1499 TBox::DrawWindowScrollers(bool force) 1500 { 1501 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1502 rgb_color backgroundColor; 1503 rgb_color dark; 1504 1505 if (panelColor.Brightness() > 100) { 1506 backgroundColor = tint_color(panelColor, B_DARKEN_1_TINT); 1507 dark = tint_color(backgroundColor, B_DARKEN_2_TINT); 1508 } else { 1509 backgroundColor = panelColor; 1510 dark = tint_color(panelColor, B_LIGHTEN_2_TINT); 1511 } 1512 1513 bool updateUp = false; 1514 bool updateDown = false; 1515 1516 BRect rect = fWindow->WindowView()->Bounds(); 1517 if (rect.top != 0) { 1518 updateUp = true; 1519 fUpScroller = true; 1520 } else { 1521 fUpScroller = false; 1522 if (force) 1523 updateUp = true; 1524 } 1525 1526 int32 groupIndex = fManager->CurrentIndex(); 1527 int32 maxIndex = fManager->CountWindows(groupIndex) - 1; 1528 1529 BRect lastFrame(0, 0, 0, 0); 1530 if (maxIndex >= 0) 1531 lastFrame = fWindow->WindowView()->FrameOf(maxIndex); 1532 1533 if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) { 1534 updateDown = true; 1535 fDownScroller = true; 1536 } else { 1537 fDownScroller = false; 1538 if (force) 1539 updateDown = true; 1540 } 1541 1542 PushState(); 1543 SetDrawingMode(B_OP_COPY); 1544 1545 rect = fWindow->WindowView()->Frame(); 1546 rect.InsetBy(-3, 0); 1547 if (updateUp) { 1548 if (fUpScroller) { 1549 SetHighColor(dark); 1550 BPoint pt1, pt2, pt3; 1551 pt1.x = rect.left - 6; 1552 pt1.y = rect.top + 3; 1553 pt2.y = pt3.y = pt1.y + 4; 1554 pt2.x = pt1.x - 4; 1555 pt3.x = pt1.x + 4; 1556 FillTriangle(pt1, pt2, pt3); 1557 1558 pt1.x += rect.Width() + 12; 1559 pt2.x += rect.Width() + 12; 1560 pt3.x += rect.Width() + 12; 1561 FillTriangle(pt1, pt2, pt3); 1562 } else if (force) { 1563 FillRect(BRect(rect.left - 10, rect.top + 3, rect.left - 2, 1564 rect.top + 7), B_SOLID_LOW); 1565 FillRect(BRect(rect.right + 2, rect.top + 3, rect.right + 10, 1566 rect.top + 7), B_SOLID_LOW); 1567 } 1568 } 1569 if (updateDown) { 1570 if (fDownScroller) { 1571 SetHighColor(dark); 1572 BPoint pt1, pt2, pt3; 1573 pt1.x = rect.left - 6; 1574 pt1.y = rect.bottom - 3; 1575 pt2.y = pt3.y = pt1.y - 4; 1576 pt2.x = pt1.x - 4; 1577 pt3.x = pt1.x + 4; 1578 FillTriangle(pt1, pt2, pt3); 1579 1580 pt1.x += rect.Width() + 12; 1581 pt2.x += rect.Width() + 12; 1582 pt3.x += rect.Width() + 12; 1583 FillTriangle(pt1, pt2, pt3); 1584 } else if (force) { 1585 FillRect(BRect(rect.left - 10, rect.bottom - 7, rect.left - 2, 1586 rect.bottom - 3), B_SOLID_LOW); 1587 FillRect(BRect(rect.right + 2, rect.bottom - 7, rect.right + 10, 1588 rect.bottom - 3), B_SOLID_LOW); 1589 } 1590 } 1591 1592 PopState(); 1593 Sync(); 1594 } 1595 1596 1597 // #pragma mark - 1598 1599 1600 TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager* manager) 1601 : 1602 BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL, 1603 B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES), 1604 fManager(manager), 1605 fHairTrigger(true) 1606 { 1607 BRect rect = frame; 1608 rect.OffsetTo(B_ORIGIN); 1609 rect.InsetBy(kHorizontalMargin, 0); 1610 rect.top = kVerticalMargin; 1611 rect.bottom = rect.top + kSlotSize - 1; 1612 1613 fIconView = new TIconView(rect, manager, this); 1614 1615 rect.top = rect.bottom + (kVerticalMargin * 1 + 4); 1616 rect.InsetBy(9, 0); 1617 1618 fWindowView = new TWindowView(rect, manager, this); 1619 fWindowView->ResizeToPreferred(); 1620 1621 fTopView = new TBox(Bounds(), fManager, this, fIconView); 1622 AddChild(fTopView); 1623 fTopView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1624 fTopView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1625 fTopView->SetHighUIColor(B_PANEL_TEXT_COLOR); 1626 1627 SetPulseRate(0); 1628 fTopView->AddChild(fIconView); 1629 fTopView->AddChild(fWindowView); 1630 1631 if (be_plain_font->Size() != 12) { 1632 float sizeDelta = be_plain_font->Size() - 12; 1633 ResizeBy(0, sizeDelta); 1634 } 1635 } 1636 1637 1638 TSwitcherWindow::~TSwitcherWindow() 1639 { 1640 } 1641 1642 1643 void 1644 TSwitcherWindow::MessageReceived(BMessage* message) 1645 { 1646 switch (message->what) { 1647 case B_UNMAPPED_KEY_DOWN: 1648 case B_KEY_DOWN: 1649 { 1650 int32 repeats = 0; 1651 if (message->FindInt32("be:key_repeat", &repeats) == B_OK 1652 && (fSkipKeyRepeats || (repeats % 6) != 0)) 1653 break; 1654 1655 // The first actual key press let's us listening to repeated keys 1656 fSkipKeyRepeats = false; 1657 1658 uint32 rawChar; 1659 uint32 modifiers; 1660 message->FindInt32("raw_char", 0, (int32*)&rawChar); 1661 message->FindInt32("modifiers", 0, (int32*)&modifiers); 1662 DoKey(rawChar, modifiers); 1663 break; 1664 } 1665 1666 default: 1667 BWindow::MessageReceived(message); 1668 } 1669 } 1670 1671 1672 void 1673 TSwitcherWindow::Redraw(int32 index) 1674 { 1675 BRect frame = fIconView->FrameOf(index); 1676 frame.right = fIconView->Bounds().right; 1677 fIconView->Invalidate(frame); 1678 } 1679 1680 1681 void 1682 TSwitcherWindow::DoKey(uint32 key, uint32 modifiers) 1683 { 1684 bool forward = ((modifiers & B_SHIFT_KEY) == 0); 1685 1686 switch (key) { 1687 case B_RIGHT_ARROW: 1688 fManager->CycleApp(true, false); 1689 break; 1690 1691 case B_LEFT_ARROW: 1692 case '1': 1693 fManager->CycleApp(false, false); 1694 break; 1695 1696 case B_UP_ARROW: 1697 fManager->CycleWindow(false, false); 1698 break; 1699 1700 case B_DOWN_ARROW: 1701 fManager->CycleWindow(true, false); 1702 break; 1703 1704 case B_TAB: 1705 fManager->CycleApp(forward, false); 1706 break; 1707 1708 case B_ESCAPE: 1709 fManager->Stop(false, 0); 1710 break; 1711 1712 case B_SPACE: 1713 case B_ENTER: 1714 fManager->Stop(true, modifiers); 1715 break; 1716 1717 case 'q': 1718 case 'Q': 1719 fManager->QuitApp(); 1720 break; 1721 1722 case 'h': 1723 case 'H': 1724 fManager->HideApp(); 1725 break; 1726 1727 #if _ALLOW_STICKY_ 1728 case 's': 1729 case 'S': 1730 if (fHairTrigger) { 1731 SetLook(B_TITLED_WINDOW_LOOK); 1732 fHairTrigger = false; 1733 } else { 1734 SetLook(B_MODAL_WINDOW_LOOK); 1735 fHairTrigger = true; 1736 } 1737 break; 1738 #endif 1739 } 1740 } 1741 1742 1743 bool 1744 TSwitcherWindow::QuitRequested() 1745 { 1746 ((TBarApp*)be_app)->Settings()->switcherLoc = Frame().LeftTop(); 1747 fManager->Stop(false, 0); 1748 return false; 1749 } 1750 1751 1752 void 1753 TSwitcherWindow::WindowActivated(bool state) 1754 { 1755 if (state) 1756 fManager->Unblock(); 1757 } 1758 1759 1760 void 1761 TSwitcherWindow::Update(int32 prev, int32 current, int32 previousSlot, 1762 int32 currentSlot, bool forward) 1763 { 1764 if (!IsHidden()) 1765 fIconView->Update(prev, current, previousSlot, currentSlot, forward); 1766 else 1767 fIconView->CenterOn(current); 1768 1769 fWindowView->UpdateGroup(current, 0); 1770 } 1771 1772 1773 void 1774 TSwitcherWindow::Hide() 1775 { 1776 fIconView->Hiding(); 1777 SetPulseRate(0); 1778 BWindow::Hide(); 1779 } 1780 1781 1782 void 1783 TSwitcherWindow::Show() 1784 { 1785 fHairTrigger = true; 1786 fSkipKeyRepeats = true; 1787 fIconView->Showing(); 1788 SetPulseRate(100000); 1789 SetLook(B_MODAL_WINDOW_LOOK); 1790 BWindow::Show(); 1791 } 1792 1793 1794 TBox* 1795 TSwitcherWindow::TopView() 1796 { 1797 return fTopView; 1798 } 1799 1800 1801 bool 1802 TSwitcherWindow::HairTrigger() 1803 { 1804 return fHairTrigger; 1805 } 1806 1807 1808 inline int32 1809 TSwitcherWindow::SlotOf(int32 i) 1810 { 1811 return fIconView->SlotOf(i); 1812 } 1813 1814 1815 inline TIconView* 1816 TSwitcherWindow::IconView() 1817 { 1818 return fIconView; 1819 } 1820 1821 1822 inline TWindowView* 1823 TSwitcherWindow::WindowView() 1824 { 1825 return fWindowView; 1826 } 1827 1828 1829 // #pragma mark - 1830 1831 1832 TIconView::TIconView(BRect frame, TSwitchManager* manager, 1833 TSwitcherWindow* switcherWindow) 1834 : BView(frame, "main_view", B_FOLLOW_NONE, 1835 B_WILL_DRAW | B_PULSE_NEEDED), 1836 fAutoScrolling(false), 1837 fSwitcher(switcherWindow), 1838 fManager(manager) 1839 { 1840 BRect rect(0, 0, kSlotSize - 1, kSlotSize - 1); 1841 1842 fOffView = new BView(rect, "off_view", B_FOLLOW_NONE, B_WILL_DRAW); 1843 fOffBitmap = new BBitmap(rect, B_RGB32, true); 1844 fOffBitmap->AddChild(fOffView); 1845 1846 fCurrentSmall = new BBitmap(BRect(0, 0, 15, 15), kIconFormat); 1847 fCurrentLarge = new BBitmap(BRect(0, 0, 31, 31), kIconFormat); 1848 } 1849 1850 1851 TIconView::~TIconView() 1852 { 1853 delete fCurrentSmall; 1854 delete fCurrentLarge; 1855 delete fOffBitmap; 1856 } 1857 1858 1859 void 1860 TIconView::KeyDown(const char* /*bytes*/, int32 /*numBytes*/) 1861 { 1862 } 1863 1864 1865 void 1866 TIconView::CacheIcons(TTeamGroup* teamGroup) 1867 { 1868 const BBitmap* bitmap = teamGroup->SmallIcon(); 1869 ASSERT(bitmap); 1870 fCurrentSmall->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1871 bitmap->ColorSpace()); 1872 1873 bitmap = teamGroup->LargeIcon(); 1874 ASSERT(bitmap); 1875 fCurrentLarge->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1876 bitmap->ColorSpace()); 1877 } 1878 1879 1880 void 1881 TIconView::AnimateIcon(BBitmap* startIcon, BBitmap* endIcon) 1882 { 1883 BRect centerRect(kCenterSlot * kSlotSize, 0, 1884 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1885 BRect startIconBounds = startIcon->Bounds(); 1886 BRect bounds = Bounds(); 1887 float width = startIconBounds.Width(); 1888 int32 amount = (width < 20) ? -2 : 2; 1889 1890 // center the starting icon inside of centerRect 1891 float off = (centerRect.Width() - width) / 2; 1892 startIconBounds.OffsetTo(BPoint(off, off)); 1893 1894 // scroll the centerRect to correct location 1895 centerRect.OffsetBy(bounds.left, 0); 1896 1897 BRect destRect = fOffBitmap->Bounds(); 1898 // scroll to the centerRect location 1899 destRect.OffsetTo(centerRect.left, 0); 1900 // center the destRect inside of centerRect. 1901 off = (centerRect.Width() - destRect.Width()) / 2; 1902 destRect.OffsetBy(BPoint(off, off)); 1903 1904 fOffBitmap->Lock(); 1905 rgb_color backgroundColor = ui_color(B_PANEL_BACKGROUND_COLOR); 1906 if (backgroundColor.Brightness() > 100) 1907 fOffView->SetHighColor(tint_color(backgroundColor, B_DARKEN_1_TINT)); 1908 else 1909 fOffView->SetHighColor(tint_color(backgroundColor, 0.85)); 1910 1911 for (int i = 0; i < 2; i++) { 1912 startIconBounds.InsetBy(amount, amount); 1913 snooze(20000); 1914 fOffView->SetDrawingMode(B_OP_COPY); 1915 fOffView->FillRect(fOffView->Bounds()); 1916 fOffView->SetDrawingMode(B_OP_ALPHA); 1917 fOffView->DrawBitmap(startIcon, startIconBounds); 1918 fOffView->Sync(); 1919 DrawBitmap(fOffBitmap, destRect); 1920 } 1921 for (int i = 0; i < 2; i++) { 1922 startIconBounds.InsetBy(amount, amount); 1923 snooze(20000); 1924 fOffView->SetDrawingMode(B_OP_COPY); 1925 fOffView->FillRect(fOffView->Bounds()); 1926 fOffView->SetDrawingMode(B_OP_ALPHA); 1927 fOffView->DrawBitmap(endIcon, startIconBounds); 1928 fOffView->Sync(); 1929 DrawBitmap(fOffBitmap, destRect); 1930 } 1931 1932 fOffBitmap->Unlock(); 1933 } 1934 1935 1936 void 1937 TIconView::Update(int32, int32 current, int32 previousSlot, int32 currentSlot, 1938 bool forward) 1939 { 1940 // Animate the shrinking of the currently centered icon. 1941 AnimateIcon(fCurrentLarge, fCurrentSmall); 1942 1943 int32 nslots = abs(previousSlot - currentSlot); 1944 int32 stepSize = kScrollStep; 1945 1946 if (forward && (currentSlot < previousSlot)) { 1947 // we were at the end of the list and we just moved to the start 1948 forward = false; 1949 if (previousSlot - currentSlot > 4) 1950 stepSize *= 2; 1951 } else if (!forward && (currentSlot > previousSlot)) { 1952 // we're are moving backwards and we just hit start of list and 1953 // we wrapped to the end. 1954 forward = true; 1955 if (currentSlot - previousSlot > 4) 1956 stepSize *= 2; 1957 } 1958 1959 int32 scrollValue = forward ? stepSize : -stepSize; 1960 int32 total = 0; 1961 1962 fAutoScrolling = true; 1963 while (total < (nslots * kSlotSize)) { 1964 ScrollBy(scrollValue, 0); 1965 snooze(1000); 1966 total += stepSize; 1967 Window()->UpdateIfNeeded(); 1968 } 1969 fAutoScrolling = false; 1970 1971 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(current); 1972 ASSERT(teamGroup); 1973 CacheIcons(teamGroup); 1974 1975 // Animate the expansion of the currently centered icon 1976 AnimateIcon(fCurrentSmall, fCurrentLarge); 1977 } 1978 1979 1980 void 1981 TIconView::CenterOn(int32 index) 1982 { 1983 BRect rect = FrameOf(index); 1984 ScrollTo(rect.left - (kCenterSlot * kSlotSize), 0); 1985 } 1986 1987 1988 int32 1989 TIconView::ItemAtPoint(BPoint point) const 1990 { 1991 return IndexAt((int32)(point.x / kSlotSize) - kCenterSlot); 1992 } 1993 1994 1995 void 1996 TIconView::ScrollTo(BPoint where) 1997 { 1998 BView::ScrollTo(where); 1999 fSwitcher->TopView()->DrawIconScrollers(true); 2000 } 2001 2002 2003 int32 2004 TIconView::IndexAt(int32 slot) const 2005 { 2006 if (slot < 0 || slot >= fManager->GroupList()->CountItems()) 2007 return -1; 2008 2009 return slot; 2010 } 2011 2012 2013 int32 2014 TIconView::SlotOf(int32 index) const 2015 { 2016 BRect rect = FrameOf(index); 2017 2018 return (int32)(rect.left / kSlotSize) - kCenterSlot; 2019 } 2020 2021 2022 BRect 2023 TIconView::FrameOf(int32 index) const 2024 { 2025 int32 visible = index + kCenterSlot; 2026 // first few slots in view are empty 2027 2028 return BRect(visible * kSlotSize, 0, (visible + 1) * kSlotSize - 1, 2029 kSlotSize - 1); 2030 } 2031 2032 2033 void 2034 TIconView::DrawTeams(BRect update) 2035 { 2036 float tint = B_NO_TINT; 2037 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR); 2038 2039 if (panelColor.Brightness() < 100) 2040 tint = 0.85; 2041 else 2042 tint = B_DARKEN_1_TINT; 2043 2044 SetHighUIColor(B_PANEL_BACKGROUND_COLOR, tint); 2045 SetLowUIColor(ViewUIColor(), tint); 2046 2047 FillRect(update); 2048 int32 mainIndex = fManager->CurrentIndex(); 2049 BList* list = fManager->GroupList(); 2050 int32 count = list->CountItems(); 2051 2052 BRect rect(kCenterSlot * kSlotSize, 0, 2053 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 2054 2055 for (int32 i = 0; i < count; i++) { 2056 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2057 if (rect.Intersects(update) && teamGroup) { 2058 SetDrawingMode(B_OP_ALPHA); 2059 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 2060 2061 teamGroup->Draw(this, rect, !fAutoScrolling && (i == mainIndex)); 2062 2063 if (i == mainIndex) 2064 CacheIcons(teamGroup); 2065 2066 SetDrawingMode(B_OP_COPY); 2067 } 2068 rect.OffsetBy(kSlotSize, 0); 2069 } 2070 } 2071 2072 2073 void 2074 TIconView::Draw(BRect update) 2075 { 2076 DrawTeams(update); 2077 } 2078 2079 2080 void 2081 TIconView::MouseDown(BPoint where) 2082 { 2083 int32 index = ItemAtPoint(where); 2084 if (index >= 0) { 2085 int32 previousIndex = fManager->CurrentIndex(); 2086 int32 previousSlot = fManager->CurrentSlot(); 2087 int32 currentSlot = SlotOf(index); 2088 fManager->SwitchToApp(previousIndex, index, (currentSlot 2089 > previousSlot)); 2090 } 2091 } 2092 2093 2094 void 2095 TIconView::Pulse() 2096 { 2097 uint32 modifiersKeys = modifiers(); 2098 if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) { 2099 fManager->Stop(true, modifiersKeys); 2100 return; 2101 } 2102 2103 if (!fSwitcher->HairTrigger()) { 2104 uint32 buttons; 2105 BPoint point; 2106 GetMouse(&point, &buttons); 2107 if (buttons != 0) { 2108 point = ConvertToScreen(point); 2109 if (!Window()->Frame().Contains(point)) 2110 fManager->Stop(false, 0); 2111 } 2112 } 2113 } 2114 2115 2116 void 2117 TIconView::Showing() 2118 { 2119 } 2120 2121 2122 void 2123 TIconView::Hiding() 2124 { 2125 ScrollTo(B_ORIGIN); 2126 } 2127 2128 2129 // #pragma mark - 2130 2131 2132 TWindowView::TWindowView(BRect rect, TSwitchManager* manager, 2133 TSwitcherWindow* window) 2134 : BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), 2135 fCurrentToken(-1), 2136 fItemHeight(-1), 2137 fSwitcher(window), 2138 fManager(manager) 2139 { 2140 SetFont(be_plain_font); 2141 } 2142 2143 2144 void 2145 TWindowView::AttachedToWindow() 2146 { 2147 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 2148 } 2149 2150 2151 void 2152 TWindowView::ScrollTo(BPoint where) 2153 { 2154 BView::ScrollTo(where); 2155 fSwitcher->TopView()->DrawWindowScrollers(true); 2156 } 2157 2158 2159 BRect 2160 TWindowView::FrameOf(int32 index) const 2161 { 2162 return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1); 2163 } 2164 2165 2166 void 2167 TWindowView::GetPreferredSize(float* _width, float* _height) 2168 { 2169 font_height fh; 2170 be_plain_font->GetHeight(&fh); 2171 fItemHeight = (int32) fh.ascent + fh.descent; 2172 2173 // top & bottom margin 2174 fItemHeight = fItemHeight + 3 + 3; 2175 2176 // want fItemHeight to be divisible by kWindowScrollSteps. 2177 fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps) 2178 / kWindowScrollSteps) * kWindowScrollSteps; 2179 2180 *_height = fItemHeight; 2181 2182 // leave width alone 2183 *_width = Bounds().Width(); 2184 } 2185 2186 2187 void 2188 TWindowView::ShowIndex(int32 newIndex) 2189 { 2190 // convert index to scroll location 2191 BPoint point(0, newIndex * fItemHeight); 2192 BRect bounds = Bounds(); 2193 2194 int32 groupIndex = fManager->CurrentIndex(); 2195 TTeamGroup* teamGroup 2196 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2197 if (teamGroup == NULL) 2198 return; 2199 2200 window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex); 2201 if (windowInfo == NULL) 2202 return; 2203 2204 fCurrentToken = windowInfo->server_token; 2205 free(windowInfo); 2206 2207 if (bounds.top == point.y) 2208 return; 2209 2210 int32 oldIndex = (int32) (bounds.top / fItemHeight); 2211 2212 int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps); 2213 int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize; 2214 int32 total = 0; 2215 int32 nslots = abs(newIndex - oldIndex); 2216 2217 while (total < (nslots * (int32)fItemHeight)) { 2218 ScrollBy(0, scrollValue); 2219 snooze(10000); 2220 total += stepSize; 2221 Window()->UpdateIfNeeded(); 2222 } 2223 } 2224 2225 2226 void 2227 TWindowView::Draw(BRect update) 2228 { 2229 int32 groupIndex = fManager->CurrentIndex(); 2230 TTeamGroup* teamGroup 2231 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2232 2233 if (teamGroup == NULL) 2234 return; 2235 2236 BRect bounds = Bounds(); 2237 int32 windowIndex = (int32) (bounds.top / fItemHeight); 2238 BRect windowRect = bounds; 2239 2240 windowRect.top = windowIndex * fItemHeight; 2241 windowRect.bottom = (windowIndex + 1) * fItemHeight - 1; 2242 2243 for (int32 i = 0; i < 3; i++) { 2244 if (!update.Intersects(windowRect)) { 2245 windowIndex++; 2246 windowRect.OffsetBy(0, fItemHeight); 2247 continue; 2248 } 2249 2250 // is window in current workspace? 2251 2252 bool local = true; 2253 bool minimized = false; 2254 BString title; 2255 2256 client_window_info* windowInfo 2257 = fManager->WindowInfo(groupIndex, windowIndex); 2258 if (windowInfo != NULL) { 2259 if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0) 2260 title << teamGroup->Name() << ": " << windowInfo->name; 2261 else 2262 title = teamGroup->Name(); 2263 2264 int32 currentWorkspace = current_workspace(); 2265 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) 2266 local = false; 2267 2268 minimized = windowInfo->is_mini; 2269 free(windowInfo); 2270 } else 2271 title = teamGroup->Name(); 2272 2273 if (!title.Length()) 2274 return; 2275 2276 float stringWidth = StringWidth(title.String()); 2277 float maxWidth = bounds.Width() - (14 + 5); 2278 2279 if (stringWidth > maxWidth) { 2280 // window title is too long, need to truncate 2281 TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth); 2282 stringWidth = maxWidth; 2283 } 2284 2285 BPoint point((bounds.Width() - (stringWidth + 14 + 5)) / 2, 2286 windowRect.bottom - 4); 2287 BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2); 2288 SetDrawingMode(B_OP_OVER); 2289 const BBitmap* bitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE, 2290 minimized ? R_WindowHiddenIcon : R_WindowShownIcon); 2291 p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2; 2292 DrawBitmap(bitmap, p); 2293 2294 if (!local) { 2295 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR); 2296 if (color.Brightness() > 100) 2297 SetHighColor(tint_color(color, B_DARKEN_4_TINT)); 2298 else 2299 SetHighColor(tint_color(color, B_LIGHTEN_1_TINT)); 2300 2301 p.x -= 8; 2302 p.y += 4; 2303 StrokeLine(p + BPoint(2, 2), p + BPoint(2, 2)); 2304 StrokeLine(p + BPoint(4, 2), p + BPoint(6, 2)); 2305 2306 StrokeLine(p + BPoint(0, 5), p + BPoint(0, 5)); 2307 StrokeLine(p + BPoint(2, 5), p + BPoint(6, 5)); 2308 2309 StrokeLine(p + BPoint(1, 8), p + BPoint(1, 8)); 2310 StrokeLine(p + BPoint(3, 8), p + BPoint(6, 8)); 2311 } 2312 2313 point.x += 21; 2314 MovePenTo(point); 2315 2316 SetHighUIColor(B_PANEL_TEXT_COLOR); 2317 DrawString(title.String()); 2318 SetDrawingMode(B_OP_COPY); 2319 2320 windowIndex++; 2321 windowRect.OffsetBy(0, fItemHeight); 2322 } 2323 } 2324 2325 2326 void 2327 TWindowView::UpdateGroup(int32 , int32 windowIndex) 2328 { 2329 ScrollTo(0, windowIndex * fItemHeight); 2330 Invalidate(Bounds()); 2331 } 2332 2333 2334 void 2335 TWindowView::Pulse() 2336 { 2337 // If selected window went away then reset to first window 2338 window_info *windowInfo = get_window_info(fCurrentToken); 2339 if (windowInfo == NULL) { 2340 Invalidate(); 2341 ShowIndex(0); 2342 } else 2343 free(windowInfo); 2344 } 2345