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(), item->Menu()->ConvertToScreen(item->Frame()), 282 true); 283 break; 284 } 285 286 default: 287 BMenuBar::MessageReceived(message); 288 break; 289 } 290 } 291 292 293 void 294 TExpandoMenuBar::MouseDown(BPoint where) 295 { 296 BMessage *message = Window()->CurrentMessage(); 297 298 // check for three finger salute, a.k.a. Vulcan Death Grip 299 if (message != NULL) { 300 int32 modifiers = 0; 301 message->FindInt32("modifiers", &modifiers); 302 303 if ((modifiers & B_COMMAND_KEY) != 0 304 && (modifiers & B_OPTION_KEY) != 0 305 && (modifiers & B_SHIFT_KEY) != 0 306 && !fBarView->Dragging()) { 307 TTeamMenuItem *item = TeamItemAtPoint(where); 308 309 if (item != NULL) { 310 const BList *teams = item->Teams(); 311 int32 teamCount = teams->CountItems(); 312 313 team_id teamID; 314 for (int32 team = 0; team < teamCount; team++) { 315 teamID = (team_id)teams->ItemAt(team); 316 kill_team(teamID); 317 // remove the team immediately 318 // from display 319 RemoveTeam(teamID, false); 320 } 321 322 return; 323 } 324 } 325 } 326 327 // This feature is broken because the menu bar never receives 328 // the second click 329 #ifdef DOUBLECLICKBRINGSTOFRONT 330 // doubleclick on an item brings all to front 331 for (int32 i = fFirstApp; i < count; i++) { 332 TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(i); 333 if (item->Frame().Contains(where)) { 334 bigtime_t clickSpeed = 0; 335 get_click_speed(&clickSpeed); 336 if ( (fLastClickItem == i) && 337 (clickSpeed > (system_time() - fLastClickTime)) ) { 338 // bring this team's window to the front 339 BMessage showMessage(M_BRING_TEAM_TO_FRONT); 340 showMessage.AddInt32("itemIndex", i); 341 Window()->PostMessage(&showMessage, this); 342 return; 343 } 344 345 fLastClickItem = i; 346 fLastClickTime = system_time(); 347 break; 348 } 349 } 350 #endif 351 352 // control click - show all/hide all shortcut 353 if (message != NULL) { 354 int32 modifiers = 0; 355 message->FindInt32("modifiers", &modifiers); 356 if ((modifiers & B_CONTROL_KEY) != 0 357 && ! fBarView->Dragging()) { 358 TTeamMenuItem *item = TeamItemAtPoint(where); 359 if (item != NULL) { 360 // show/hide item's teams 361 BMessage showMessage((modifiers & B_SHIFT_KEY) != 0 362 ? M_MINIMIZE_TEAM : M_BRING_TEAM_TO_FRONT); 363 showMessage.AddInt32("itemIndex", IndexOf(item)); 364 Window()->PostMessage(&showMessage, this); 365 return; 366 } 367 } 368 } 369 370 // Check the bounds of the expand Team icon 371 if (fShowTeamExpander && fVertical && !fBarView->Dragging()) { 372 TTeamMenuItem *item = TeamItemAtPoint(where); 373 if (item != NULL) { 374 BRect expanderRect = item->ExpanderBounds(); 375 if (expanderRect.Contains(where)) { 376 // Let the update thread wait... 377 BAutolock locker(sMonLocker); 378 379 // Toggle the item 380 item->ToggleExpandState(true); 381 item->Draw(); 382 383 // Absorb the message. 384 return; 385 } 386 } 387 } 388 389 BMenuBar::MouseDown(where); 390 } 391 392 393 void 394 TExpandoMenuBar::MouseMoved(BPoint where, uint32 code, const BMessage *message) 395 { 396 if (!message) { 397 // force a cleanup 398 fBarView->DragStop(true); 399 BMenuBar::MouseMoved(where, code, message); 400 return; 401 } 402 403 uint32 buttons; 404 if (!(Window()->CurrentMessage()) 405 || Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) < B_OK) 406 buttons = 0; 407 408 switch (code) { 409 case B_ENTERED_VIEW: 410 if (message && buttons != 0) { 411 fBarView->CacheDragData(message); 412 MouseDown(where); 413 } 414 break; 415 416 case B_EXITED_VIEW: 417 if (fBarView->Dragging() && buttons != 0) { 418 if (!TeamItemAtPoint(where) 419 && !InBeMenu(where) 420 && (fSeparatorItem && !fSeparatorItem->Frame().Contains(where)) 421 && !Frame().Contains(where)) { 422 fBarView->DragStop(); 423 } 424 } 425 break; 426 } 427 BMenuBar::MouseMoved(where, code, message); 428 } 429 430 431 bool 432 TExpandoMenuBar::InBeMenu(BPoint loc) const 433 { 434 if (!fVertical) { 435 if (fBeMenuItem && fBeMenuItem->Frame().Contains(loc)) 436 return true; 437 } else { 438 TBarWindow *window = dynamic_cast<TBarWindow*>(Window()); 439 if (window) { 440 if (TBeMenu *bemenu = window->BeMenu()) { 441 bool inBeMenu = false; 442 if (bemenu->LockLooper()) { 443 inBeMenu = bemenu->Frame().Contains(loc); 444 bemenu->UnlockLooper(); 445 } 446 return inBeMenu; 447 } 448 } 449 } 450 451 return false; 452 } 453 454 455 /** Returns the team menu item that belongs to the item under the 456 * specified \a point. 457 * If \a _item is given, it will return the exact menu item under 458 * that point (which might be a window item when the expander is on). 459 */ 460 461 TTeamMenuItem * 462 TExpandoMenuBar::TeamItemAtPoint(BPoint point, BMenuItem **_item) 463 { 464 TTeamMenuItem *lastApp = NULL; 465 int32 count = CountItems(); 466 467 for (int32 i = fFirstApp; i < count; i++) { 468 BMenuItem *item = ItemAt(i); 469 470 if (dynamic_cast<TTeamMenuItem *>(item) != NULL) 471 lastApp = (TTeamMenuItem *)item; 472 473 if (item && item->Frame().Contains(point)) { 474 if (_item != NULL) 475 *_item = item; 476 477 return lastApp; 478 } 479 } 480 481 // no item found 482 483 if (_item != NULL) 484 *_item = NULL; 485 486 return NULL; 487 } 488 489 490 void 491 TExpandoMenuBar::AddTeam(BList *team, BBitmap *icon, char *name, char *signature) 492 { 493 float itemWidth = fVertical ? Frame().Width() : kMinimumWindowWidth; 494 float itemHeight = -1.0f; 495 496 desk_settings *settings = ((TBarApp *)be_app)->Settings(); 497 TTeamMenuItem *item = new TTeamMenuItem(team, icon, name, signature, itemWidth, 498 itemHeight, fDrawLabel, fVertical); 499 500 if (settings->trackerAlwaysFirst && !strcmp(signature, kTrackerSignature)) { 501 AddItem(item, fFirstApp); 502 } else if (settings->sortRunningApps) { 503 TTeamMenuItem *teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(fFirstApp)); 504 int32 firstApp = fFirstApp; 505 506 // if Tracker should always be the first item, we need to skip it 507 // when sorting in the current item 508 if (settings->trackerAlwaysFirst && teamItem != NULL 509 && !strcmp(teamItem->Signature(), kTrackerSignature)) { 510 firstApp++; 511 } 512 513 int32 count = CountItems(), i; 514 for (i = firstApp; i < count; i++) { 515 teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(i)); 516 if (teamItem != NULL && strcasecmp(teamItem->Name(), name) > 0) { 517 AddItem(item, i); 518 break; 519 } 520 } 521 // was the item added to the list yet? 522 if (i == count) 523 AddItem(item); 524 } else 525 AddItem(item); 526 527 if (fVertical) { 528 if (item && fShowTeamExpander && fExpandNewTeams) 529 item->ToggleExpandState(false); 530 531 fBarView->SizeWindow(BScreen(Window()).Frame()); 532 } else 533 CheckItemSizes(1); 534 535 Window()->UpdateIfNeeded(); 536 } 537 538 539 void 540 TExpandoMenuBar::AddTeam(team_id team, const char *signature) 541 { 542 int32 count = CountItems(); 543 for (int32 i = fFirstApp; i < count; i++) { 544 // Only add to team menu items 545 if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) { 546 if (strcasecmp(item->Signature(), signature) == 0) { 547 if (!(item->Teams()->HasItem((void *)team))) 548 item->Teams()->AddItem((void *)team); 549 550 break; 551 } 552 } 553 } 554 } 555 556 557 void 558 TExpandoMenuBar::RemoveTeam(team_id team, bool partial) 559 { 560 int32 count = CountItems(); 561 for (int32 i = fFirstApp; i < count; i++) { 562 if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) { 563 if (item->Teams()->HasItem((void *)team)) { 564 item->Teams()->RemoveItem(team); 565 566 if (partial) 567 return; 568 569 #ifdef DOUBLECLICKBRINGSTOFRONT 570 if (fLastClickItem == i) 571 fLastClickItem = -1; 572 #endif 573 574 RemoveItem(i); 575 576 if (fVertical) { 577 // instead of resizing the window here and there in the code 578 // the resize method will be centered in one place 579 // thus, the same behavior (good or bad) will be used whereever 580 // window sizing is done 581 fBarView->SizeWindow(BScreen(Window()).Frame()); 582 } else 583 CheckItemSizes(-1); 584 585 Window()->UpdateIfNeeded(); 586 587 delete item; 588 return; 589 } 590 } 591 } 592 } 593 594 595 void 596 TExpandoMenuBar::CheckItemSizes(int32 delta) 597 { 598 float width = Frame().Width(); 599 int32 count = CountItems(); 600 bool reset = false; 601 float newWidth = 0; 602 float fullWidth = (kMinimumWindowWidth * count); 603 604 if (!fBarView->Vertical()) { 605 // in this case there are 2 extra items: 606 // The Be Menu 607 // The little separator item 608 fullWidth = fullWidth - (kMinimumWindowWidth * 2) + (kBeMenuWidth + kSepItemWidth); 609 width -= (kBeMenuWidth + kSepItemWidth); 610 count -= 2; 611 } 612 613 if (delta >= 0 && fullWidth > width) { 614 fOverflow = true; 615 reset = true; 616 newWidth = floorf(width/count); 617 } else if (delta < 0 && fOverflow) { 618 reset = true; 619 if (fullWidth > width) 620 newWidth = floorf(width/count); 621 else 622 newWidth = kMinimumWindowWidth; 623 } 624 if (newWidth > kMinimumWindowWidth) 625 newWidth = kMinimumWindowWidth; 626 627 if (reset) { 628 SetMaxContentWidth(newWidth); 629 if (newWidth == kMinimumWindowWidth) 630 fOverflow = false; 631 InvalidateLayout(); 632 633 for (int32 index = fFirstApp; ; index++) { 634 TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(index); 635 if (!item) 636 break; 637 item->SetOverrideWidth(newWidth); 638 } 639 640 Invalidate(); 641 Window()->UpdateIfNeeded(); 642 } 643 } 644 645 646 menu_layout 647 TExpandoMenuBar::MenuLayout() const 648 { 649 return Layout(); 650 } 651 652 653 void 654 TExpandoMenuBar::Draw(BRect update) 655 { 656 BMenu::Draw(update); 657 } 658 659 660 void 661 TExpandoMenuBar::DrawBackground(BRect) 662 { 663 BRect bounds(Bounds()); 664 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR); 665 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 666 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT); 667 rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT); 668 int32 last = CountItems() - 1; 669 float start; 670 671 if (last >= 0) 672 start = ItemAt(last)->Frame().right + 1; 673 else 674 start = 0; 675 676 if (!fVertical) { 677 SetHighColor(vlight); 678 StrokeLine(BPoint(start, bounds.top+1), bounds.RightTop() + BPoint(0,1)); 679 StrokeLine(BPoint(start, bounds.top+1), BPoint(start, bounds.bottom)); 680 SetHighColor(hilite); 681 StrokeLine(BPoint(start+1, bounds.bottom), bounds.RightBottom()); 682 } 683 } 684 685 686 /** Something to help determine if we are showing too many apps 687 * need to add in scrolling functionality. 688 */ 689 690 void 691 TExpandoMenuBar::CheckForSizeOverrun() 692 { 693 BRect screenFrame = (BScreen(Window())).Frame(); 694 if (fVertical) 695 fIsScrolling = Window()->Frame().bottom > screenFrame.bottom; 696 else 697 fIsScrolling = false; 698 } 699 700 701 void 702 TExpandoMenuBar::SizeWindow() 703 { 704 if (fVertical) 705 fBarView->SizeWindow(BScreen(Window()).Frame()); 706 else 707 CheckItemSizes(1); 708 } 709 710 711 int32 712 TExpandoMenuBar::monitor_team_windows(void *arg) 713 { 714 TExpandoMenuBar *teamMenu = (TExpandoMenuBar *)arg; 715 716 while (teamMenu->sDoMonitor) { 717 sMonLocker.Lock(); 718 719 if (teamMenu->Window()->LockWithTimeout(50000) == B_OK) { 720 int32 totalItems = teamMenu->CountItems(); 721 722 // Set all WindowMenuItems to require an update. 723 TWindowMenuItem *item = NULL; 724 for (int32 i = 0; i < totalItems; i++) { 725 if (!teamMenu->SubmenuAt(i)){ 726 item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i)); 727 item->SetRequireUpdate(); 728 } 729 } 730 731 // Perform SetTo() on all the items that still exist as well as add new items. 732 bool itemModified = false, resize = false; 733 TTeamMenuItem *teamItem = NULL; 734 735 for (int32 i = 0; i < totalItems; i++) { 736 if (teamMenu->SubmenuAt(i) == NULL) 737 continue; 738 739 teamItem = static_cast<TTeamMenuItem *>(teamMenu->ItemAt(i)); 740 if (teamItem->IsExpanded()) { 741 int32 teamCount = teamItem->Teams()->CountItems(); 742 for (int32 j = 0; j < teamCount; j++) { 743 // The following code is almost a copy/paste from 744 // WindowMenu.cpp 745 team_id theTeam = (team_id)teamItem->Teams()->ItemAt(j); 746 int32 count = 0; 747 int32 *tokens = get_token_list(theTeam, &count); 748 749 for (int32 k = 0; k < count; k++) { 750 window_info *wInfo = get_window_info(tokens[k]); 751 if (wInfo == NULL) 752 continue; 753 754 if (TWindowMenu::WindowShouldBeListed(wInfo->w_type) 755 && (wInfo->show_hide_level <= 0 || wInfo->is_mini)) { 756 // Check if we have a matching window item... 757 item = teamItem->ExpandedWindowItem(wInfo->id); 758 if (item) { 759 item->SetTo(wInfo->name, wInfo->id, wInfo->is_mini, 760 ((1 << current_workspace()) & wInfo->workspaces) != 0); 761 762 if (strcmp(wInfo->name, item->Label()) != 0) 763 item->SetLabel(wInfo->name); 764 765 if (item->ChangedState()) 766 itemModified = true; 767 } else if (teamItem->IsExpanded()) { 768 // Add the item 769 item = new TWindowMenuItem(wInfo->name, wInfo->id, 770 wInfo->is_mini, 771 ((1 << current_workspace()) & wInfo->workspaces) != 0, 772 false); 773 item->ExpandedItem(true); 774 teamMenu->AddItem(item, i + 1); 775 resize = true; 776 } 777 } 778 free(wInfo); 779 } 780 free(tokens); 781 } 782 } 783 } 784 785 // Remove any remaining items which require an update. 786 for (int32 i = 0; i < totalItems; i++) { 787 if (!teamMenu->SubmenuAt(i)){ 788 item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i)); 789 if (item && item->RequiresUpdate()) { 790 item = static_cast<TWindowMenuItem *>(teamMenu->RemoveItem(i)); 791 delete item; 792 totalItems--; 793 794 resize = true; 795 } 796 } 797 } 798 799 // If any of the WindowMenuItems changed state, we need to force a repaint. 800 if (itemModified || resize) { 801 teamMenu->Invalidate(); 802 if (resize) 803 teamMenu->SizeWindow(); 804 } 805 806 teamMenu->Window()->Unlock(); 807 } 808 809 sMonLocker.Unlock(); 810 811 // sleep for a bit... 812 snooze(150000); 813 } 814 return B_OK; 815 } 816 817