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