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