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