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