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