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