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 #include <Debug.h> 36 #include <string.h> 37 #include <NodeInfo.h> 38 #include <Roster.h> 39 #include <Screen.h> 40 #include <Bitmap.h> 41 #include <Autolock.h> 42 43 #include "icons.h" 44 #include "icons_logo.h" 45 #include "BarApp.h" 46 #include "BarMenuTitle.h" 47 #include "BarView.h" 48 #include "BeMenu.h" 49 #include "DeskBarUtils.h" 50 #include "ExpandoMenuBar.h" 51 #include "ResourceSet.h" 52 #include "ShowHideMenuItem.h" 53 #include "StatusView.h" 54 #include "TeamMenuItem.h" 55 #include "WindowMenu.h" 56 #include "WindowMenuItem.h" 57 58 const float kBeMenuWidth = 50.0f; 59 const float kSepItemWidth = 5.0f; 60 61 const uint32 M_MINIMIZE_TEAM = 'mntm'; 62 const uint32 M_BRING_TEAM_TO_FRONT = 'bftm'; 63 64 65 bool TExpandoMenuBar::sDoMonitor = false; 66 thread_id TExpandoMenuBar::sMonThread = B_ERROR; 67 BLocker TExpandoMenuBar::sMonLocker("expando monitor"); 68 69 70 TExpandoMenuBar::TExpandoMenuBar(TBarView *bar, BRect frame, const char *name, 71 bool vertical, bool drawLabel) 72 : BMenuBar(frame, name, B_FOLLOW_NONE, 73 vertical ? B_ITEMS_IN_COLUMN : B_ITEMS_IN_ROW, vertical), 74 fVertical(vertical), 75 fOverflow(false), 76 fDrawLabel(drawLabel), 77 fIsScrolling(false), 78 fShowTeamExpander(static_cast<TBarApp *>(be_app)->Settings()->superExpando), 79 fExpandNewTeams(static_cast<TBarApp *>(be_app)->Settings()->expandNewTeams), 80 fBarView(bar), 81 fFirstApp(0) 82 { 83 #ifdef DOUBLECLICKBRINGSTOFRONT 84 fLastClickItem = -1; 85 fLastClickTime = 0; 86 #endif 87 88 SetItemMargins(0.0f, 0.0f, 0.0f, 0.0f); 89 SetFont(be_plain_font); 90 SetMaxContentWidth(kMinimumWindowWidth); 91 } 92 93 94 int 95 TExpandoMenuBar::CompareByName(const void *first, const void *second) 96 { 97 return strcasecmp((*(static_cast<BarTeamInfo * const*>(first)))->name, 98 (*(static_cast<BarTeamInfo * const*>(second)))->name); 99 } 100 101 102 void 103 TExpandoMenuBar::AttachedToWindow() 104 { 105 BMessenger self(this); 106 BList teamList; 107 TBarApp::Subscribe(self, &teamList); 108 float width = fVertical ? Frame().Width() : kMinimumWindowWidth; 109 float height = -1.0f; 110 111 // top or bottom mode, add be menu and sep for menubar tracking consistency 112 if (!fVertical) { 113 TBeMenu *beMenu = new TBeMenu(fBarView); 114 TBarWindow::SetBeMenu(beMenu); 115 fBeMenuItem = new TBarMenuTitle(kBeMenuWidth, Frame().Height(), 116 AppResSet()->FindBitmap(B_MESSAGE_TYPE, R_BeLogoIcon), beMenu, true); 117 AddItem(fBeMenuItem); 118 119 fSeparatorItem = new TTeamMenuItem(kSepItemWidth, height, fVertical); 120 AddItem(fSeparatorItem); 121 fSeparatorItem->SetEnabled(false); 122 fFirstApp = 2; 123 } else { 124 fBeMenuItem = NULL; 125 fSeparatorItem = NULL; 126 } 127 128 desk_settings *settings = ((TBarApp *)be_app)->Settings(); 129 130 if (settings->sortRunningApps) 131 teamList.SortItems(CompareByName); 132 133 int32 count = teamList.CountItems(); 134 for (int32 i = 0; i < count; i++) { 135 BarTeamInfo *barInfo = (BarTeamInfo *)teamList.ItemAt(i); 136 if ((barInfo->flags & B_BACKGROUND_APP) == 0 137 && strcasecmp(barInfo->sig, kDeskbarSignature) != 0) { 138 if (settings->trackerAlwaysFirst 139 && !strcmp(barInfo->sig, kTrackerSignature)) { 140 AddItem(new TTeamMenuItem(barInfo->teams, barInfo->icon, 141 barInfo->name, barInfo->sig, width, height, 142 fDrawLabel, fVertical), fFirstApp); 143 } else { 144 AddItem(new TTeamMenuItem(barInfo->teams, barInfo->icon, 145 barInfo->name, barInfo->sig, width, height, 146 fDrawLabel, fVertical)); 147 } 148 149 barInfo->teams = NULL; 150 barInfo->icon = NULL; 151 barInfo->name = NULL; 152 barInfo->sig = NULL; 153 } 154 155 delete barInfo; 156 } 157 158 BMenuBar::AttachedToWindow(); 159 160 if (CountItems() == 0) { 161 // If we're empty, BMenuBar::AttachedToWindow() resizes us to some 162 // weird value - we just override it again 163 ResizeTo(width, 0); 164 } 165 166 if (fVertical) { 167 sDoMonitor = true; 168 sMonThread = spawn_thread(monitor_team_windows, 169 "Expando Window Watcher", B_LOW_PRIORITY, this); 170 resume_thread(sMonThread); 171 } 172 } 173 174 175 void 176 TExpandoMenuBar::DetachedFromWindow() 177 { 178 BMenuBar::DetachedFromWindow(); 179 180 if (sMonThread != B_ERROR) { 181 sDoMonitor = false; 182 183 status_t returnCode; 184 wait_for_thread(sMonThread, &returnCode); 185 186 sMonThread = B_ERROR; 187 } 188 189 BMessenger self(this); 190 BMessage message(msg_Unsubscribe); 191 message.AddMessenger("messenger", self); 192 be_app->PostMessage(&message); 193 194 BMenuItem *item = NULL; 195 while ((item = RemoveItem(0L)) != NULL) 196 delete item; 197 } 198 199 200 void 201 TExpandoMenuBar::MessageReceived(BMessage *message) 202 { 203 int32 index; 204 TTeamMenuItem *item; 205 206 switch (message->what) { 207 case B_SOME_APP_LAUNCHED: { 208 BList *teams = NULL; 209 message->FindPointer("teams", (void **)&teams); 210 211 BBitmap *icon = NULL; 212 message->FindPointer("icon", (void **)&icon); 213 214 const char *signature; 215 if (message->FindString("sig", &signature) == B_OK 216 &&strcasecmp(signature, kDeskbarSignature) == 0) { 217 delete teams; 218 delete icon; 219 break; 220 } 221 222 uint32 flags; 223 if (message->FindInt32("flags", ((int32*) &flags)) == B_OK 224 && (flags & B_BACKGROUND_APP) != 0) { 225 delete teams; 226 delete icon; 227 break; 228 } 229 230 const char *name = NULL; 231 message->FindString("name", &name); 232 233 AddTeam(teams, icon, strdup(name), strdup(signature)); 234 break; 235 } 236 237 case msg_AddTeam: 238 AddTeam(message->FindInt32("team"), message->FindString("sig")); 239 break; 240 241 case msg_RemoveTeam: 242 { 243 team_id team = -1; 244 message->FindInt32("team", &team); 245 246 RemoveTeam(team, true); 247 break; 248 } 249 250 case B_SOME_APP_QUIT: 251 { 252 team_id team = -1; 253 message->FindInt32("team", &team); 254 255 RemoveTeam(team, false); 256 break; 257 } 258 259 case M_MINIMIZE_TEAM: 260 { 261 index = message->FindInt32("itemIndex"); 262 item = dynamic_cast<TTeamMenuItem *>(ItemAt(index)); 263 if (item == NULL) 264 break; 265 266 TShowHideMenuItem::TeamShowHideCommon(B_MINIMIZE_WINDOW, 267 item->Teams(), 268 item->Menu()->ConvertToScreen(item->Frame()), 269 true); 270 break; 271 } 272 273 case M_BRING_TEAM_TO_FRONT: 274 { 275 index = message->FindInt32("itemIndex"); 276 item = dynamic_cast<TTeamMenuItem *>(ItemAt(index)); 277 if (item == NULL) 278 break; 279 280 TShowHideMenuItem::TeamShowHideCommon(B_BRING_TO_FRONT, 281 item->Teams(), 282 item->Menu()->ConvertToScreen(item->Frame()), 283 true); 284 break; 285 } 286 287 default: 288 BMenuBar::MessageReceived(message); 289 break; 290 } 291 } 292 293 294 void 295 TExpandoMenuBar::MouseDown(BPoint where) 296 { 297 BMessage *message = Window()->CurrentMessage(); 298 299 // check for three finger salute, a.k.a. Vulcan Death Grip 300 if (message != NULL) { 301 int32 modifiers = 0; 302 message->FindInt32("modifiers", &modifiers); 303 304 if ((modifiers & B_COMMAND_KEY) != 0 305 && (modifiers & B_OPTION_KEY) != 0 306 && (modifiers & B_SHIFT_KEY) != 0 307 && !fBarView->Dragging()) { 308 309 TTeamMenuItem *item = ItemAtPoint(where); 310 if (item) { 311 const BList *teams = item->Teams(); 312 int32 teamCount = teams->CountItems(); 313 314 team_id teamID; 315 for (int32 team = 0; team < teamCount; team++) { 316 teamID = (team_id)teams->ItemAt(team); 317 kill_team(teamID); 318 // remove the team immediately 319 // from display 320 RemoveTeam(teamID, false); 321 } 322 323 return; 324 } 325 } 326 } 327 328 const int32 count = CountItems(); 329 330 // This feature is broken because the menu bar never receives 331 // the second click 332 #ifdef DOUBLECLICKBRINGSTOFRONT 333 // doubleclick on an item brings all to front 334 for (int32 i = fFirstApp; i < count; i++) { 335 TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(i); 336 if (item->Frame().Contains(where)) { 337 bigtime_t clickSpeed = 0; 338 get_click_speed(&clickSpeed); 339 if ( (fLastClickItem == i) && 340 (clickSpeed > (system_time() - fLastClickTime)) ) { 341 // bring this team's window to the front 342 BMessage showMessage(M_BRING_TEAM_TO_FRONT); 343 showMessage.AddInt32("itemIndex", i); 344 Window()->PostMessage(&showMessage, this); 345 return; 346 } 347 348 fLastClickItem = i; 349 fLastClickTime = system_time(); 350 break; 351 } 352 } 353 #endif 354 355 // control click - show all/hide all shortcut 356 if (message != NULL) { 357 int32 modifiers = 0; 358 message->FindInt32("modifiers", &modifiers); 359 if ((modifiers & B_CONTROL_KEY) != 0 360 && ! fBarView->Dragging()) { 361 int32 lastApp = -1; 362 363 // find the clicked item 364 for (int32 i = fFirstApp; i < count; i++) { 365 const TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(i); 366 367 // check if this item is really a team item (what a cruel way...) 368 // "lastApp" will always point to the last application in 369 // the list - the other entries might be windows (due to the team expander) 370 if (item->Submenu()) 371 lastApp = i; 372 373 if (item->Frame().Contains(where)) { 374 // show/hide item's teams 375 BMessage showMessage((modifiers & B_SHIFT_KEY) != 0 376 ? M_MINIMIZE_TEAM : M_BRING_TEAM_TO_FRONT); 377 showMessage.AddInt32("itemIndex", lastApp); 378 Window()->PostMessage(&showMessage, this); 379 return; 380 } 381 } 382 } 383 } 384 385 // Check the bounds of the expand Team icon 386 if (fShowTeamExpander && fVertical && !fBarView->Dragging()) { 387 TTeamMenuItem *item = ItemAtPoint(where); 388 if (item->Submenu()) { 389 BRect expanderRect = item->ExpanderBounds(); 390 if (expanderRect.Contains(where)) { 391 // Let the update thread wait... 392 BAutolock locker(sMonLocker); 393 394 // Toggle the item 395 item->ToggleExpandState(true); 396 item->Draw(); 397 398 // Absorb the message. 399 return; 400 } 401 } 402 } 403 404 BMenuBar::MouseDown(where); 405 } 406 407 408 void 409 TExpandoMenuBar::MouseMoved(BPoint where, uint32 code, const BMessage *message) 410 { 411 if (!message) { 412 // force a cleanup 413 fBarView->DragStop(true); 414 BMenuBar::MouseMoved(where, code, message); 415 return; 416 } 417 418 BPoint loc; 419 uint32 buttons; 420 GetMouse(&loc, &buttons); 421 422 switch (code) { 423 case B_ENTERED_VIEW: 424 if (message && buttons != 0) { 425 fBarView->CacheDragData((BMessage *)message); 426 MouseDown(loc); 427 } 428 break; 429 430 case B_EXITED_VIEW: 431 if (fBarView->Dragging() && buttons != 0) { 432 if (!ItemAtPoint(where) 433 && !InBeMenu(where) 434 && (fSeparatorItem && !fSeparatorItem->Frame().Contains(where)) 435 && !Frame().Contains(where)) 436 fBarView->DragStop(); 437 438 } 439 break; 440 } 441 BMenuBar::MouseMoved(where, code, message); 442 } 443 444 445 bool 446 TExpandoMenuBar::InBeMenu(BPoint loc) const 447 { 448 if (!fVertical) { 449 if (fBeMenuItem && fBeMenuItem->Frame().Contains(loc)) 450 return true; 451 } else { 452 TBarWindow *window = dynamic_cast<TBarWindow*>(Window()); 453 if (window) { 454 TBeMenu *bemenu = window->BeMenu(); 455 if (bemenu && bemenu->Frame().Contains(loc)) 456 return true; 457 } 458 } 459 460 return false; 461 } 462 463 464 TTeamMenuItem * 465 TExpandoMenuBar::ItemAtPoint(BPoint point) 466 { 467 TTeamMenuItem *item = NULL; 468 int32 count = CountItems(); 469 470 for (int32 i = fFirstApp; i < count; i++) { 471 item = (TTeamMenuItem *)ItemAt(i); 472 if (item && item->Frame().Contains(point)) 473 return item; 474 } 475 return NULL; 476 } 477 478 479 void 480 TExpandoMenuBar::AddTeam(BList *team, BBitmap *icon, char *name, char *signature) 481 { 482 float itemWidth = fVertical ? Frame().Width() : kMinimumWindowWidth; 483 float itemHeight = -1.0f; 484 485 desk_settings *settings = ((TBarApp *)be_app)->Settings(); 486 TTeamMenuItem *item = new TTeamMenuItem(team, icon, name, signature, itemWidth, 487 itemHeight, fDrawLabel, fVertical); 488 489 if (settings->trackerAlwaysFirst && !strcmp(signature, kTrackerSignature)) { 490 AddItem(item, fFirstApp); 491 } else if (settings->sortRunningApps) { 492 TTeamMenuItem *teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(fFirstApp)); 493 int32 firstApp = fFirstApp; 494 495 // if Tracker should always be the first item, we need to skip it 496 // when sorting in the current item 497 if (settings->trackerAlwaysFirst && teamItem != NULL 498 && !strcmp(teamItem->Signature(), kTrackerSignature)) { 499 firstApp++; 500 } 501 502 int32 count = CountItems(), i; 503 for (i = firstApp; i < count; i++) { 504 teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(i)); 505 if (teamItem != NULL && strcasecmp(teamItem->Name(), name) > 0) { 506 AddItem(item, i); 507 break; 508 } 509 } 510 // was the item added to the list yet? 511 if (i == count) 512 AddItem(item); 513 } else 514 AddItem(item); 515 516 if (fVertical) { 517 if (item && fShowTeamExpander && fExpandNewTeams) 518 item->ToggleExpandState(false); 519 520 fBarView->SizeWindow(BScreen(Window()).Frame()); 521 } else 522 CheckItemSizes(1); 523 524 Window()->UpdateIfNeeded(); 525 } 526 527 528 void 529 TExpandoMenuBar::AddTeam(team_id team, const char *signature) 530 { 531 int32 count = CountItems(); 532 for (int32 i = fFirstApp; i < count; i++) { 533 // Only add to team menu items 534 if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) { 535 if (strcasecmp(item->Signature(), signature) == 0) { 536 if (!(item->Teams()->HasItem((void *)team))) 537 item->Teams()->AddItem((void *)team); 538 539 break; 540 } 541 } 542 } 543 } 544 545 546 void 547 TExpandoMenuBar::RemoveTeam(team_id team, bool partial) 548 { 549 int32 count = CountItems(); 550 for (int32 i = fFirstApp; i < count; i++) { 551 if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) { 552 if (item->Teams()->HasItem((void *)team)) { 553 item->Teams()->RemoveItem(team); 554 555 if (partial) 556 return; 557 558 #ifdef DOUBLECLICKBRINGSTOFRONT 559 if (fLastClickItem == i) 560 fLastClickItem = -1; 561 #endif 562 563 RemoveItem(i); 564 565 if (fVertical) { 566 // instead of resizing the window here and there in the code 567 // the resize method will be centered in one place 568 // thus, the same behavior (good or bad) will be used whereever 569 // window sizing is done 570 fBarView->SizeWindow(BScreen(Window()).Frame()); 571 } else 572 CheckItemSizes(-1); 573 574 Window()->UpdateIfNeeded(); 575 576 delete item; 577 return; 578 } 579 } 580 } 581 } 582 583 584 void 585 TExpandoMenuBar::CheckItemSizes(int32 delta) 586 { 587 float width = Frame().Width(); 588 int32 count = CountItems(); 589 bool reset = false; 590 float newWidth = 0; 591 float fullWidth = (kMinimumWindowWidth * count); 592 593 if (!fBarView->Vertical()) { 594 // in this case there are 2 extra items: 595 // The Be Menu 596 // The little separator item 597 fullWidth = fullWidth - (kMinimumWindowWidth * 2) + (kBeMenuWidth + kSepItemWidth); 598 width -= (kBeMenuWidth + kSepItemWidth); 599 count -= 2; 600 } 601 602 if (delta >= 0 && fullWidth > width) { 603 fOverflow = true; 604 reset = true; 605 newWidth = floorf(width/count); 606 } else if (delta < 0 && fOverflow) { 607 reset = true; 608 if (fullWidth > width) 609 newWidth = floorf(width/count); 610 else 611 newWidth = kMinimumWindowWidth; 612 } 613 if (newWidth > kMinimumWindowWidth) 614 newWidth = kMinimumWindowWidth; 615 616 if (reset) { 617 SetMaxContentWidth(newWidth); 618 if (newWidth == kMinimumWindowWidth) 619 fOverflow = false; 620 InvalidateLayout(); 621 622 for (int32 index = fFirstApp; ; index++) { 623 TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(index); 624 if (!item) 625 break; 626 item->SetOverrideWidth(newWidth); 627 } 628 629 Invalidate(); 630 Window()->UpdateIfNeeded(); 631 } 632 } 633 634 635 menu_layout 636 TExpandoMenuBar::MenuLayout() const 637 { 638 return Layout(); 639 } 640 641 642 void 643 TExpandoMenuBar::Draw(BRect update) 644 { 645 BMenu::Draw(update); 646 } 647 648 649 void 650 TExpandoMenuBar::DrawBackground(BRect) 651 { 652 BRect bounds(Bounds()); 653 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR); 654 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 655 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT); 656 rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT); 657 int32 last = CountItems() - 1; 658 float start; 659 660 if (last >= 0) 661 start = ItemAt(last)->Frame().right + 1; 662 else 663 start = 0; 664 665 if (!fVertical) { 666 SetHighColor(vlight); 667 StrokeLine(BPoint(start, bounds.top+1), bounds.RightTop() + BPoint(0,1)); 668 StrokeLine(BPoint(start, bounds.top+1), BPoint(start, bounds.bottom)); 669 SetHighColor(hilite); 670 StrokeLine(BPoint(start+1, bounds.bottom), bounds.RightBottom()); 671 } 672 } 673 674 675 /** Something to help determine if we are showing too many apps 676 * need to add in scrolling functionality. 677 */ 678 679 void 680 TExpandoMenuBar::CheckForSizeOverrun() 681 { 682 BRect screenFrame = (BScreen(Window())).Frame(); 683 if (fVertical) 684 fIsScrolling = Window()->Frame().bottom > screenFrame.bottom; 685 else 686 fIsScrolling = false; 687 } 688 689 690 void 691 TExpandoMenuBar::SizeWindow() 692 { 693 if (fVertical) 694 fBarView->SizeWindow(BScreen(Window()).Frame()); 695 else 696 CheckItemSizes(1); 697 } 698 699 700 int32 701 TExpandoMenuBar::monitor_team_windows(void *arg) 702 { 703 TExpandoMenuBar *teamMenu = (TExpandoMenuBar *)arg; 704 705 while (teamMenu->sDoMonitor) { 706 sMonLocker.Lock(); 707 708 if (teamMenu->Window()->LockWithTimeout(50000) == B_OK) { 709 int32 totalItems = teamMenu->CountItems(); 710 711 // Set all WindowMenuItems to require an update. 712 TWindowMenuItem *item = NULL; 713 for (int32 i = 0; i < totalItems; i++) { 714 if (!teamMenu->SubmenuAt(i)){ 715 item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i)); 716 item->SetRequireUpdate(); 717 } 718 } 719 720 // Perform SetTo() on all the items that still exist as well as add new items. 721 bool itemModified = false, resize = false; 722 TTeamMenuItem *teamItem = NULL; 723 724 for (int32 i = 0; i < totalItems; i++) { 725 if (teamMenu->SubmenuAt(i) == NULL) 726 continue; 727 728 teamItem = static_cast<TTeamMenuItem *>(teamMenu->ItemAt(i)); 729 if (teamItem->IsExpanded()) { 730 int32 teamCount = teamItem->Teams()->CountItems(); 731 for (int32 j = 0; j < teamCount; j++) { 732 // The following code is almost a copy/paste from 733 // WindowMenu.cpp 734 team_id theTeam = (team_id)teamItem->Teams()->ItemAt(j); 735 int32 count = 0; 736 int32 *tokens = get_token_list(theTeam, &count); 737 738 for (int32 k = 0; k < count; k++) { 739 window_info *wInfo = get_window_info(tokens[k]); 740 if (wInfo == NULL) 741 continue; 742 743 if (TWindowMenu::WindowShouldBeListed(wInfo->w_type) 744 && (wInfo->show_hide_level <= 0 || wInfo->is_mini)) { 745 // Check if we have a matching window item... 746 item = teamItem->ExpandedWindowItem(wInfo->id); 747 if (item) { 748 item->SetTo(wInfo->name, wInfo->id, wInfo->is_mini, 749 ((1 << current_workspace()) & wInfo->workspaces) != 0); 750 751 if (strcmp(wInfo->name, item->Label()) != 0) 752 item->SetLabel(wInfo->name); 753 754 if (item->ChangedState()) 755 itemModified = true; 756 } else if (teamItem->IsExpanded()) { 757 // Add the item 758 item = new TWindowMenuItem(wInfo->name, wInfo->id, 759 wInfo->is_mini, 760 ((1 << current_workspace()) & wInfo->workspaces) != 0, 761 false); 762 item->ExpandedItem(true); 763 teamMenu->AddItem(item, i + 1); 764 resize = true; 765 } 766 } 767 free(wInfo); 768 } 769 free(tokens); 770 } 771 } 772 } 773 774 // Remove any remaining items which require an update. 775 for (int32 i = 0; i < totalItems; i++) { 776 if (!teamMenu->SubmenuAt(i)){ 777 item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i)); 778 if (item && item->RequiresUpdate()) { 779 item = static_cast<TWindowMenuItem *>(teamMenu->RemoveItem(i)); 780 delete item; 781 totalItems--; 782 783 resize = true; 784 } 785 } 786 } 787 788 // If any of the WindowMenuItems changed state, we need to force a repaint. 789 if (itemModified || resize) { 790 teamMenu->Invalidate(); 791 if (resize) 792 teamMenu->SizeWindow(); 793 } 794 795 teamMenu->Window()->Unlock(); 796 } 797 798 sMonLocker.Unlock(); 799 800 // sleep for a bit... 801 snooze(150000); 802 } 803 return B_OK; 804 } 805 806