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