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