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 = 0; 602 message->FindInt32("modifiers", (int32*)&modifiers); 603 int32 key = 0; 604 message->FindInt32("key", &key); 605 606 Process((modifiers & B_SHIFT_KEY) == 0, key == 0x11); 607 } 608 } 609 } else 610 MainEntry(message); 611 612 break; 613 } 614 615 default: 616 break; 617 } 618 } 619 620 621 #ifdef __HAIKU__ 622 void 623 TSwitchManager::_SortApps() 624 { 625 team_id* teams; 626 int32 count; 627 if (BPrivate::get_application_order(current_workspace(), &teams, &count) 628 != B_OK) 629 return; 630 631 BList groups; 632 if (!groups.AddList(&fGroupList)) { 633 free(teams); 634 return; 635 } 636 637 fGroupList.MakeEmpty(); 638 639 for (int32 i = 0; i < count; i++) { 640 // find team 641 TTeamGroup* info = NULL; 642 for (int32 j = 0; (info = (TTeamGroup*)groups.ItemAt(j)) != NULL; j++) { 643 if (info->TeamList()->HasItem((void*)teams[i])) { 644 groups.RemoveItem(j); 645 break; 646 } 647 } 648 649 if (info != NULL) 650 fGroupList.AddItem(info); 651 } 652 653 fGroupList.AddList(&groups); 654 // add the remaining entries 655 free(teams); 656 } 657 #endif // __HAIKU__ 658 659 660 void 661 TSwitchManager::MainEntry(BMessage* message) 662 { 663 bigtime_t now = system_time(); 664 bigtime_t timeout = now + 180000; 665 // The delay above was arrived at by trial and error and 666 // has a good "feel" 667 668 app_info appInfo; 669 be_roster->GetActiveAppInfo(&appInfo); 670 671 bool resetQuickSwitch = false; 672 #ifdef __HAIKU__ 673 if (now > fLastSwitch + 400000) { 674 _SortApps(); 675 resetQuickSwitch = true; 676 } 677 678 fLastSwitch = now; 679 #endif 680 681 int32 index; 682 fCurrentIndex = FindTeam(appInfo.team, &index) != NULL ? index : 0; 683 684 if (resetQuickSwitch) { 685 fQuickSwitchIndex = fCurrentIndex; 686 fQuickSwitchWindow = fCurrentWindow; 687 } 688 689 int32 key; 690 message->FindInt32("key", (int32*)&key); 691 692 uint32 modifierKeys = 0; 693 while (system_time() < timeout) { 694 modifierKeys = modifiers(); 695 if (!IsKeyDown(key)) { 696 QuickSwitch(message); 697 return; 698 } 699 if ((modifierKeys & B_CONTROL_KEY) == 0) { 700 QuickSwitch(message); 701 return; 702 } 703 snooze(20000); 704 // Must be a multiple of the delay used above 705 } 706 707 Process((modifierKeys & B_SHIFT_KEY) == 0, key == 0x11); 708 } 709 710 711 void 712 TSwitchManager::Stop(bool do_action, uint32) 713 { 714 fWindow->Hide(); 715 if (do_action) 716 ActivateApp(true, true); 717 718 release_sem(fMainMonitor); 719 } 720 721 722 TTeamGroup* 723 TSwitchManager::FindTeam(team_id teamID, int32* index) 724 { 725 int i = 0; 726 TTeamGroup* info; 727 while ((info = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) { 728 if (info->TeamList()->HasItem((void*)teamID)) { 729 *index = i; 730 return info; 731 } 732 i++; 733 } 734 735 return NULL; 736 } 737 738 739 void 740 TSwitchManager::Process(bool forward, bool byWindow) 741 { 742 bool hidden = false; 743 if (fWindow->Lock()) { 744 hidden = fWindow->IsHidden(); 745 fWindow->Unlock(); 746 } 747 if (byWindow) { 748 // If hidden we need to get things started by switching to correct app 749 if (hidden) 750 SwitchToApp(fCurrentIndex, fCurrentIndex, forward); 751 CycleWindow(forward, true); 752 } else 753 CycleApp(forward, false); 754 755 if (hidden) { 756 // more auto keyrepeat code 757 // Because of key repeats we don't want to respond to any extraneous 758 // 'TASK' messages until the window is completely shown. So block here. 759 // the WindowActivated hook function will unblock. 760 fBlock = true; 761 762 if (fWindow->Lock()) { 763 BRect screenFrame = BScreen().Frame(); 764 BRect windowFrame = fWindow->Frame(); 765 766 if (!screenFrame.Contains(windowFrame)) { 767 // center the window 768 BPoint point((screenFrame.left + screenFrame.right) / 2, 769 (screenFrame.top + screenFrame.bottom) / 2); 770 771 point.x -= (windowFrame.Width() / 2); 772 point.y -= (windowFrame.Height() / 2); 773 fWindow->MoveTo(point); 774 } 775 776 fWindow->Show(); 777 fWindow->Unlock(); 778 } 779 } 780 } 781 782 783 void 784 TSwitchManager::QuickSwitch(BMessage* message) 785 { 786 uint32 modifiers = 0; 787 message->FindInt32("modifiers", (int32*)&modifiers); 788 int32 key = 0; 789 message->FindInt32("key", &key); 790 791 team_id team; 792 if (message->FindInt32("team", &team) == B_OK) { 793 bool forward = (modifiers & B_SHIFT_KEY) == 0; 794 795 if (key == 0x11) { 796 // TODO: add the same switch logic we have for apps! 797 SwitchWindow(team, forward, true); 798 } else { 799 if (fQuickSwitchIndex >= 0) { 800 // Switch to the first app inbetween to make it always the next 801 // app to switch to after the quick switch. 802 int32 current = fCurrentIndex; 803 SwitchToApp(current, fQuickSwitchIndex, false); 804 ActivateApp(false, false); 805 806 fCurrentIndex = current; 807 } 808 809 CycleApp(forward, true); 810 } 811 } 812 813 release_sem(fMainMonitor); 814 } 815 816 817 int32 818 TSwitchManager::CountVisibleGroups() 819 { 820 int32 result = 0; 821 822 int32 count = fGroupList.CountItems(); 823 for (int32 i = 0; i < count; i++) { 824 if (!OKToUse((TTeamGroup*)fGroupList.ItemAt(i))) 825 continue; 826 827 result++; 828 } 829 return result; 830 } 831 832 833 void 834 TSwitchManager::CycleWindow(bool forward, bool wrap) 835 { 836 int32 max = CountWindows(fCurrentIndex); 837 int32 prev = fCurrentWindow; 838 int32 next = fCurrentWindow; 839 840 if (forward) { 841 next++; 842 if (next >= max) { 843 if (!wrap) 844 return; 845 next = 0; 846 } 847 } else { 848 next--; 849 if (next < 0) { 850 if (!wrap) 851 return; 852 next = max - 1; 853 } 854 } 855 fCurrentWindow = next; 856 857 if (fCurrentWindow != prev) 858 fWindow->WindowView()->ShowIndex(fCurrentWindow); 859 } 860 861 862 void 863 TSwitchManager::CycleApp(bool forward, bool activateNow) 864 { 865 int32 startIndex = fCurrentIndex; 866 int32 max = fGroupList.CountItems(); 867 868 for (;;) { 869 if (forward) { 870 fCurrentIndex++; 871 if (fCurrentIndex >= max) 872 fCurrentIndex = 0; 873 } else { 874 fCurrentIndex--; 875 if (fCurrentIndex < 0) 876 fCurrentIndex = max - 1; 877 } 878 if (fCurrentIndex == startIndex) { 879 // we've gone completely through the list without finding 880 // a good app. Oh well. 881 return; 882 } 883 884 if (!OKToUse((TTeamGroup*)fGroupList.ItemAt(fCurrentIndex))) 885 continue; 886 887 // if we're here then we found a good one 888 SwitchToApp(startIndex, fCurrentIndex, forward); 889 890 if (!activateNow) 891 break; 892 893 if (ActivateApp(false, false)) 894 break; 895 } 896 } 897 898 899 void 900 TSwitchManager::SwitchToApp(int32 previousIndex, int32 newIndex, bool forward) 901 { 902 int32 previousSlot = fCurrentSlot; 903 904 fCurrentIndex = newIndex; 905 fCurrentSlot = fWindow->SlotOf(fCurrentIndex); 906 fCurrentWindow = 0; 907 908 fWindow->Update(previousIndex, fCurrentIndex, previousSlot, fCurrentSlot, 909 forward); 910 } 911 912 913 bool 914 TSwitchManager::ActivateApp(bool forceShow, bool allowWorkspaceSwitch) 915 { 916 // Let's get the info about the selected window. If it doesn't exist 917 // anymore then get info about first window. If that doesn't exist then 918 // do nothing. 919 client_window_info* windowInfo = WindowInfo(fCurrentIndex, fCurrentWindow); 920 if (windowInfo == NULL) { 921 windowInfo = WindowInfo(fCurrentIndex, 0); 922 if (windowInfo == NULL) 923 return false; 924 } 925 926 int32 currentWorkspace = current_workspace(); 927 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 928 // Let's handle the easy case first: There's only 1 team in the group 929 if (teamGroup->TeamList()->CountItems() == 1) { 930 bool result; 931 if (forceShow && (fCurrentWindow != 0 || windowInfo->is_mini)) { 932 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT, 933 BRect(0, 0, 0, 0), false); 934 } 935 936 if (!forceShow && windowInfo->is_mini) { 937 // we aren't unhiding minimized windows, so we can't do 938 // anything here 939 result = false; 940 } else if (!allowWorkspaceSwitch 941 && (windowInfo->workspaces & (1 << currentWorkspace)) == 0) { 942 // we're not supposed to switch workspaces so abort. 943 result = false; 944 } else { 945 result = true; 946 be_roster->ActivateApp((team_id)teamGroup->TeamList()->ItemAt(0)); 947 } 948 949 ASSERT(windowInfo); 950 free(windowInfo); 951 return result; 952 } 953 954 // Now the trickier case. We're trying to Bring to the Front a group 955 // of teams. The current window (defined by fCurrentWindow) will define 956 // which workspace we're going to. Then, once that is determined we 957 // want to bring to the front every window of the group of teams that 958 // lives in that workspace. 959 960 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) { 961 if (!allowWorkspaceSwitch) { 962 // If the first window in the list isn't in current workspace, 963 // then none are. So we can't switch to this app. 964 ASSERT(windowInfo); 965 free(windowInfo); 966 return false; 967 } 968 int32 destWorkspace = LowBitIndex(windowInfo->workspaces); 969 // now switch to that workspace 970 activate_workspace(destWorkspace); 971 } 972 973 if (!forceShow && windowInfo->is_mini) { 974 // If the first window in the list is hidden then no windows in 975 // this group are visible. So we can't switch to this app. 976 ASSERT(windowInfo); 977 free(windowInfo); 978 return false; 979 } 980 981 int32 tokenCount; 982 int32* tokens = get_token_list(-1, &tokenCount); 983 if (tokens == NULL) { 984 ASSERT(windowInfo); 985 free(windowInfo); 986 return true; // weird error, so don't try to recover 987 } 988 989 BList windowsToActivate; 990 991 // Now we go through all the windows in the current workspace list in order. 992 // As we hit member teams we build the "activate" list. 993 for (int32 i = 0; i < tokenCount; i++) { 994 client_window_info* matchWindowInfo = get_window_info(tokens[i]); 995 if (!matchWindowInfo) { 996 // That window probably closed. Just go to the next one. 997 continue; 998 } 999 if (!IsVisibleInCurrentWorkspace(matchWindowInfo)) { 1000 // first non-visible in workspace window means we're done. 1001 free(matchWindowInfo); 1002 break; 1003 } 1004 if (matchWindowInfo->server_token != windowInfo->server_token 1005 && teamGroup->TeamList()->HasItem((void*)matchWindowInfo->team)) 1006 windowsToActivate.AddItem((void*)matchWindowInfo->server_token); 1007 1008 free(matchWindowInfo); 1009 } 1010 1011 free(tokens); 1012 1013 // Want to go through the list backwards to keep windows in same relative 1014 // order. 1015 int32 i = windowsToActivate.CountItems() - 1; 1016 for (; i >= 0; i--) { 1017 int32 wid = (int32) windowsToActivate.ItemAt(i); 1018 do_window_action(wid, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false); 1019 } 1020 1021 // now bring the select window on top of everything. 1022 1023 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT, 1024 BRect(0, 0, 0, 0), false); 1025 1026 free(windowInfo); 1027 return true; 1028 } 1029 1030 1031 void 1032 TSwitchManager::QuitApp() 1033 { 1034 // check if we're in the last slot already (the last usable team group) 1035 1036 TTeamGroup* teamGroup; 1037 int32 count = 0; 1038 for (int32 i = fCurrentIndex + 1; i < fGroupList.CountItems(); i++) { 1039 teamGroup = (TTeamGroup*)fGroupList.ItemAt(i); 1040 1041 if (!OKToUse(teamGroup)) 1042 continue; 1043 1044 count++; 1045 } 1046 1047 teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 1048 1049 if (count == 0) { 1050 // switch to previous app in the list so that we don't jump to 1051 // the start of the list (try to keep the same position when 1052 // the apps at the current index go away) 1053 CycleApp(false, false); 1054 } 1055 1056 // send the quit request to all teams in this group 1057 1058 for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) { 1059 team_id team = (team_id)teamGroup->TeamList()->ItemAt(i); 1060 app_info info; 1061 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) { 1062 if (!strcasecmp(info.signature, kTrackerSignature)) { 1063 // Tracker can't be quit this way 1064 continue; 1065 } 1066 1067 BMessenger messenger(NULL, team); 1068 messenger.SendMessage(B_QUIT_REQUESTED); 1069 } 1070 } 1071 } 1072 1073 1074 void 1075 TSwitchManager::HideApp() 1076 { 1077 // hide all teams in this group 1078 1079 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex); 1080 1081 for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) { 1082 team_id team = (team_id)teamGroup->TeamList()->ItemAt(i); 1083 app_info info; 1084 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) 1085 do_minimize_team(BRect(), team, false); 1086 } 1087 } 1088 1089 1090 client_window_info* 1091 TSwitchManager::WindowInfo(int32 groupIndex, int32 windowIndex) 1092 { 1093 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex); 1094 if (teamGroup == NULL) 1095 return NULL; 1096 1097 int32 tokenCount; 1098 int32* tokens = get_token_list(-1, &tokenCount); 1099 if (tokens == NULL) 1100 return NULL; 1101 1102 int32 matches = 0; 1103 1104 // Want to find the "windowIndex'th" window in window order that belongs 1105 // the the specified group (groupIndex). Since multiple teams can belong to 1106 // the same group (multiple-launch apps) we get the list of _every_ 1107 // window and go from there. 1108 1109 client_window_info* result = NULL; 1110 for (int32 i = 0; i < tokenCount; i++) { 1111 client_window_info* windowInfo = get_window_info(tokens[i]); 1112 if (windowInfo) { 1113 // skip hidden/special windows 1114 if (IsWindowOK(windowInfo) 1115 && (teamGroup->TeamList()->HasItem((void*)windowInfo->team))) { 1116 // this window belongs to the team! 1117 if (matches == windowIndex) { 1118 // we found it! 1119 result = windowInfo; 1120 break; 1121 } 1122 matches++; 1123 } 1124 free(windowInfo); 1125 } 1126 // else - that window probably closed. Just go to the next one. 1127 } 1128 1129 free(tokens); 1130 1131 return result; 1132 } 1133 1134 1135 int32 1136 TSwitchManager::CountWindows(int32 groupIndex, bool ) 1137 { 1138 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex); 1139 if (!teamGroup) 1140 return 0; 1141 1142 int32 result = 0; 1143 1144 for (int32 i = 0; ; i++) { 1145 team_id teamID = (team_id)teamGroup->TeamList()->ItemAt(i); 1146 if (teamID == 0) 1147 break; 1148 1149 int32 count; 1150 int32* tokens = get_token_list(teamID, &count); 1151 if (!tokens) 1152 continue; 1153 1154 for (int32 i = 0; i < count; i++) { 1155 window_info *windowInfo = get_window_info(tokens[i]); 1156 if (windowInfo) { 1157 if (IsWindowOK(windowInfo)) 1158 result++; 1159 free(windowInfo); 1160 } 1161 } 1162 free(tokens); 1163 } 1164 1165 return result; 1166 } 1167 1168 1169 void 1170 TSwitchManager::ActivateWindow(int32 windowID) 1171 { 1172 if (windowID == -1) 1173 windowID = fWindowID; 1174 1175 do_window_action(windowID, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false); 1176 } 1177 1178 1179 void 1180 TSwitchManager::SwitchWindow(team_id team, bool, bool activate) 1181 { 1182 // Find the _last_ window in the current workspace that belongs 1183 // to the group. This is the window to activate. 1184 1185 int32 index; 1186 TTeamGroup* teamGroup = FindTeam(team, &index); 1187 1188 // cycle through the windows in the active application 1189 int32 count; 1190 int32* tokens = get_token_list(-1, &count); 1191 if (tokens == NULL) 1192 return; 1193 1194 for (int32 i = count - 1; i >= 0; i--) { 1195 client_window_info* windowInfo = get_window_info(tokens[i]); 1196 if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo) 1197 && teamGroup->TeamList()->HasItem((void*)windowInfo->team)) { 1198 fWindowID = windowInfo->server_token; 1199 if (activate) 1200 ActivateWindow(windowInfo->server_token); 1201 1202 free(windowInfo); 1203 break; 1204 } 1205 free(windowInfo); 1206 } 1207 free(tokens); 1208 } 1209 1210 1211 void 1212 TSwitchManager::Unblock() 1213 { 1214 fBlock = false; 1215 fSkipUntil = system_time(); 1216 } 1217 1218 1219 int32 1220 TSwitchManager::CurrentIndex() 1221 { 1222 return fCurrentIndex; 1223 } 1224 1225 1226 int32 1227 TSwitchManager::CurrentWindow() 1228 { 1229 return fCurrentWindow; 1230 } 1231 1232 1233 int32 1234 TSwitchManager::CurrentSlot() 1235 { 1236 return fCurrentSlot; 1237 } 1238 1239 1240 BList* 1241 TSwitchManager::GroupList() 1242 { 1243 return &fGroupList; 1244 } 1245 1246 1247 // #pragma mark - 1248 1249 1250 TBox::TBox(BRect bounds, TSwitchManager* manager, TSwitcherWindow* window, 1251 TIconView* iview) 1252 : BBox(bounds, "top", B_FOLLOW_NONE, B_WILL_DRAW, B_NO_BORDER), 1253 fManager(manager), 1254 fWindow(window), 1255 fIconView(iview), 1256 fLeftScroller(false), 1257 fRightScroller(false) 1258 { 1259 } 1260 1261 1262 void 1263 TBox::AllAttached() 1264 { 1265 BRect centerRect(kCenterSlot * kSlotSize, 0, 1266 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1267 BRect frame = fIconView->Frame(); 1268 1269 // scroll the centerRect to correct location 1270 centerRect.OffsetBy(frame.left, frame.top); 1271 1272 // switch to local coords 1273 fIconView->ConvertToParent(¢erRect); 1274 1275 fCenter = centerRect; 1276 } 1277 1278 1279 void 1280 TBox::MouseDown(BPoint where) 1281 { 1282 if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller) 1283 return; 1284 1285 BRect frame = fIconView->Frame(); 1286 BRect bounds = Bounds(); 1287 1288 if (fLeftScroller) { 1289 BRect lhit(0, frame.top, frame.left, frame.bottom); 1290 if (lhit.Contains(where)) { 1291 // Want to scroll by NUMSLOTS-1 slots 1292 int32 previousIndex = fManager->CurrentIndex(); 1293 int32 previousSlot = fManager->CurrentSlot(); 1294 int32 newSlot = previousSlot - (kNumSlots - 1); 1295 if (newSlot < 0) 1296 newSlot = 0; 1297 int32 newIndex = fIconView->IndexAt(newSlot); 1298 1299 fManager->SwitchToApp(previousIndex, newIndex, false); 1300 } 1301 } 1302 1303 if (fRightScroller) { 1304 BRect rhit(frame.right, frame.top, bounds.right, frame.bottom); 1305 if (rhit.Contains(where)) { 1306 // Want to scroll by NUMSLOTS-1 slots 1307 int32 previousIndex = fManager->CurrentIndex(); 1308 int32 previousSlot = fManager->CurrentSlot(); 1309 int32 newSlot = previousSlot + (kNumSlots-1); 1310 int32 newIndex = fIconView->IndexAt(newSlot); 1311 1312 if (newIndex < 0) { 1313 // don't have a page full to scroll 1314 int32 valid = fManager->CountVisibleGroups(); 1315 newIndex = fIconView->IndexAt(valid-1); 1316 } 1317 fManager->SwitchToApp(previousIndex, newIndex, true); 1318 } 1319 } 1320 1321 frame = fWindow->WindowView()->Frame(); 1322 if (fUpScroller) { 1323 BRect hit1(frame.left - 10, frame.top, frame.left, (frame.top+frame.bottom)/2); 1324 BRect hit2(frame.right, frame.top, frame.right + 10, (frame.top+frame.bottom)/2); 1325 if (hit1.Contains(where) || hit2.Contains(where)) { 1326 // Want to scroll up 1 window 1327 fManager->CycleWindow(false, false); 1328 } 1329 } 1330 1331 if (fDownScroller) { 1332 BRect hit1(frame.left - 10, (frame.top+frame.bottom) / 2, frame.left, frame.bottom); 1333 BRect hit2(frame.right, (frame.top+frame.bottom) / 2, frame.right + 10, frame.bottom); 1334 if (hit1.Contains(where) || hit2.Contains(where)) { 1335 // Want to scroll down 1 window 1336 fManager->CycleWindow(true, false); 1337 } 1338 } 1339 } 1340 1341 1342 void 1343 TBox::Draw(BRect update) 1344 { 1345 static const int32 kChildInset = 7; 1346 static const int32 kWedge = 6; 1347 1348 BBox::Draw(update); 1349 1350 // The fancy border around the icon view 1351 1352 BRect bounds = Bounds(); 1353 float height = fIconView->Bounds().Height(); 1354 float center = (bounds.right + bounds.left) / 2; 1355 1356 BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2); 1357 rgb_color white = {255, 255, 255, 255}; 1358 rgb_color standardGray = ui_color(B_PANEL_BACKGROUND_COLOR); 1359 rgb_color veryDarkGray = {128, 128, 128, 255}; 1360 rgb_color darkGray = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT); 1361 1362 // Fill the area with dark gray 1363 SetHighColor(darkGray); 1364 box.InsetBy(1,1); 1365 FillRect(box); 1366 1367 box.InsetBy(-1,-1); 1368 1369 BeginLineArray(50); 1370 1371 // The main frame around the icon view 1372 AddLine(box.LeftTop(), BPoint(center-kWedge, box.top), veryDarkGray); 1373 AddLine(BPoint(center+kWedge, box.top), box.RightTop(), veryDarkGray); 1374 1375 AddLine(box.LeftBottom(), BPoint(center-kWedge, box.bottom), veryDarkGray); 1376 AddLine(BPoint(center+kWedge, box.bottom), box.RightBottom(), veryDarkGray); 1377 AddLine(box.LeftBottom() + BPoint(1, 1), 1378 BPoint(center-kWedge, box.bottom + 1), white); 1379 AddLine(BPoint(center+kWedge, box.bottom) + BPoint(0, 1), 1380 box.RightBottom() + BPoint(1, 1), white); 1381 1382 AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray); 1383 AddLine(box.RightTop(), box.RightBottom(), veryDarkGray); 1384 AddLine(box.RightTop() + BPoint(1, 1), 1385 box.RightBottom() + BPoint(1, 1), white); 1386 1387 // downward pointing area at top of frame 1388 BPoint point(center - kWedge, box.top); 1389 AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray); 1390 AddLine(point + BPoint(kWedge, kWedge), 1391 BPoint(center+kWedge, point.y), veryDarkGray); 1392 1393 AddLine(point + BPoint(1, 0), 1394 point + BPoint(1, 0) + BPoint(kWedge - 1, kWedge - 1), white); 1395 1396 AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1), 1397 BPoint(center+kWedge-1, point.y), darkGray); 1398 1399 BPoint topPoint = point; 1400 1401 // upward pointing area at bottom of frame 1402 point.y = box.bottom; 1403 point.x = center - kWedge; 1404 AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray); 1405 AddLine(point + BPoint(kWedge, -kWedge), 1406 BPoint(center+kWedge, point.y), veryDarkGray); 1407 1408 AddLine(point + BPoint(1, 0), 1409 point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white); 1410 1411 AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)), 1412 BPoint(center + kWedge - 1, point.y), darkGray); 1413 1414 BPoint bottomPoint = point; 1415 1416 EndLineArray(); 1417 1418 // fill the downward pointing arrow area 1419 SetHighColor(standardGray); 1420 FillTriangle(topPoint + BPoint(2, 0), 1421 topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2), 1422 BPoint(center + kWedge - 2, topPoint.y)); 1423 1424 // fill the upward pointing arrow area 1425 SetHighColor(standardGray); 1426 FillTriangle(bottomPoint + BPoint(2,0), 1427 bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)), 1428 BPoint(center + kWedge - 2, bottomPoint.y)); 1429 1430 DrawIconScrollers(false); 1431 DrawWindowScrollers(false); 1432 1433 } 1434 1435 1436 void 1437 TBox::DrawIconScrollers(bool force) 1438 { 1439 bool updateLeft = false; 1440 bool updateRight = false; 1441 rgb_color leftc = {0, 0, 0, 255}; 1442 rgb_color rightc = {0, 0, 0, 255}; 1443 rgb_color bkg = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT); 1444 rgb_color dark = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT); 1445 1446 BRect rect = fIconView->Bounds(); 1447 if (rect.left > (kSlotSize * kCenterSlot)) { 1448 updateLeft = true; 1449 fLeftScroller = true; 1450 leftc = dark; 1451 } else { 1452 fLeftScroller = false; 1453 if (force) { 1454 updateLeft = true; 1455 leftc = bkg; 1456 } 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 rightc = dark; 1467 } else { 1468 fRightScroller = false; 1469 if (force) { 1470 updateRight = true; 1471 rightc = bkg; 1472 } 1473 } 1474 1475 rect = fIconView->Frame(); 1476 if (updateLeft) { 1477 SetHighColor(leftc); 1478 BPoint pt1, pt2, pt3; 1479 pt1.x = rect.left - 5; 1480 pt1.y = floorf((rect.bottom + rect.top) / 2); 1481 pt2.x = pt3.x = pt1.x + 3; 1482 pt2.y = pt1.y - 3; 1483 pt3.y = pt1.y + 3; 1484 FillTriangle(pt1, pt2, pt3); 1485 } 1486 if (updateRight) { 1487 SetHighColor(rightc); 1488 BPoint pt1, pt2, pt3; 1489 pt1.x = rect.right + 4; 1490 pt1.y = rintf((rect.bottom + rect.top) / 2); 1491 pt2.x = pt3.x = pt1.x - 4; 1492 pt2.y = pt1.y - 4; 1493 pt3.y = pt1.y + 4; 1494 FillTriangle(pt1, pt2, pt3); 1495 } 1496 } 1497 1498 1499 void 1500 TBox::DrawWindowScrollers(bool force) 1501 { 1502 bool updateUp = false; 1503 bool updateDown = false; 1504 rgb_color upColor = {0, 0, 0, 255}; 1505 rgb_color downColor = {0, 0, 0, 255}; 1506 rgb_color bkg = ui_color(B_PANEL_BACKGROUND_COLOR); 1507 rgb_color dark = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT); 1508 1509 BRect rect = fWindow->WindowView()->Bounds(); 1510 if (rect.top != 0) { 1511 updateUp = true; 1512 fUpScroller = true; 1513 upColor = dark; 1514 } else { 1515 fUpScroller = false; 1516 if (force) { 1517 updateUp = true; 1518 upColor = bkg; 1519 } 1520 } 1521 1522 int32 groupIndex = fManager->CurrentIndex(); 1523 int32 maxIndex = fManager->CountWindows(groupIndex) - 1; 1524 1525 BRect lastFrame(0, 0, 0, 0); 1526 if (maxIndex >= 0) { 1527 lastFrame = fWindow->WindowView()->FrameOf(maxIndex); 1528 } 1529 if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) { 1530 updateDown = true; 1531 fDownScroller = true; 1532 downColor = dark; 1533 } else { 1534 fDownScroller = false; 1535 if (force) { 1536 updateDown = true; 1537 downColor = bkg; 1538 } 1539 } 1540 1541 rect = fWindow->WindowView()->Frame(); 1542 rect.InsetBy(-3, 0); 1543 if (updateUp) { 1544 SetHighColor(upColor); 1545 BPoint pt1, pt2, pt3; 1546 pt1.x = rect.left - 6; 1547 pt1.y = rect.top + 3; 1548 pt2.y = pt3.y = pt1.y + 4; 1549 pt2.x = pt1.x - 4; 1550 pt3.x = pt1.x + 4; 1551 FillTriangle(pt1, pt2, pt3); 1552 1553 pt1.x += rect.Width() + 12; 1554 pt2.x += rect.Width() + 12; 1555 pt3.x += rect.Width() + 12; 1556 FillTriangle(pt1, pt2, pt3); 1557 } 1558 if (updateDown) { 1559 SetHighColor(downColor); 1560 BPoint pt1, pt2, pt3; 1561 pt1.x = rect.left - 6; 1562 pt1.y = rect.bottom - 3; 1563 pt2.y = pt3.y = pt1.y - 4; 1564 pt2.x = pt1.x - 4; 1565 pt3.x = pt1.x + 4; 1566 FillTriangle(pt1, pt2, pt3); 1567 1568 pt1.x += rect.Width() + 12; 1569 pt2.x += rect.Width() + 12; 1570 pt3.x += rect.Width() + 12; 1571 FillTriangle(pt1, pt2, pt3); 1572 1573 } 1574 Sync(); 1575 } 1576 1577 1578 // #pragma mark - 1579 1580 1581 TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager* manager) 1582 : BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK, 1583 B_MODAL_ALL_WINDOW_FEEL, 1584 B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES), 1585 fManager(manager), 1586 fHairTrigger(true) 1587 { 1588 BRect rect = frame; 1589 rect.OffsetTo(B_ORIGIN); 1590 rect.InsetBy(kHorizontalMargin, 0); 1591 rect.top = kVerticalMargin; 1592 rect.bottom = rect.top + kSlotSize - 1; 1593 1594 fIconView = new TIconView(rect, manager, this); 1595 1596 rect.top = rect.bottom + (kVerticalMargin * 1 + 4); 1597 rect.InsetBy(9, 0); 1598 1599 fWindowView = new TWindowView(rect, manager, this); 1600 fWindowView->ResizeToPreferred(); 1601 1602 fTopView = new TBox(Bounds(), fManager, this, fIconView); 1603 AddChild(fTopView); 1604 1605 SetPulseRate(0); 1606 fTopView->AddChild(fIconView); 1607 fTopView->AddChild(fWindowView); 1608 } 1609 1610 1611 TSwitcherWindow::~TSwitcherWindow() 1612 { 1613 } 1614 1615 1616 void 1617 TSwitcherWindow::MessageReceived(BMessage* message) 1618 { 1619 switch (message->what) { 1620 case B_UNMAPPED_KEY_DOWN: 1621 case B_KEY_DOWN: 1622 { 1623 int32 repeats = 0; 1624 if (message->FindInt32("be:key_repeat", &repeats) == B_OK 1625 && (fSkipKeyRepeats || (repeats % 6) != 0)) 1626 break; 1627 1628 // The first actual key press let's us listening to repeated keys 1629 fSkipKeyRepeats = false; 1630 1631 uint32 rawChar; 1632 uint32 modifiers; 1633 message->FindInt32("raw_char", 0, (int32*)&rawChar); 1634 message->FindInt32("modifiers", 0, (int32*)&modifiers); 1635 DoKey(rawChar, modifiers); 1636 break; 1637 } 1638 1639 default: 1640 BWindow::MessageReceived(message); 1641 } 1642 } 1643 1644 1645 void 1646 TSwitcherWindow::Redraw(int32 index) 1647 { 1648 BRect frame = fIconView->FrameOf(index); 1649 frame.right = fIconView->Bounds().right; 1650 fIconView->Invalidate(frame); 1651 } 1652 1653 1654 void 1655 TSwitcherWindow::DoKey(uint32 key, uint32 modifiers) 1656 { 1657 bool forward = ((modifiers & B_SHIFT_KEY) == 0); 1658 1659 switch (key) { 1660 case B_RIGHT_ARROW: 1661 fManager->CycleApp(true, false); 1662 break; 1663 1664 case B_LEFT_ARROW: 1665 case '1': 1666 fManager->CycleApp(false, false); 1667 break; 1668 1669 case B_UP_ARROW: 1670 fManager->CycleWindow(false, false); 1671 break; 1672 1673 case B_DOWN_ARROW: 1674 fManager->CycleWindow(true, false); 1675 break; 1676 1677 case B_TAB: 1678 fManager->CycleApp(forward, false); 1679 break; 1680 1681 case B_ESCAPE: 1682 fManager->Stop(false, 0); 1683 break; 1684 1685 case B_SPACE: 1686 case B_ENTER: 1687 fManager->Stop(true, modifiers); 1688 break; 1689 1690 case 'q': 1691 case 'Q': 1692 fManager->QuitApp(); 1693 break; 1694 1695 case 'h': 1696 case 'H': 1697 fManager->HideApp(); 1698 break; 1699 1700 #if _ALLOW_STICKY_ 1701 case 's': 1702 case 'S': 1703 if (fHairTrigger) { 1704 SetLook(B_TITLED_WINDOW_LOOK); 1705 fHairTrigger = false; 1706 } else { 1707 SetLook(B_MODAL_WINDOW_LOOK); 1708 fHairTrigger = true; 1709 } 1710 break; 1711 #endif 1712 } 1713 } 1714 1715 1716 bool 1717 TSwitcherWindow::QuitRequested() 1718 { 1719 ((TBarApp*)be_app)->Settings()->switcherLoc = Frame().LeftTop(); 1720 fManager->Stop(false, 0); 1721 return false; 1722 } 1723 1724 1725 void 1726 TSwitcherWindow::WindowActivated(bool state) 1727 { 1728 if (state) 1729 fManager->Unblock(); 1730 } 1731 1732 1733 void 1734 TSwitcherWindow::Update(int32 prev, int32 current, int32 previousSlot, 1735 int32 currentSlot, bool forward) 1736 { 1737 if (!IsHidden()) 1738 fIconView->Update(prev, current, previousSlot, currentSlot, forward); 1739 else 1740 fIconView->CenterOn(current); 1741 1742 fWindowView->UpdateGroup(current, 0); 1743 } 1744 1745 1746 void 1747 TSwitcherWindow::Hide() 1748 { 1749 fIconView->Hiding(); 1750 SetPulseRate(0); 1751 BWindow::Hide(); 1752 } 1753 1754 1755 void 1756 TSwitcherWindow::Show() 1757 { 1758 fHairTrigger = true; 1759 fSkipKeyRepeats = true; 1760 fIconView->Showing(); 1761 SetPulseRate(100000); 1762 SetLook(B_MODAL_WINDOW_LOOK); 1763 BWindow::Show(); 1764 } 1765 1766 1767 TBox* 1768 TSwitcherWindow::TopView() 1769 { 1770 return fTopView; 1771 } 1772 1773 1774 bool 1775 TSwitcherWindow::HairTrigger() 1776 { 1777 return fHairTrigger; 1778 } 1779 1780 1781 inline int32 1782 TSwitcherWindow::SlotOf(int32 i) 1783 { 1784 return fIconView->SlotOf(i); 1785 } 1786 1787 1788 inline TIconView* 1789 TSwitcherWindow::IconView() 1790 { 1791 return fIconView; 1792 } 1793 1794 1795 inline TWindowView* 1796 TSwitcherWindow::WindowView() 1797 { 1798 return fWindowView; 1799 } 1800 1801 1802 // #pragma mark - 1803 1804 1805 TIconView::TIconView(BRect frame, TSwitchManager* manager, 1806 TSwitcherWindow* switcherWindow) 1807 : BView(frame, "main_view", B_FOLLOW_NONE, 1808 B_WILL_DRAW | B_PULSE_NEEDED), 1809 fAutoScrolling(false), 1810 fSwitcher(switcherWindow), 1811 fManager(manager) 1812 { 1813 BRect rect(0, 0, kSlotSize - 1, kSlotSize - 1); 1814 rgb_color color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT); 1815 1816 fOffView = new BView(rect, "off_view", B_FOLLOW_NONE, B_WILL_DRAW); 1817 fOffView->SetHighColor(color); 1818 fOffBitmap = new BBitmap(rect, B_RGB32, true); 1819 fOffBitmap->AddChild(fOffView); 1820 1821 fCurrentSmall = new BBitmap(BRect(0, 0, 15, 15), kIconFormat); 1822 fCurrentLarge = new BBitmap(BRect(0, 0, 31, 31), kIconFormat); 1823 1824 SetViewColor(color); 1825 SetLowColor(color); 1826 } 1827 1828 1829 TIconView::~TIconView() 1830 { 1831 delete fCurrentSmall; 1832 delete fCurrentLarge; 1833 delete fOffBitmap; 1834 } 1835 1836 1837 void 1838 TIconView::KeyDown(const char* /*bytes*/, int32 /*numBytes*/) 1839 { 1840 } 1841 1842 1843 void 1844 TIconView::CacheIcons(TTeamGroup* teamGroup) 1845 { 1846 const BBitmap* bitmap = teamGroup->SmallIcon(); 1847 ASSERT(bitmap); 1848 fCurrentSmall->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1849 bitmap->ColorSpace()); 1850 1851 bitmap = teamGroup->LargeIcon(); 1852 ASSERT(bitmap); 1853 fCurrentLarge->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, 1854 bitmap->ColorSpace()); 1855 } 1856 1857 1858 void 1859 TIconView::AnimateIcon(BBitmap* startIcon, BBitmap* endIcon) 1860 { 1861 BRect centerRect(kCenterSlot*kSlotSize, 0, 1862 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 1863 BRect startIconBounds = startIcon->Bounds(); 1864 BRect bounds = Bounds(); 1865 float width = startIconBounds.Width(); 1866 int32 amount = (width < 20) ? -2 : 2; 1867 1868 // center the starting icon inside of centerRect 1869 float off = (centerRect.Width() - width) / 2; 1870 startIconBounds.OffsetTo(BPoint(off, off)); 1871 1872 // scroll the centerRect to correct location 1873 centerRect.OffsetBy(bounds.left, 0); 1874 1875 BRect destRect = fOffBitmap->Bounds(); 1876 // scroll to the centerRect location 1877 destRect.OffsetTo(centerRect.left, 0); 1878 // center the destRect inside of centerRect. 1879 off = (centerRect.Width() - destRect.Width()) / 2; 1880 destRect.OffsetBy(BPoint(off, off)); 1881 1882 fOffBitmap->Lock(); 1883 fOffView->SetDrawingMode(B_OP_ALPHA); 1884 for (int i = 0; i < 2; i++) { 1885 startIconBounds.InsetBy(amount,amount); 1886 snooze(20000); 1887 fOffView->FillRect(fOffView->Bounds()); 1888 fOffView->DrawBitmap(startIcon, startIconBounds); 1889 fOffView->Sync(); 1890 DrawBitmap(fOffBitmap, destRect); 1891 } 1892 for (int i = 0; i < 2; i++) { 1893 startIconBounds.InsetBy(amount,amount); 1894 snooze(20000); 1895 fOffView->FillRect(fOffView->Bounds()); 1896 fOffView->DrawBitmap(endIcon, startIconBounds); 1897 fOffView->Sync(); 1898 DrawBitmap(fOffBitmap, destRect); 1899 } 1900 1901 fOffView->SetDrawingMode(B_OP_COPY); 1902 fOffBitmap->Unlock(); 1903 } 1904 1905 1906 void 1907 TIconView::Update(int32, int32 current, int32 previousSlot, int32 currentSlot, 1908 bool forward) 1909 { 1910 // Animate the shrinking of the currently centered icon. 1911 AnimateIcon(fCurrentLarge, fCurrentSmall); 1912 1913 int32 nslots = abs(previousSlot - currentSlot); 1914 int32 stepSize = kScrollStep; 1915 1916 if (forward && (currentSlot < previousSlot)) { 1917 // we were at the end of the list and we just moved to the start 1918 forward = false; 1919 if (previousSlot - currentSlot > 4) 1920 stepSize *= 2; 1921 } else if (!forward && (currentSlot > previousSlot)) { 1922 // we're are moving backwards and we just hit start of list and 1923 // we wrapped to the end. 1924 forward = true; 1925 if (currentSlot - previousSlot > 4) 1926 stepSize *= 2; 1927 } 1928 1929 int32 scrollValue = forward ? stepSize : -stepSize; 1930 int32 total = 0; 1931 1932 fAutoScrolling = true; 1933 while (total < (nslots * kSlotSize)) { 1934 ScrollBy(scrollValue, 0); 1935 snooze(1000); 1936 total += stepSize; 1937 Window()->UpdateIfNeeded(); 1938 } 1939 fAutoScrolling = false; 1940 1941 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(current); 1942 ASSERT(teamGroup); 1943 CacheIcons(teamGroup); 1944 1945 // Animate the expansion of the currently centered icon 1946 AnimateIcon(fCurrentSmall, fCurrentLarge); 1947 } 1948 1949 1950 void 1951 TIconView::CenterOn(int32 index) 1952 { 1953 BRect rect = FrameOf(index); 1954 ScrollTo(rect.left - (kCenterSlot * kSlotSize), 0); 1955 } 1956 1957 1958 int32 1959 TIconView::ItemAtPoint(BPoint point) const 1960 { 1961 float tmpPointVerticalIndex = (point.x / kSlotSize) - kCenterSlot; 1962 if (tmpPointVerticalIndex < 0) 1963 return -1; 1964 1965 int32 pointVerticalIndex = (int32)tmpPointVerticalIndex; 1966 1967 for (int32 i = 0, verticalIndex = 0; ; i++) { 1968 1969 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(i); 1970 if (teamGroup == NULL) 1971 break; 1972 1973 if (!OKToUse(teamGroup)) 1974 continue; 1975 1976 if (verticalIndex == pointVerticalIndex) 1977 return i; 1978 1979 verticalIndex++; 1980 } 1981 return -1; 1982 } 1983 1984 1985 void 1986 TIconView::ScrollTo(BPoint where) 1987 { 1988 BView::ScrollTo(where); 1989 fSwitcher->TopView()->DrawIconScrollers(true); 1990 } 1991 1992 1993 int32 1994 TIconView::IndexAt(int32 slot) const 1995 { 1996 BList* list = fManager->GroupList(); 1997 int32 count = list->CountItems(); 1998 int32 slotIndex = 0; 1999 2000 for (int32 i = 0; i < count; i++) { 2001 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2002 2003 if (!OKToUse(teamGroup)) 2004 continue; 2005 2006 if (slotIndex == slot) { 2007 return i; 2008 } 2009 slotIndex++; 2010 } 2011 return -1; 2012 } 2013 2014 2015 int32 2016 TIconView::SlotOf(int32 index) const 2017 { 2018 BRect rect = FrameOf(index); 2019 return (int32)(rect.left / kSlotSize) - kCenterSlot; 2020 } 2021 2022 2023 BRect 2024 TIconView::FrameOf(int32 index) const 2025 { 2026 BList* list = fManager->GroupList(); 2027 int32 visible = kCenterSlot - 1; 2028 // first few slots in view are empty 2029 2030 TTeamGroup* teamGroup; 2031 for (int32 i = 0; i <= index; i++) { 2032 teamGroup = (TTeamGroup*)list->ItemAt(i); 2033 2034 if (!OKToUse(teamGroup)) 2035 continue; 2036 2037 visible++; 2038 } 2039 2040 return BRect(visible * kSlotSize, 0, (visible + 1) * kSlotSize - 1, kSlotSize - 1); 2041 } 2042 2043 2044 void 2045 TIconView::DrawTeams(BRect update) 2046 { 2047 int32 mainIndex = fManager->CurrentIndex(); 2048 BList* list = fManager->GroupList(); 2049 int32 count = list->CountItems(); 2050 2051 BRect rect(kCenterSlot * kSlotSize, 0, 2052 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1); 2053 2054 for (int32 i = 0; i < count; i++) { 2055 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i); 2056 2057 if (!OKToUse(teamGroup)) 2058 continue; 2059 2060 if (rect.Intersects(update) && teamGroup) { 2061 #ifdef __HAIKU__ 2062 SetDrawingMode(B_OP_ALPHA); 2063 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 2064 #else 2065 SetDrawingMode(B_OP_OVER); 2066 #endif 2067 teamGroup->Draw(this, rect, !fAutoScrolling && (i == mainIndex)); 2068 2069 if (i == mainIndex) 2070 CacheIcons(teamGroup); 2071 2072 SetDrawingMode(B_OP_COPY); 2073 } 2074 rect.OffsetBy(kSlotSize,0); 2075 } 2076 } 2077 2078 2079 void 2080 TIconView::Draw(BRect update) 2081 { 2082 DrawTeams(update); 2083 } 2084 2085 2086 void 2087 TIconView::MouseDown(BPoint where) 2088 { 2089 int32 index = ItemAtPoint(where); 2090 if (index >= 0) { 2091 int32 previousIndex = fManager->CurrentIndex(); 2092 int32 previousSlot = fManager->CurrentSlot(); 2093 int32 currentSlot = SlotOf(index); 2094 fManager->SwitchToApp(previousIndex, index, (currentSlot > previousSlot)); 2095 } 2096 } 2097 2098 2099 void 2100 TIconView::Pulse() 2101 { 2102 uint32 modifiersKeys = modifiers(); 2103 if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) { 2104 fManager->Stop(true, modifiersKeys); 2105 return; 2106 } 2107 2108 if (!fSwitcher->HairTrigger()) { 2109 uint32 buttons; 2110 BPoint point; 2111 GetMouse(&point, &buttons); 2112 if (buttons != 0) { 2113 point = ConvertToScreen(point); 2114 if (!Window()->Frame().Contains(point)) 2115 fManager->Stop(false, 0); 2116 } 2117 } 2118 } 2119 2120 2121 void 2122 TIconView::Showing() 2123 { 2124 } 2125 2126 2127 void 2128 TIconView::Hiding() 2129 { 2130 ScrollTo(B_ORIGIN); 2131 } 2132 2133 2134 // #pragma mark - 2135 2136 2137 TWindowView::TWindowView(BRect rect, TSwitchManager* manager, 2138 TSwitcherWindow* window) 2139 : BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED), 2140 fCurrentToken(-1), 2141 fSwitcher(window), 2142 fManager(manager) 2143 { 2144 SetFont(be_plain_font); 2145 } 2146 2147 2148 void 2149 TWindowView::AttachedToWindow() 2150 { 2151 if (Parent()) 2152 SetViewColor(Parent()->ViewColor()); 2153 else 2154 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 2155 } 2156 2157 2158 void 2159 TWindowView::ScrollTo(BPoint where) 2160 { 2161 BView::ScrollTo(where); 2162 fSwitcher->TopView()->DrawWindowScrollers(true); 2163 } 2164 2165 2166 2167 BRect 2168 TWindowView::FrameOf(int32 index) const 2169 { 2170 return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1); 2171 } 2172 2173 2174 void 2175 TWindowView::GetPreferredSize(float* _width, float* _height) 2176 { 2177 font_height fh; 2178 be_plain_font->GetHeight(&fh); 2179 fItemHeight = (int32) fh.ascent + fh.descent; 2180 2181 // top & bottom margin 2182 fItemHeight = fItemHeight + 3 + 3; 2183 2184 // want fItemHeight to be divisible by kWindowScrollSteps. 2185 fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps) 2186 / kWindowScrollSteps) * kWindowScrollSteps; 2187 2188 *_height = fItemHeight; 2189 2190 // leave width alone 2191 *_width = Bounds().Width(); 2192 } 2193 2194 2195 void 2196 TWindowView::ShowIndex(int32 newIndex) 2197 { 2198 // convert index to scroll location 2199 BPoint point(0, newIndex * fItemHeight); 2200 BRect bounds = Bounds(); 2201 2202 int32 groupIndex = fManager->CurrentIndex(); 2203 TTeamGroup* teamGroup 2204 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2205 if (teamGroup == NULL) 2206 return; 2207 2208 window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex); 2209 if (windowInfo == NULL) 2210 return; 2211 2212 fCurrentToken = windowInfo->server_token; 2213 free(windowInfo); 2214 2215 if (bounds.top == point.y) 2216 return; 2217 2218 int32 oldIndex = (int32) (bounds.top / fItemHeight); 2219 2220 int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps); 2221 int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize; 2222 int32 total = 0; 2223 int32 nslots = abs(newIndex - oldIndex); 2224 2225 while (total < (nslots * (int32)fItemHeight)) { 2226 ScrollBy(0, scrollValue); 2227 snooze(10000); 2228 total += stepSize; 2229 Window()->UpdateIfNeeded(); 2230 } 2231 } 2232 2233 2234 void 2235 TWindowView::Draw(BRect update) 2236 { 2237 int32 groupIndex = fManager->CurrentIndex(); 2238 TTeamGroup* teamGroup 2239 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex); 2240 if (teamGroup == NULL) 2241 return; 2242 2243 BRect bounds = Bounds(); 2244 int32 windowIndex = (int32) (bounds.top / fItemHeight); 2245 BRect windowRect = bounds; 2246 2247 windowRect.top = windowIndex * fItemHeight; 2248 windowRect.bottom = (windowIndex + 1) * fItemHeight - 1; 2249 2250 for (int32 i = 0; i < 3; i++) { 2251 if (!update.Intersects(windowRect)) { 2252 windowIndex++; 2253 windowRect.OffsetBy(0, fItemHeight); 2254 continue; 2255 } 2256 2257 // is window in current workspace? 2258 2259 bool local = true; 2260 bool minimized = false; 2261 BString title; 2262 2263 client_window_info* windowInfo 2264 = fManager->WindowInfo(groupIndex, windowIndex); 2265 if (windowInfo != NULL) { 2266 if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0) 2267 title << teamGroup->Name() << ": " << windowInfo->name; 2268 else 2269 title = teamGroup->Name(); 2270 2271 int32 currentWorkspace = current_workspace(); 2272 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) 2273 local = false; 2274 2275 minimized = windowInfo->is_mini; 2276 free(windowInfo); 2277 } else 2278 title = teamGroup->Name(); 2279 2280 if (!title.Length()) 2281 return; 2282 2283 float stringWidth = StringWidth(title.String()); 2284 float maxWidth = bounds.Width() - (14 + 5); 2285 2286 if (stringWidth > maxWidth) { 2287 // window title is too long, need to truncate 2288 TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth); 2289 stringWidth = maxWidth; 2290 } 2291 2292 BPoint point((bounds.Width() - (stringWidth + 14 + 5)) / 2, windowRect.bottom - 4); 2293 BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2); 2294 SetDrawingMode(B_OP_OVER); 2295 const BBitmap* bitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE, 2296 minimized ? R_WindowHiddenIcon : R_WindowShownIcon); 2297 p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2; 2298 DrawBitmap(bitmap, p); 2299 2300 if (!local) { 2301 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT)); 2302 p.x -= 8; 2303 p.y += 4; 2304 StrokeLine(p + BPoint(2, 2), p + BPoint(2, 2)); 2305 StrokeLine(p + BPoint(4, 2), p + BPoint(6, 2)); 2306 2307 StrokeLine(p + BPoint(0, 5), p + BPoint(0, 5)); 2308 StrokeLine(p + BPoint(2, 5), p + BPoint(6, 5)); 2309 2310 StrokeLine(p + BPoint(1, 8), p + BPoint(1, 8)); 2311 StrokeLine(p + BPoint(3, 8), p + BPoint(6, 8)); 2312 2313 SetHighColor(0, 0, 0); 2314 } 2315 2316 point.x += 21; 2317 MovePenTo(point); 2318 2319 DrawString(title.String()); 2320 SetDrawingMode(B_OP_COPY); 2321 2322 windowIndex++; 2323 windowRect.OffsetBy(0, fItemHeight); 2324 } 2325 } 2326 2327 2328 void 2329 TWindowView::UpdateGroup(int32 , int32 windowIndex) 2330 { 2331 ScrollTo(0, windowIndex * fItemHeight); 2332 Invalidate(Bounds()); 2333 } 2334 2335 2336 void 2337 TWindowView::Pulse() 2338 { 2339 // If selected window went away then reset to first window 2340 window_info *windowInfo = get_window_info(fCurrentToken); 2341 if (windowInfo == NULL) { 2342 Invalidate(); 2343 ShowIndex(0); 2344 } else 2345 free(windowInfo); 2346 } 2347 2348