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