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