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