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 "BeMenu.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 kDefaultBeMenuWidth = 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 fBeMenuWidth(kDefaultBeMenuWidth), 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 be menu and sep for menubar tracking consistency 115 if (!fVertical) { 116 TBeMenu* beMenu = new TBeMenu(fBarView); 117 TBarWindow::SetBeMenu(beMenu); 118 const BBitmap* logoBitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE, 119 R_BeLogoIcon); 120 if (logoBitmap != NULL) 121 fBeMenuWidth = logoBitmap->Bounds().Width() + 16; 122 fBeMenuItem = new TBarMenuTitle(fBeMenuWidth, Frame().Height(), 123 logoBitmap, beMenu, true); 124 AddItem(fBeMenuItem); 125 126 fSeparatorItem = new TTeamMenuItem(kSepItemWidth, height, fVertical); 127 AddItem(fSeparatorItem); 128 fSeparatorItem->SetEnabled(false); 129 fFirstApp = 2; 130 } else { 131 fBeMenuItem = NULL; 132 fSeparatorItem = NULL; 133 } 134 135 desk_settings* settings = ((TBarApp*)be_app)->Settings(); 136 137 if (settings->sortRunningApps) 138 teamList.SortItems(CompareByName); 139 140 int32 count = teamList.CountItems(); 141 for (int32 i = 0; i < count; i++) { 142 BarTeamInfo* barInfo = (BarTeamInfo*)teamList.ItemAt(i); 143 if ((barInfo->flags & B_BACKGROUND_APP) == 0 144 && strcasecmp(barInfo->sig, kDeskbarSignature) != 0) { 145 if (settings->trackerAlwaysFirst 146 && !strcmp(barInfo->sig, kTrackerSignature)) { 147 AddItem(new TTeamMenuItem(barInfo->teams, barInfo->icon, 148 barInfo->name, barInfo->sig, width, height, 149 fDrawLabel, fVertical), fFirstApp); 150 } else { 151 AddItem(new TTeamMenuItem(barInfo->teams, barInfo->icon, 152 barInfo->name, barInfo->sig, width, height, 153 fDrawLabel, fVertical)); 154 } 155 156 barInfo->teams = NULL; 157 barInfo->icon = NULL; 158 barInfo->name = NULL; 159 barInfo->sig = NULL; 160 } 161 162 delete barInfo; 163 } 164 165 BMenuBar::AttachedToWindow(); 166 167 if (CountItems() == 0) { 168 // If we're empty, BMenuBar::AttachedToWindow() resizes us to some 169 // weird value - we just override it again 170 ResizeTo(width, 0); 171 } 172 173 if (fVertical) { 174 sDoMonitor = true; 175 sMonThread = spawn_thread(monitor_team_windows, 176 "Expando Window Watcher", B_LOW_PRIORITY, this); 177 resume_thread(sMonThread); 178 } 179 } 180 181 182 void 183 TExpandoMenuBar::DetachedFromWindow() 184 { 185 BMenuBar::DetachedFromWindow(); 186 187 if (sMonThread != B_ERROR) { 188 sDoMonitor = false; 189 190 status_t returnCode; 191 wait_for_thread(sMonThread, &returnCode); 192 193 sMonThread = B_ERROR; 194 } 195 196 BMessenger self(this); 197 BMessage message(kUnsubscribe); 198 message.AddMessenger("messenger", self); 199 be_app->PostMessage(&message); 200 201 RemoveItems(0, CountItems(), true); 202 } 203 204 205 void 206 TExpandoMenuBar::MessageReceived(BMessage* message) 207 { 208 int32 index; 209 TTeamMenuItem* item; 210 211 switch (message->what) { 212 case B_SOME_APP_LAUNCHED: { 213 BList* teams = NULL; 214 message->FindPointer("teams", (void**)&teams); 215 216 BBitmap* icon = NULL; 217 message->FindPointer("icon", (void**)&icon); 218 219 const char* signature; 220 if (message->FindString("sig", &signature) == B_OK 221 &&strcasecmp(signature, kDeskbarSignature) == 0) { 222 delete teams; 223 delete icon; 224 break; 225 } 226 227 uint32 flags; 228 if (message->FindInt32("flags", ((int32*) &flags)) == B_OK 229 && (flags & B_BACKGROUND_APP) != 0) { 230 delete teams; 231 delete icon; 232 break; 233 } 234 235 const char* name = NULL; 236 message->FindString("name", &name); 237 238 AddTeam(teams, icon, strdup(name), strdup(signature)); 239 break; 240 } 241 242 case kAddTeam: 243 AddTeam(message->FindInt32("team"), message->FindString("sig")); 244 break; 245 246 case kRemoveTeam: 247 { 248 team_id team = -1; 249 message->FindInt32("team", &team); 250 251 RemoveTeam(team, true); 252 break; 253 } 254 255 case B_SOME_APP_QUIT: 256 { 257 team_id team = -1; 258 message->FindInt32("team", &team); 259 260 RemoveTeam(team, false); 261 break; 262 } 263 264 case kMinimizeTeam: 265 { 266 index = message->FindInt32("itemIndex"); 267 item = dynamic_cast<TTeamMenuItem*>(ItemAt(index)); 268 if (item == NULL) 269 break; 270 271 TShowHideMenuItem::TeamShowHideCommon(B_MINIMIZE_WINDOW, 272 item->Teams(), 273 item->Menu()->ConvertToScreen(item->Frame()), 274 true); 275 break; 276 } 277 278 case kBringTeamToFront: 279 { 280 index = message->FindInt32("itemIndex"); 281 item = dynamic_cast<TTeamMenuItem*>(ItemAt(index)); 282 if (item == NULL) 283 break; 284 285 TShowHideMenuItem::TeamShowHideCommon(B_BRING_TO_FRONT, 286 item->Teams(), item->Menu()->ConvertToScreen(item->Frame()), 287 true); 288 break; 289 } 290 291 default: 292 BMenuBar::MessageReceived(message); 293 break; 294 } 295 } 296 297 298 void 299 TExpandoMenuBar::MouseDown(BPoint where) 300 { 301 BMessage* message = Window()->CurrentMessage(); 302 BMenuItem* menuItem; 303 TTeamMenuItem* item = TeamItemAtPoint(where, &menuItem); 304 305 // check for three finger salute, a.k.a. Vulcan Death Grip 306 if (message != NULL && item != NULL && !fBarView->Dragging()) { 307 int32 modifiers = 0; 308 message->FindInt32("modifiers", &modifiers); 309 310 if ((modifiers & B_COMMAND_KEY) != 0 311 && (modifiers & B_CONTROL_KEY) != 0 312 && (modifiers & B_SHIFT_KEY) != 0) { 313 const BList* teams = item->Teams(); 314 int32 teamCount = teams->CountItems(); 315 316 team_id teamID; 317 for (int32 team = 0; team < teamCount; team++) { 318 teamID = (team_id)teams->ItemAt(team); 319 kill_team(teamID); 320 // remove the team immediately from display 321 RemoveTeam(teamID, false); 322 } 323 324 return; 325 } 326 327 // control click - show all/hide all shortcut 328 if ((modifiers & B_CONTROL_KEY) != 0) { 329 // show/hide item's teams 330 BMessage showMessage((modifiers & B_SHIFT_KEY) != 0 331 ? kMinimizeTeam : kBringTeamToFront); 332 showMessage.AddInt32("itemIndex", IndexOf(item)); 333 Window()->PostMessage(&showMessage, this); 334 return; 335 } 336 337 // Check the bounds of the expand Team icon 338 if (fShowTeamExpander && fVertical) { 339 BRect expanderRect = item->ExpanderBounds(); 340 if (expanderRect.Contains(where)) { 341 // Let the update thread wait... 342 BAutolock locker(sMonLocker); 343 344 // Toggle the item 345 item->ToggleExpandState(true); 346 item->Draw(); 347 348 // Absorb the message. 349 return; 350 } 351 } 352 353 // double-click on an item brings the team to front 354 int32 clicks; 355 if (message->FindInt32("clicks", &clicks) == B_OK && clicks > 1 356 && item == menuItem && item == fLastClickItem) { 357 // activate this team 358 be_roster->ActivateApp((team_id)item->Teams()->ItemAt(0)); 359 return; 360 } 361 362 fLastClickItem = item; 363 } 364 365 BMenuBar::MouseDown(where); 366 } 367 368 369 void 370 TExpandoMenuBar::MouseMoved(BPoint where, uint32 code, const BMessage* message) 371 { 372 if (!message) { 373 // force a cleanup 374 _FinishedDrag(); 375 BMenuBar::MouseMoved(where, code, message); 376 return; 377 } 378 379 uint32 buttons; 380 if (!(Window()->CurrentMessage()) 381 || Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) 382 < B_OK) 383 buttons = 0; 384 385 if (buttons == 0) 386 return; 387 388 switch (code) { 389 case B_ENTERED_VIEW: 390 // fPreviousDragTargetItem should always be NULL here anyways. 391 if (fPreviousDragTargetItem) 392 _FinishedDrag(); 393 394 fBarView->CacheDragData(message); 395 fPreviousDragTargetItem = NULL; 396 break; 397 398 case B_OUTSIDE_VIEW: 399 // NOTE: Should not be here, but for the sake of defensive 400 // programming... 401 case B_EXITED_VIEW: 402 _FinishedDrag(); 403 break; 404 405 case B_INSIDE_VIEW: 406 if (fBarView->Dragging()) { 407 TTeamMenuItem* item = NULL; 408 for (int32 i = 0; i < CountItems(); i++) { 409 BMenuItem* _item = ItemAt(i); 410 if (_item->Frame().Contains(where)) { 411 item = dynamic_cast<TTeamMenuItem*>(_item); 412 break; 413 } 414 } 415 if (item == fPreviousDragTargetItem) 416 break; 417 if (fPreviousDragTargetItem != NULL) 418 fPreviousDragTargetItem->SetOverrideSelected(false); 419 if (item != NULL) 420 item->SetOverrideSelected(true); 421 fPreviousDragTargetItem = item; 422 } 423 break; 424 } 425 } 426 427 428 void 429 TExpandoMenuBar::MouseUp(BPoint where) 430 { 431 if (!fBarView->Dragging()) { 432 BMenuBar::MouseUp(where); 433 return; 434 } 435 436 _FinishedDrag(true); 437 } 438 439 440 bool 441 TExpandoMenuBar::InBeMenu(BPoint loc) const 442 { 443 if (!fVertical) { 444 if (fBeMenuItem && fBeMenuItem->Frame().Contains(loc)) 445 return true; 446 } else { 447 TBarWindow* window = dynamic_cast<TBarWindow*>(Window()); 448 if (window) { 449 if (TBeMenu* bemenu = window->BeMenu()) { 450 bool inBeMenu = false; 451 if (bemenu->LockLooper()) { 452 inBeMenu = bemenu->Frame().Contains(loc); 453 bemenu->UnlockLooper(); 454 } 455 return inBeMenu; 456 } 457 } 458 } 459 460 return false; 461 } 462 463 464 /*! Returns the team menu item that belongs to the item under the 465 specified \a point. 466 If \a _item is given, it will return the exact menu item under 467 that point (which might be a window item when the expander is on). 468 */ 469 TTeamMenuItem* 470 TExpandoMenuBar::TeamItemAtPoint(BPoint point, BMenuItem** _item) 471 { 472 TTeamMenuItem* lastApp = NULL; 473 int32 count = CountItems(); 474 475 for (int32 i = fFirstApp; i < count; i++) { 476 BMenuItem* item = ItemAt(i); 477 478 if (dynamic_cast<TTeamMenuItem*>(item) != NULL) 479 lastApp = (TTeamMenuItem*)item; 480 481 if (item && item->Frame().Contains(point)) { 482 if (_item != NULL) 483 *_item = item; 484 485 return lastApp; 486 } 487 } 488 489 // no item found 490 491 if (_item != NULL) 492 *_item = NULL; 493 494 return NULL; 495 } 496 497 498 void 499 TExpandoMenuBar::AddTeam(BList* team, BBitmap* icon, char* name, 500 char* signature) 501 { 502 float itemWidth = fVertical ? fBarView->Bounds().Width() 503 : sMinimumWindowWidth; 504 float itemHeight = -1.0f; 505 506 desk_settings* settings = ((TBarApp*)be_app)->Settings(); 507 TTeamMenuItem* item = new TTeamMenuItem(team, icon, name, signature, 508 itemWidth, itemHeight, fDrawLabel, fVertical); 509 510 if (settings->trackerAlwaysFirst && !strcmp(signature, kTrackerSignature)) { 511 AddItem(item, fFirstApp); 512 } else if (settings->sortRunningApps) { 513 TTeamMenuItem* teamItem 514 = dynamic_cast<TTeamMenuItem*>(ItemAt(fFirstApp)); 515 int32 firstApp = fFirstApp; 516 517 // if Tracker should always be the first item, we need to skip it 518 // when sorting in the current item 519 if (settings->trackerAlwaysFirst && teamItem != NULL 520 && !strcmp(teamItem->Signature(), kTrackerSignature)) { 521 firstApp++; 522 } 523 524 int32 count = CountItems(), i; 525 for (i = firstApp; i < count; i++) { 526 teamItem = dynamic_cast<TTeamMenuItem*>(ItemAt(i)); 527 if (teamItem != NULL && strcasecmp(teamItem->Name(), name) > 0) { 528 AddItem(item, i); 529 break; 530 } 531 } 532 // was the item added to the list yet? 533 if (i == count) 534 AddItem(item); 535 } else 536 AddItem(item); 537 538 if (fVertical) { 539 if (item && fShowTeamExpander && fExpandNewTeams) 540 item->ToggleExpandState(false); 541 542 fBarView->SizeWindow(BScreen(Window()).Frame()); 543 } else 544 CheckItemSizes(1); 545 546 Window()->UpdateIfNeeded(); 547 } 548 549 550 void 551 TExpandoMenuBar::AddTeam(team_id team, const char* signature) 552 { 553 int32 count = CountItems(); 554 for (int32 i = fFirstApp; i < count; i++) { 555 // Only add to team menu items 556 if (TTeamMenuItem* item = dynamic_cast<TTeamMenuItem*>(ItemAt(i))) { 557 if (strcasecmp(item->Signature(), signature) == 0) { 558 if (!(item->Teams()->HasItem((void*)team))) 559 item->Teams()->AddItem((void*)team); 560 561 break; 562 } 563 } 564 } 565 } 566 567 568 void 569 TExpandoMenuBar::RemoveTeam(team_id team, bool partial) 570 { 571 int32 count = CountItems(); 572 for (int32 i = fFirstApp; i < count; i++) { 573 if (TTeamMenuItem* item = dynamic_cast<TTeamMenuItem*>(ItemAt(i))) { 574 if (item->Teams()->HasItem((void*)team)) { 575 item->Teams()->RemoveItem(team); 576 577 if (partial) 578 return; 579 580 #ifdef DOUBLECLICKBRINGSTOFRONT 581 if (fLastClickItem == i) 582 fLastClickItem = -1; 583 #endif 584 585 RemoveItem(i); 586 587 if (fVertical) { 588 // instead of resizing the window here and there in the 589 // code the resize method will be centered in one place 590 // thus, the same behavior (good or bad) will be used 591 // whereever window sizing is done 592 fBarView->SizeWindow(BScreen(Window()).Frame()); 593 } else 594 CheckItemSizes(-1); 595 596 Window()->UpdateIfNeeded(); 597 598 delete item; 599 return; 600 } 601 } 602 } 603 } 604 605 606 void 607 TExpandoMenuBar::CheckItemSizes(int32 delta) 608 { 609 float width = Frame().Width(); 610 int32 count = CountItems(); 611 bool reset = false; 612 float newWidth = 0; 613 float fullWidth = (sMinimumWindowWidth * count); 614 615 if (!fBarView->Vertical()) { 616 // in this case there are 2 extra items: 617 // - The Be Menu 618 // - The little separator item 619 fullWidth = fullWidth - (sMinimumWindowWidth * 2) 620 + (fBeMenuWidth + kSepItemWidth); 621 width -= (fBeMenuWidth + kSepItemWidth); 622 count -= 2; 623 } 624 625 if (delta >= 0 && fullWidth > width) { 626 fOverflow = true; 627 reset = true; 628 newWidth = floorf(width / count); 629 } else if (delta < 0 && fOverflow) { 630 reset = true; 631 if (fullWidth > width) 632 newWidth = floorf(width / count); 633 else 634 newWidth = sMinimumWindowWidth; 635 } 636 if (newWidth > sMinimumWindowWidth) 637 newWidth = sMinimumWindowWidth; 638 639 if (reset) { 640 SetMaxContentWidth(newWidth); 641 if (newWidth == sMinimumWindowWidth) 642 fOverflow = false; 643 InvalidateLayout(); 644 645 for (int32 index = fFirstApp; ; index++) { 646 TTeamMenuItem* item = (TTeamMenuItem*)ItemAt(index); 647 if (!item) 648 break; 649 item->SetOverrideWidth(newWidth); 650 } 651 652 Invalidate(); 653 Window()->UpdateIfNeeded(); 654 } 655 } 656 657 658 menu_layout 659 TExpandoMenuBar::MenuLayout() const 660 { 661 return Layout(); 662 } 663 664 665 void 666 TExpandoMenuBar::Draw(BRect update) 667 { 668 BMenu::Draw(update); 669 } 670 671 672 void 673 TExpandoMenuBar::DrawBackground(BRect) 674 { 675 if (fVertical) 676 return; 677 678 BRect bounds(Bounds()); 679 rgb_color menuColor = ViewColor(); 680 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 681 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT); 682 rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT); 683 684 int32 last = CountItems() - 1; 685 if (last >= 0) 686 bounds.left = ItemAt(last)->Frame().right + 1; 687 else 688 bounds.left = 0; 689 690 if (be_control_look != NULL) { 691 SetHighColor(tint_color(menuColor, 1.22)); 692 StrokeLine(bounds.LeftTop(), bounds.LeftBottom()); 693 bounds.left++; 694 uint32 borders = BControlLook::B_TOP_BORDER 695 | BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER; 696 697 be_control_look->DrawButtonBackground(this, bounds, bounds, menuColor, 698 0, borders); 699 } else { 700 SetHighColor(vlight); 701 StrokeLine(bounds.LeftTop(), bounds.RightTop()); 702 StrokeLine(BPoint(bounds.left, bounds.top + 1), bounds.LeftBottom()); 703 SetHighColor(hilite); 704 StrokeLine(BPoint(bounds.left + 1, bounds.bottom), 705 bounds.RightBottom()); 706 } 707 } 708 709 710 /*! Something to help determine if we are showing too many apps 711 need to add in scrolling functionality. 712 */ 713 void 714 TExpandoMenuBar::CheckForSizeOverrun() 715 { 716 BRect screenFrame = (BScreen(Window())).Frame(); 717 718 fIsScrolling = fVertical ? Window()->Frame().bottom > screenFrame.bottom 719 : 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