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