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.Brightness() < 100) { 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.Brightness() > 100) { 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.Brightness() > 100) { 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.Brightness() > 100 1944 ? B_DARKEN_1_TINT : 0.85)); 1945 1946 // animate start icon 1947 for (int i = 0; i < 2; i++) { 1948 rect.InsetBy(inset, inset); 1949 snooze(20000); 1950 fOffView->SetDrawingMode(B_OP_COPY); 1951 fOffView->FillRect(fOffView->Bounds()); 1952 fOffView->SetDrawingMode(B_OP_ALPHA); 1953 fOffView->DrawBitmap(start, rect); 1954 fOffView->Sync(); 1955 DrawBitmap(fOffBitmap, destRect); 1956 } 1957 1958 // draw cached start icon again to clear composed icon rounding errors 1959 fOffView->SetDrawingMode(B_OP_COPY); 1960 fOffView->FillRect(fOffView->Bounds()); 1961 fOffView->SetDrawingMode(B_OP_ALPHA); 1962 fOffView->DrawBitmap(start, startRect); 1963 fOffView->Sync(); 1964 DrawBitmap(fOffBitmap, destRect); 1965 1966 // animate end icon 1967 for (int i = 0; i < 2; i++) { 1968 rect.InsetBy(inset, inset); 1969 snooze(20000); 1970 fOffView->SetDrawingMode(B_OP_COPY); 1971 fOffView->FillRect(fOffView->Bounds()); 1972 fOffView->SetDrawingMode(B_OP_ALPHA); 1973 fOffView->DrawBitmap(end, rect); 1974 fOffView->Sync(); 1975 DrawBitmap(fOffBitmap, destRect); 1976 } 1977 1978 // draw cached end icon again 1979 fOffView->SetDrawingMode(B_OP_COPY); 1980 fOffView->FillRect(fOffView->Bounds()); 1981 fOffView->SetDrawingMode(B_OP_ALPHA); 1982 fOffView->DrawBitmap(end, endRect); 1983 fOffView->Sync(); 1984 DrawBitmap(fOffBitmap, destRect); 1985 1986 fOffBitmap->Unlock(); 1987 } 1988 1989 1990 void 1991 TIconView::Update(int32 previous, int32 current, 1992 int32 previousSlot, int32 currentSlot, bool forward) 1993 { 1994 BList* groupList = fManager->GroupList(); 1995 ASSERT(groupList); 1996 1997 // animate shrinking previously centered icon 1998 TTeamGroup* previousGroup = (TTeamGroup*)groupList->ItemAt(previous); 1999 ASSERT(previousGroup); 2000 AnimateIcon(previousGroup->LargeIcon(), previousGroup->SmallIcon()); 2001 2002 int32 nslots = abs(previousSlot - currentSlot); 2003 int32 stepSize = fManager->ScrollStep(); 2004 2005 if (forward && (currentSlot < previousSlot)) { 2006 // we were at the end of the list and we just moved to the start 2007 forward = false; 2008 if (previousSlot - currentSlot > 4) 2009 stepSize *= 2; 2010 } else if (!forward && (currentSlot > previousSlot)) { 2011 // we're are moving backwards and we just hit start of list and 2012 // we wrapped to the end. 2013 forward = true; 2014 if (currentSlot - previousSlot > 4) 2015 stepSize *= 2; 2016 } 2017 2018 int32 scrollValue = forward ? stepSize : -stepSize; 2019 int32 total = 0; 2020 2021 fAutoScrolling = true; 2022 while (total < (nslots * fManager->SlotSize())) { 2023 ScrollBy(scrollValue, 0); 2024 snooze(1000); 2025 total += stepSize; 2026 Window()->UpdateIfNeeded(); 2027 } 2028 fAutoScrolling = false; 2029 2030 // animate expanding currently centered icon 2031 TTeamGroup* currentGroup = (TTeamGroup*)groupList->ItemAt(current); 2032 ASSERT(currentGroup != NULL); 2033 AnimateIcon(currentGroup->SmallIcon(), currentGroup->LargeIcon()); 2034 } 2035 2036 2037 void 2038 TIconView::CenterOn(int32 index) 2039 { 2040 BRect rect = FrameOf(index); 2041 ScrollTo(rect.left - (fManager->CenterSlot() * fManager->SlotSize()), 0); 2042 } 2043 2044 2045 int32 2046 TIconView::ItemAtPoint(BPoint point) const 2047 { 2048 return IndexAt((int32)(point.x / fManager->SlotSize()) - fManager->CenterSlot()); 2049 } 2050 2051 2052 void 2053 TIconView::ScrollTo(BPoint where) 2054 { 2055 BView::ScrollTo(where); 2056 fSwitcher->TopView()->DrawIconScrollers(true); 2057 } 2058 2059 2060 int32 2061 TIconView::IndexAt(int32 slot) const 2062 { 2063 if (slot < 0 || slot >= fManager->GroupList()->CountItems()) 2064 return -1; 2065 2066 return slot; 2067 } 2068 2069 2070 int32 2071 TIconView::SlotOf(int32 index) const 2072 { 2073 BRect rect = FrameOf(index); 2074 2075 return (int32)(rect.left / fManager->SlotSize()) - fManager->CenterSlot(); 2076 } 2077 2078 2079 BRect 2080 TIconView::FrameOf(int32 index) const 2081 { 2082 int32 visible = index + fManager->CenterSlot(); 2083 // first few slots in view are empty 2084 2085 return BRect(visible * fManager->SlotSize(), 0, 2086 (visible + 1) * fManager->SlotSize() - 1, fManager->SlotSize() - 1); 2087 } 2088 2089 2090 void 2091 TIconView::DrawTeams(BRect update) 2092 { 2093 float tint = B_NO_TINT; 2094 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR); 2095 2096 if (panelColor.Brightness() < 100) 2097 tint = 0.85; 2098 else 2099 tint = B_DARKEN_1_TINT; 2100 2101 SetHighUIColor(B_PANEL_BACKGROUND_COLOR, tint); 2102 SetLowUIColor(ViewUIColor(), tint); 2103 2104 FillRect(update); 2105 int32 mainIndex = fManager->CurrentIndex(); 2106 BList* groupList = fManager->GroupList(); 2107 int32 teamCount = groupList->CountItems(); 2108 2109 BRect rect(fManager->CenterSlot() * fManager->SlotSize(), 0, 2110 (fManager->CenterSlot() + 1) * fManager->SlotSize() - 1, 2111 fManager->SlotSize() - 1); 2112 2113 for (int32 i = 0; i < teamCount; i++) { 2114 TTeamGroup* group = (TTeamGroup*)groupList->ItemAt(i); 2115 if (rect.Intersects(update) && group != NULL) { 2116 SetDrawingMode(B_OP_ALPHA); 2117 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 2118 2119 group->Draw(this, rect, !fAutoScrolling && (i == mainIndex)); 2120 2121 SetDrawingMode(B_OP_COPY); 2122 } 2123 rect.OffsetBy(fManager->SlotSize(), 0); 2124 } 2125 } 2126 2127 2128 void 2129 TIconView::Draw(BRect update) 2130 { 2131 DrawTeams(update); 2132 } 2133 2134 2135 void 2136 TIconView::MouseDown(BPoint where) 2137 { 2138 int32 index = ItemAtPoint(where); 2139 if (index >= 0) { 2140 int32 previousIndex = fManager->CurrentIndex(); 2141 int32 previousSlot = fManager->CurrentSlot(); 2142 int32 currentSlot = SlotOf(index); 2143 fManager->SwitchToApp(previousIndex, index, (currentSlot 2144 > previousSlot)); 2145 } 2146 } 2147 2148 2149 void 2150 TIconView::Pulse() 2151 { 2152 uint32 modifiersKeys = modifiers(); 2153 if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) { 2154 fManager->Stop(true, modifiersKeys); 2155 return; 2156 } 2157 2158 if (!fSwitcher->HairTrigger()) { 2159 uint32 buttons; 2160 BPoint point; 2161 GetMouse(&point, &buttons); 2162 if (buttons != 0) { 2163 point = ConvertToScreen(point); 2164 if (!Window()->Frame().Contains(point)) 2165 fManager->Stop(false, 0); 2166 } 2167 } 2168 } 2169 2170 2171 void 2172 TIconView::Showing() 2173 { 2174 } 2175 2176 2177 void 2178 TIconView::Hiding() 2179 { 2180 ScrollTo(B_ORIGIN); 2181 } 2182 2183 2184 // #pragma mark - TWindowView 2185 2186 2187 TWindowView::TWindowView(BRect rect, TSwitchManager* manager, 2188 TSwitcherWindow* window) 2189 : BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), 2190 fCurrentToken(-1), 2191 fItemHeight(-1), 2192 fSwitcher(window), 2193 fManager(manager) 2194 { 2195 SetFont(be_plain_font); 2196 } 2197 2198 2199 void 2200 TWindowView::AttachedToWindow() 2201 { 2202 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 2203 } 2204 2205 2206 void 2207 TWindowView::ScrollTo(BPoint where) 2208 { 2209 BView::ScrollTo(where); 2210 fSwitcher->TopView()->DrawWindowScrollers(true); 2211 } 2212 2213 2214 BRect 2215 TWindowView::FrameOf(int32 index) const 2216 { 2217 return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1); 2218 } 2219 2220 2221 void 2222 TWindowView::GetPreferredSize(float* _width, float* _height) 2223 { 2224 font_height fh; 2225 be_plain_font->GetHeight(&fh); 2226 fItemHeight = (int32) fh.ascent + fh.descent; 2227 2228 // top & bottom margin 2229 float margin = be_control_look->DefaultLabelSpacing(); 2230 fItemHeight = fItemHeight + margin * 2; 2231 2232 // want fItemHeight to be divisible by kWindowScrollSteps. 2233 fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps) 2234 / kWindowScrollSteps) * kWindowScrollSteps; 2235 2236 *_height = fItemHeight; 2237 2238 // leave width alone 2239 *_width = Bounds().Width(); 2240 } 2241 2242 2243 void 2244 TWindowView::ShowIndex(int32 newIndex) 2245 { 2246 // convert index to scroll location 2247 BPoint point(0, newIndex * fItemHeight); 2248 BRect bounds = Bounds(); 2249 2250 int32 groupIndex = fManager->CurrentIndex(); 2251 TTeamGroup* teamGroup 2252 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2253 if (teamGroup == NULL) 2254 return; 2255 2256 window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex); 2257 if (windowInfo == NULL) 2258 return; 2259 2260 fCurrentToken = windowInfo->server_token; 2261 free(windowInfo); 2262 2263 if (bounds.top == point.y) 2264 return; 2265 2266 int32 oldIndex = (int32) (bounds.top / fItemHeight); 2267 2268 int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps); 2269 int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize; 2270 int32 total = 0; 2271 int32 nslots = abs(newIndex - oldIndex); 2272 2273 while (total < (nslots * (int32)fItemHeight)) { 2274 ScrollBy(0, scrollValue); 2275 snooze(10000); 2276 total += stepSize; 2277 Window()->UpdateIfNeeded(); 2278 } 2279 } 2280 2281 2282 void 2283 TWindowView::Draw(BRect update) 2284 { 2285 int32 groupIndex = fManager->CurrentIndex(); 2286 TTeamGroup* teamGroup 2287 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2288 2289 if (teamGroup == NULL) 2290 return; 2291 2292 BRect bounds = Bounds(); 2293 int32 windowIndex = (int32) (bounds.top / fItemHeight); 2294 BRect windowRect = bounds; 2295 2296 windowRect.top = windowIndex * fItemHeight; 2297 windowRect.bottom = (windowIndex + 1) * fItemHeight - 1; 2298 2299 for (int32 i = 0; i < 3; i++) { 2300 if (!update.Intersects(windowRect)) { 2301 windowIndex++; 2302 windowRect.OffsetBy(0, fItemHeight); 2303 continue; 2304 } 2305 2306 // is window in current workspace? 2307 2308 bool local = true; 2309 bool minimized = false; 2310 BString title; 2311 2312 client_window_info* windowInfo 2313 = fManager->WindowInfo(groupIndex, windowIndex); 2314 if (windowInfo != NULL) { 2315 if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0) 2316 title << teamGroup->Name() << ": " << windowInfo->name; 2317 else 2318 title = teamGroup->Name(); 2319 2320 int32 currentWorkspace = current_workspace(); 2321 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) 2322 local = false; 2323 2324 minimized = windowInfo->is_mini; 2325 free(windowInfo); 2326 } else 2327 title = teamGroup->Name(); 2328 2329 if (!title.Length()) 2330 return; 2331 2332 float iconWidth = 0; 2333 2334 // get the (cached) window icon bitmap 2335 TBarApp* app = static_cast<TBarApp*>(be_app); 2336 const BBitmap* bitmap = app->FetchWindowIcon(local, minimized); 2337 if (bitmap != NULL) 2338 iconWidth = bitmap->Bounds().Width(); 2339 2340 float spacing = be_control_look->DefaultLabelSpacing(); 2341 float stringWidth = StringWidth(title.String()); 2342 float maxWidth = bounds.Width(); 2343 if (bitmap != NULL) 2344 maxWidth -= (iconWidth + spacing); 2345 2346 if (stringWidth > maxWidth) { 2347 // window title is too long, need to truncate 2348 TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth); 2349 stringWidth = maxWidth; 2350 } 2351 2352 BPoint point((bounds.Width() - stringWidth) / 2, windowRect.bottom); 2353 if (bitmap != NULL) { 2354 point.x = (bounds.Width() 2355 - (stringWidth + iconWidth + spacing)) / 2; 2356 } 2357 2358 BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2); 2359 SetDrawingMode(B_OP_OVER); 2360 2361 // center bitmap horizontally and move text past icon 2362 if (bitmap != NULL) { 2363 p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2; 2364 DrawBitmap(bitmap, p); 2365 2366 point.x += bitmap->Bounds().Width() + spacing; 2367 } 2368 2369 // center text vertically 2370 font_height fontHeight; 2371 GetFontHeight(&fontHeight); 2372 float textHeight = fontHeight.ascent + fontHeight.descent; 2373 point.y -= (textHeight / 2) + (spacing / 2); 2374 if (bitmap != NULL) { 2375 // center in middle of window icon 2376 point.y += ((textHeight - bitmap->Bounds().Height()) / 2); 2377 } 2378 2379 MovePenTo(point); 2380 2381 SetHighUIColor(B_PANEL_TEXT_COLOR); 2382 DrawString(title.String()); 2383 SetDrawingMode(B_OP_COPY); 2384 2385 windowIndex++; 2386 windowRect.OffsetBy(0, fItemHeight); 2387 } 2388 } 2389 2390 2391 void 2392 TWindowView::UpdateGroup(int32, int32 windowIndex) 2393 { 2394 ScrollTo(0, windowIndex * fItemHeight); 2395 Invalidate(Bounds()); 2396 } 2397 2398 2399 void 2400 TWindowView::Pulse() 2401 { 2402 // If selected window went away then reset to first window 2403 window_info *windowInfo = get_window_info(fCurrentToken); 2404 if (windowInfo == NULL) { 2405 Invalidate(); 2406 ShowIndex(0); 2407 } else 2408 free(windowInfo); 2409 } 2410