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