btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / deskbar / ExpandoMenuBar.cpp
blob6b9ebd9a366fc31d03b82180296f30a434ce4906
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
37 #include "ExpandoMenuBar.h"
39 #include <strings.h>
41 #include <map>
43 #include <Autolock.h>
44 #include <Bitmap.h>
45 #include <Collator.h>
46 #include <ControlLook.h>
47 #include <Debug.h>
48 #include <MenuPrivate.h>
49 #include <NodeInfo.h>
50 #include <Roster.h>
51 #include <Screen.h>
52 #include <Thread.h>
53 #include <Window.h>
55 #include "icons.h"
57 #include "BarApp.h"
58 #include "BarMenuTitle.h"
59 #include "BarView.h"
60 #include "BarWindow.h"
61 #include "DeskbarMenu.h"
62 #include "DeskbarUtils.h"
63 #include "InlineScrollView.h"
64 #include "ResourceSet.h"
65 #include "ShowHideMenuItem.h"
66 #include "StatusView.h"
67 #include "TeamMenu.h"
68 #include "TeamMenuItem.h"
69 #include "WindowMenu.h"
70 #include "WindowMenuItem.h"
73 const float kMinMenuItemWidth = 50.0f;
74 const float kSepItemWidth = 5.0f;
75 const float kIconPadding = 8.0f;
77 const uint32 kMinimizeTeam = 'mntm';
78 const uint32 kBringTeamToFront = 'bftm';
80 bool TExpandoMenuBar::sDoMonitor = false;
81 thread_id TExpandoMenuBar::sMonThread = B_ERROR;
82 BLocker TExpandoMenuBar::sMonLocker("expando monitor");
84 typedef std::map<BString, TTeamMenuItem*> TeamMenuItemMap;
87 // #pragma mark - TExpandoMenuBar
90 TExpandoMenuBar::TExpandoMenuBar(TBarView* barView, bool vertical)
92 BMenuBar(BRect(0, 0, 0, 0), "ExpandoMenuBar", B_FOLLOW_NONE,
93 vertical ? B_ITEMS_IN_COLUMN : B_ITEMS_IN_ROW),
94 fBarView(barView),
95 fVertical(vertical),
96 fOverflow(false),
97 fFirstBuild(true),
98 fDeskbarMenuWidth(kMinMenuItemWidth),
99 fPreviousDragTargetItem(NULL),
100 fLastMousedOverItem(NULL),
101 fLastClickedItem(NULL)
103 SetItemMargins(0.0f, 0.0f, 0.0f, 0.0f);
104 SetFont(be_plain_font);
105 SetMaxItemWidth();
107 // top or bottom mode, add deskbar menu and sep for menubar tracking
108 // consistency
109 const BBitmap* logoBitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE,
110 R_LeafLogoBitmap);
111 if (logoBitmap != NULL)
112 fDeskbarMenuWidth = logoBitmap->Bounds().Width() + 16;
116 void
117 TExpandoMenuBar::AllAttached()
119 BMenuBar::AllAttached();
121 SizeWindow(0);
125 void
126 TExpandoMenuBar::AttachedToWindow()
128 BMenuBar::AttachedToWindow();
130 fTeamList.MakeEmpty();
132 if (fVertical)
133 StartMonitoringWindows();
137 void
138 TExpandoMenuBar::DetachedFromWindow()
140 BMenuBar::DetachedFromWindow();
142 StopMonitoringWindows();
144 BMessenger self(this);
145 BMessage message(kUnsubscribe);
146 message.AddMessenger("messenger", self);
147 be_app->PostMessage(&message);
149 RemoveItems(0, CountItems(), true);
153 void
154 TExpandoMenuBar::MessageReceived(BMessage* message)
156 int32 index;
157 TTeamMenuItem* item;
159 switch (message->what) {
160 case B_SOME_APP_LAUNCHED:
162 BList* teams = NULL;
163 message->FindPointer("teams", (void**)&teams);
165 BBitmap* icon = NULL;
166 message->FindPointer("icon", (void**)&icon);
168 const char* signature = NULL;
169 message->FindString("sig", &signature);
171 uint32 flags = 0;
172 message->FindInt32("flags", ((int32*) &flags));
174 const char* name = NULL;
175 message->FindString("name", &name);
177 AddTeam(teams, icon, strdup(name), strdup(signature));
178 break;
181 case B_MOUSE_WHEEL_CHANGED:
183 float deltaY = 0;
184 message->FindFloat("be:wheel_delta_y", &deltaY);
185 if (deltaY == 0)
186 return;
188 TInlineScrollView* scrollView
189 = dynamic_cast<TInlineScrollView*>(Parent());
190 if (scrollView == NULL)
191 return;
193 float largeStep;
194 float smallStep;
195 scrollView->GetSteps(&smallStep, &largeStep);
197 // pressing the option/command/control key scrolls faster
198 if (modifiers() & (B_OPTION_KEY | B_COMMAND_KEY | B_CONTROL_KEY))
199 deltaY *= largeStep;
200 else
201 deltaY *= smallStep;
203 scrollView->ScrollBy(deltaY);
204 break;
207 case kAddTeam:
208 AddTeam(message->FindInt32("team"), message->FindString("sig"));
209 break;
211 case kRemoveTeam:
213 team_id team = -1;
214 message->FindInt32("team", &team);
216 RemoveTeam(team, true);
217 break;
220 case B_SOME_APP_QUIT:
222 team_id team = -1;
223 message->FindInt32("team", &team);
225 RemoveTeam(team, false);
226 break;
229 case kMinimizeTeam:
231 index = message->FindInt32("itemIndex");
232 item = dynamic_cast<TTeamMenuItem*>(ItemAt(index));
233 if (item == NULL)
234 break;
236 TShowHideMenuItem::TeamShowHideCommon(B_MINIMIZE_WINDOW,
237 item->Teams(),
238 item->Menu()->ConvertToScreen(item->Frame()),
239 true);
240 break;
243 case kBringTeamToFront:
245 index = message->FindInt32("itemIndex");
246 item = dynamic_cast<TTeamMenuItem*>(ItemAt(index));
247 if (item == NULL)
248 break;
250 TShowHideMenuItem::TeamShowHideCommon(B_BRING_TO_FRONT,
251 item->Teams(), item->Menu()->ConvertToScreen(item->Frame()),
252 true);
253 break;
256 default:
257 BMenuBar::MessageReceived(message);
258 break;
263 void
264 TExpandoMenuBar::MouseDown(BPoint where)
266 BMessage* message = Window()->CurrentMessage();
267 BMenuItem* menuItem;
268 TTeamMenuItem* item = TeamItemAtPoint(where, &menuItem);
270 if (message == NULL || item == NULL || fBarView->Dragging()) {
271 BMenuBar::MouseDown(where);
272 return;
275 int32 modifiers = 0;
276 message->FindInt32("modifiers", &modifiers);
278 // check for three finger salute, a.k.a. Vulcan Death Grip
279 if ((modifiers & B_COMMAND_KEY) != 0
280 && (modifiers & B_CONTROL_KEY) != 0
281 && (modifiers & B_SHIFT_KEY) != 0) {
282 const BList* teams = item->Teams();
283 int32 teamCount = teams->CountItems();
284 team_id teamID;
285 for (int32 team = 0; team < teamCount; team++) {
286 teamID = (addr_t)teams->ItemAt(team);
287 kill_team(teamID);
288 RemoveTeam(teamID, false);
289 // remove the team from display immediately
291 return;
292 // absorb the message
295 // control click - show all/hide all shortcut
296 if ((modifiers & B_CONTROL_KEY) != 0) {
297 // show/hide item's teams
298 BMessage showMessage((modifiers & B_SHIFT_KEY) != 0
299 ? kMinimizeTeam : kBringTeamToFront);
300 showMessage.AddInt32("itemIndex", IndexOf(item));
301 Window()->PostMessage(&showMessage, this);
302 return;
303 // absorb the message
306 // check if within expander bounds to expand window items
307 if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando
308 && item->ExpanderBounds().Contains(where)) {
309 // start the animation here, finish on mouse up
310 fLastClickedItem = item;
311 MouseDownThread<TExpandoMenuBar>::TrackMouse(this,
312 &TExpandoMenuBar::_DoneTracking, &TExpandoMenuBar::_Track);
313 Invalidate(item->ExpanderBounds());
314 return;
315 // absorb the message
318 // double-click on an item brings the team to front
319 int32 clicks;
320 if (message->FindInt32("clicks", &clicks) == B_OK && clicks > 1
321 && item == menuItem && item == fLastClickedItem) {
322 be_roster->ActivateApp((addr_t)item->Teams()->ItemAt(0));
323 // activate this team
324 return;
325 // absorb the message
328 fLastClickedItem = item;
329 BMenuBar::MouseDown(where);
333 void
334 TExpandoMenuBar::MouseMoved(BPoint where, uint32 code, const BMessage* message)
336 int32 buttons;
337 BMessage* currentMessage = Window()->CurrentMessage();
338 if (currentMessage == NULL
339 || currentMessage->FindInt32("buttons", &buttons) != B_OK) {
340 buttons = 0;
343 if (message == NULL) {
344 // force a cleanup
345 _FinishedDrag();
347 switch (code) {
348 case B_INSIDE_VIEW:
350 BMenuItem* menuItem;
351 TTeamMenuItem* item = TeamItemAtPoint(where, &menuItem);
352 TWindowMenuItem* windowMenuItem
353 = dynamic_cast<TWindowMenuItem*>(menuItem);
355 if (item == NULL || menuItem == NULL) {
356 // item is NULL, remove the tooltip and break out
357 fLastMousedOverItem = NULL;
358 SetToolTip((const char*)NULL);
359 break;
362 if (menuItem == fLastMousedOverItem) {
363 // already set the tooltip for this item, break out
364 break;
367 if (windowMenuItem != NULL && fBarView->Vertical()
368 && fBarView->ExpandoState() && item->IsExpanded()) {
369 // expando mode window menu item
370 fLastMousedOverItem = menuItem;
371 if (strcasecmp(windowMenuItem->TruncatedLabel(),
372 windowMenuItem->Label()) > 0) {
373 // label is truncated, set tooltip
374 SetToolTip(windowMenuItem->Label());
375 } else
376 SetToolTip((const char*)NULL);
378 break;
381 if (!dynamic_cast<TBarApp*>(be_app)->Settings()->hideLabels) {
382 // item has a visible label, set tool tip if truncated
383 fLastMousedOverItem = menuItem;
384 if (strcasecmp(item->TruncatedLabel(), item->Label()) > 0) {
385 // label is truncated, set tooltip
386 SetToolTip(item->Label());
387 } else
388 SetToolTip((const char*)NULL);
390 break;
393 SetToolTip(item->Label());
394 // new item, set the tooltip to the item label
395 fLastMousedOverItem = menuItem;
396 // save the current menuitem for the next MouseMoved() call
397 break;
401 BMenuBar::MouseMoved(where, code, message);
402 return;
405 if (buttons == 0)
406 return;
408 switch (code) {
409 case B_ENTERED_VIEW:
410 // fPreviousDragTargetItem should always be NULL here anyways.
411 if (fPreviousDragTargetItem != NULL)
412 _FinishedDrag();
414 fBarView->CacheDragData(message);
415 fPreviousDragTargetItem = NULL;
416 break;
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 _FinishedDrag();
423 break;
425 case B_INSIDE_VIEW:
426 if (fBarView->Dragging()) {
427 TTeamMenuItem* item = NULL;
428 int32 itemCount = CountItems();
429 for (int32 i = 0; i < itemCount; i++) {
430 BMenuItem* _item = ItemAt(i);
431 if (_item->Frame().Contains(where)) {
432 item = dynamic_cast<TTeamMenuItem*>(_item);
433 break;
436 if (item == fPreviousDragTargetItem)
437 break;
438 if (fPreviousDragTargetItem != NULL)
439 fPreviousDragTargetItem->SetOverrideSelected(false);
440 if (item != NULL)
441 item->SetOverrideSelected(true);
442 fPreviousDragTargetItem = item;
444 break;
449 void
450 TExpandoMenuBar::MouseUp(BPoint where)
452 if (fBarView->Dragging()) {
453 _FinishedDrag(true);
454 return;
455 // absorb the message
458 BMenuBar::MouseUp(where);
462 void
463 TExpandoMenuBar::BuildItems()
465 BMessenger self(this);
466 TBarApp::Subscribe(self, &fTeamList);
468 int32 iconSize = static_cast<TBarApp*>(be_app)->IconSize();
469 desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings();
471 float itemWidth = -1.0f;
472 if (fVertical) {
473 itemWidth = Frame().Width();
474 SetMaxContentWidth(itemWidth);
475 } else {
476 itemWidth = iconSize;
477 if (!settings->hideLabels)
478 itemWidth += gMinimumWindowWidth - kMinimumIconSize;
479 else
480 itemWidth += kIconPadding * 2;
482 float itemHeight = -1.0f;
484 TeamMenuItemMap items;
485 int32 itemCount = CountItems();
486 BList itemList(itemCount);
487 for (int32 i = 0; i < itemCount; i++) {
488 BMenuItem* menuItem = RemoveItem((int32)0);
489 itemList.AddItem(menuItem);
490 TTeamMenuItem* item = dynamic_cast<TTeamMenuItem*>(menuItem);
491 if (item != NULL)
492 items[BString(item->Signature()).ToLower()] = item;
495 if (settings->sortRunningApps)
496 fTeamList.SortItems(TTeamMenu::CompareByName);
498 int32 teamCount = fTeamList.CountItems();
499 for (int32 i = 0; i < teamCount; i++) {
500 BarTeamInfo* barInfo = (BarTeamInfo*)fTeamList.ItemAt(i);
501 TeamMenuItemMap::const_iterator iter
502 = items.find(BString(barInfo->sig).ToLower());
503 if (iter == items.end()) {
504 // new team
505 TTeamMenuItem* item = new TTeamMenuItem(barInfo->teams,
506 barInfo->icon, barInfo->name, barInfo->sig, itemWidth,
507 itemHeight);
509 if (settings->trackerAlwaysFirst
510 && strcasecmp(barInfo->sig, kTrackerSignature) == 0) {
511 AddItem(item, 0);
512 } else
513 AddItem(item);
515 if (fFirstBuild && fVertical && settings->expandNewTeams)
516 item->ToggleExpandState(true);
517 } else {
518 // existing team, update info and add it
519 TTeamMenuItem* item = iter->second;
520 item->SetIcon(barInfo->icon);
521 item->SetOverrideWidth(itemWidth);
522 item->SetOverrideHeight(itemHeight);
524 if (settings->trackerAlwaysFirst
525 && strcasecmp(barInfo->sig, kTrackerSignature) == 0) {
526 AddItem(item, 0);
527 } else
528 AddItem(item);
530 // add window items back
531 int32 index = itemList.IndexOf(item);
532 TWindowMenuItem* windowItem;
533 TWindowMenu* submenu = dynamic_cast<TWindowMenu*>(item->Submenu());
534 bool hasWindowItems = false;
535 while ((windowItem = dynamic_cast<TWindowMenuItem*>(
536 (BMenuItem*)(itemList.ItemAt(++index)))) != NULL) {
537 if (fVertical)
538 AddItem(windowItem);
539 else {
540 delete windowItem;
541 hasWindowItems = submenu != NULL;
545 // unexpand if turn off show team expander
546 if (fVertical && !settings->superExpando && item->IsExpanded())
547 item->ToggleExpandState(false);
549 if (hasWindowItems) {
550 // add (new) window items in submenu
551 submenu->SetExpanded(false, 0);
552 submenu->AttachedToWindow();
557 if (CountItems() == 0) {
558 // If we're empty, BMenuBar::AttachedToWindow() resizes us to some
559 // weird value - we just override it again
560 ResizeTo(itemWidth, 0);
563 fFirstBuild = false;
567 bool
568 TExpandoMenuBar::InDeskbarMenu(BPoint loc) const
570 TBarWindow* window = dynamic_cast<TBarWindow*>(Window());
571 if (window != NULL) {
572 if (TDeskbarMenu* bemenu = window->DeskbarMenu()) {
573 bool inDeskbarMenu = false;
574 if (bemenu->LockLooper()) {
575 inDeskbarMenu = bemenu->Frame().Contains(loc);
576 bemenu->UnlockLooper();
578 return inDeskbarMenu;
582 return false;
586 /*! Returns the team menu item that belongs to the item under the
587 specified \a point.
588 If \a _item is given, it will return the exact menu item under
589 that point (which might be a window item when the expander is on).
591 TTeamMenuItem*
592 TExpandoMenuBar::TeamItemAtPoint(BPoint point, BMenuItem** _item)
594 TTeamMenuItem* lastApp = NULL;
595 int32 count = CountItems();
597 for (int32 i = 0; i < count; i++) {
598 BMenuItem* item = ItemAt(i);
600 if (dynamic_cast<TTeamMenuItem*>(item) != NULL)
601 lastApp = (TTeamMenuItem*)item;
603 if (item && item->Frame().Contains(point)) {
604 if (_item != NULL)
605 *_item = item;
607 return lastApp;
611 // no item found
613 if (_item != NULL)
614 *_item = NULL;
616 return NULL;
620 void
621 TExpandoMenuBar::AddTeam(BList* team, BBitmap* icon, char* name,
622 char* signature)
624 int32 iconSize = static_cast<TBarApp*>(be_app)->IconSize();
625 desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings();
627 float itemWidth = -1.0f;
628 if (fVertical)
629 itemWidth = fBarView->Bounds().Width();
630 else {
631 itemWidth = iconSize;
632 if (!settings->hideLabels)
633 itemWidth += gMinimumWindowWidth - kMinimumIconSize;
634 else
635 itemWidth += kIconPadding * 2;
637 float itemHeight = -1.0f;
639 TTeamMenuItem* item = new TTeamMenuItem(team, icon, name, signature,
640 itemWidth, itemHeight);
642 if (settings->trackerAlwaysFirst
643 && strcasecmp(signature, kTrackerSignature) == 0) {
644 AddItem(item, 0);
645 } else if (settings->sortRunningApps) {
646 TTeamMenuItem* teamItem = dynamic_cast<TTeamMenuItem*>(ItemAt(0));
647 int32 firstApp = 0;
649 // if Tracker should always be the first item, we need to skip it
650 // when sorting in the current item
651 if (settings->trackerAlwaysFirst && teamItem != NULL
652 && strcasecmp(teamItem->Signature(), kTrackerSignature) == 0) {
653 firstApp++;
656 BCollator collator;
657 BLocale::Default()->GetCollator(&collator);
659 int32 i = firstApp;
660 int32 itemCount = CountItems();
661 while (i < itemCount) {
662 teamItem = dynamic_cast<TTeamMenuItem*>(ItemAt(i));
663 if (teamItem != NULL && collator.Compare(teamItem->Label(), name)
664 > 0) {
665 AddItem(item, i);
666 break;
668 i++;
670 // was the item added to the list yet?
671 if (i == itemCount)
672 AddItem(item);
673 } else
674 AddItem(item);
676 if (fVertical && settings->superExpando && settings->expandNewTeams)
677 item->ToggleExpandState(false);
679 SizeWindow(1);
680 Window()->UpdateIfNeeded();
684 void
685 TExpandoMenuBar::AddTeam(team_id team, const char* signature)
687 int32 itemCount = CountItems();
688 for (int32 i = 0; i < itemCount; i++) {
689 // Only add to team menu items
690 TTeamMenuItem* item = dynamic_cast<TTeamMenuItem*>(ItemAt(i));
691 if (item != NULL && strcasecmp(item->Signature(), signature) == 0
692 && !(item->Teams()->HasItem((void*)(addr_t)team))) {
693 item->Teams()->AddItem((void*)(addr_t)team);
694 break;
700 void
701 TExpandoMenuBar::RemoveTeam(team_id team, bool partial)
703 TWindowMenuItem* windowItem = NULL;
705 for (int32 i = CountItems() - 1; i >= 0; i--) {
706 TTeamMenuItem* item = dynamic_cast<TTeamMenuItem*>(ItemAt(i));
707 if (item != NULL && item->Teams()->HasItem((void*)(addr_t)team)) {
708 item->Teams()->RemoveItem(team);
709 if (partial)
710 return;
712 BAutolock locker(sMonLocker);
713 // make the update thread wait
714 RemoveItem(i);
715 if (item == fPreviousDragTargetItem)
716 fPreviousDragTargetItem = NULL;
718 if (item == fLastMousedOverItem)
719 fLastMousedOverItem = NULL;
721 if (item == fLastClickedItem)
722 fLastClickedItem = NULL;
724 delete item;
725 while ((windowItem = dynamic_cast<TWindowMenuItem*>(
726 ItemAt(i))) != NULL) {
727 // Also remove window items (if there are any)
728 RemoveItem(i);
729 if (windowItem == fLastMousedOverItem)
730 fLastMousedOverItem = NULL;
732 if (windowItem == fLastClickedItem)
733 fLastClickedItem = NULL;
735 delete windowItem;
737 SizeWindow(-1);
738 Window()->UpdateIfNeeded();
739 return;
745 void
746 TExpandoMenuBar::CheckItemSizes(int32 delta)
748 if (fBarView->Vertical())
749 return;
751 bool drawLabels = !static_cast<TBarApp*>(be_app)->Settings()->hideLabels;
753 float maxWidth = fBarView->DragRegion()->Frame().left
754 - fDeskbarMenuWidth - kSepItemWidth;
755 int32 iconSize = static_cast<TBarApp*>(be_app)->IconSize();
756 float iconOnlyWidth = kIconPadding + iconSize + kIconPadding;
757 float minItemWidth = drawLabels
758 ? iconOnlyWidth + kMinMenuItemWidth
759 : iconOnlyWidth - kIconPadding;
760 float maxItemWidth = drawLabels
761 ? gMinimumWindowWidth + iconSize - kMinimumIconSize
762 : iconOnlyWidth;
763 float menuWidth = maxItemWidth * CountItems() + fDeskbarMenuWidth
764 + kSepItemWidth;
766 bool reset = false;
767 float newWidth = -1.0f;
769 if (delta >= 0 && menuWidth > maxWidth) {
770 fOverflow = true;
771 reset = true;
772 newWidth = floorf(maxWidth / CountItems());
773 } else if (delta < 0 && fOverflow) {
774 reset = true;
775 if (menuWidth > maxWidth)
776 newWidth = floorf(maxWidth / CountItems());
777 else
778 newWidth = maxItemWidth;
781 if (reset) {
782 if (newWidth > maxItemWidth)
783 newWidth = maxItemWidth;
784 else if (newWidth < minItemWidth)
785 newWidth = minItemWidth;
787 SetMaxContentWidth(newWidth);
788 if (newWidth == maxItemWidth)
789 fOverflow = false;
791 InvalidateLayout();
793 for (int32 index = 0; ; index++) {
794 TTeamMenuItem* item = (TTeamMenuItem*)ItemAt(index);
795 if (item == NULL)
796 break;
798 item->SetOverrideWidth(newWidth);
801 Invalidate();
802 Window()->UpdateIfNeeded();
803 fBarView->CheckForScrolling();
808 menu_layout
809 TExpandoMenuBar::MenuLayout() const
811 return Layout();
815 void
816 TExpandoMenuBar::SetMenuLayout(menu_layout layout)
818 fVertical = layout == B_ITEMS_IN_COLUMN;
819 BPrivate::MenuPrivate(this).SetLayout(layout);
820 SetMaxItemWidth();
821 // when the menu layout changes, make sure to set the max width
825 void
826 TExpandoMenuBar::Draw(BRect updateRect)
828 BMenu::Draw(updateRect);
832 void
833 TExpandoMenuBar::DrawBackground(BRect updateRect)
835 if (fVertical)
836 return;
838 BRect bounds(Bounds());
839 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
840 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
841 rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT);
843 int32 count = CountItems() - 1;
844 if (count >= 0)
845 bounds.left = ItemAt(count)->Frame().right + 1;
846 else
847 bounds.left = 0;
849 if (be_control_look != NULL) {
850 SetHighColor(tint_color(menuColor, 1.22));
851 StrokeLine(bounds.LeftTop(), bounds.LeftBottom());
852 bounds.left++;
853 uint32 borders = BControlLook::B_TOP_BORDER
854 | BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER;
856 be_control_look->DrawButtonBackground(this, bounds, bounds, menuColor,
857 0, borders);
858 } else {
859 SetHighColor(vlight);
860 StrokeLine(bounds.LeftTop(), bounds.RightTop());
861 StrokeLine(BPoint(bounds.left, bounds.top + 1), bounds.LeftBottom());
862 SetHighColor(hilite);
863 StrokeLine(BPoint(bounds.left + 1, bounds.bottom),
864 bounds.RightBottom());
869 /*! Something to help determine if we are showing too many apps
870 need to add in scrolling functionality.
872 bool
873 TExpandoMenuBar::CheckForSizeOverrun()
875 if (fVertical) {
876 if (Window() == NULL)
877 return false;
879 BRect screenFrame = (BScreen(Window())).Frame();
880 return Window()->Frame().bottom > screenFrame.bottom;
883 // horizontal
884 int32 count = CountItems() - 1;
885 if (count < 0)
886 return false;
888 int32 iconSize = static_cast<TBarApp*>(be_app)->IconSize();
889 float iconOnlyWidth = kIconPadding + iconSize + kIconPadding;
890 float minItemWidth = !static_cast<TBarApp*>(be_app)->Settings()->hideLabels
891 ? iconOnlyWidth + kMinMenuItemWidth
892 : iconOnlyWidth - kIconPadding;
893 float menuWidth = minItemWidth * CountItems() + fDeskbarMenuWidth
894 + kSepItemWidth;
895 float maxWidth = fBarView->DragRegion()->Frame().left
896 - fDeskbarMenuWidth - kSepItemWidth;
898 return menuWidth > maxWidth;
902 void
903 TExpandoMenuBar::SetMaxItemWidth()
905 if (fVertical)
906 SetMaxContentWidth(static_cast<TBarApp*>(be_app)->Settings()->width);
907 else {
908 // Make more room for the icon in horizontal mode
909 int32 iconSize = static_cast<TBarApp*>(be_app)->IconSize();
910 SetMaxContentWidth(gMinimumWindowWidth + iconSize
911 - kMinimumIconSize);
916 void
917 TExpandoMenuBar::SizeWindow(int32 delta)
919 // instead of resizing the window here and there in the
920 // code the resize method will be centered in one place
921 // thus, the same behavior (good or bad) will be used
922 // wherever window sizing is done
923 if (fVertical) {
924 BRect screenFrame = (BScreen(Window())).Frame();
925 fBarView->SizeWindow(screenFrame);
926 fBarView->PositionWindow(screenFrame);
927 fBarView->CheckForScrolling();
928 } else
929 CheckItemSizes(delta);
933 void
934 TExpandoMenuBar::StartMonitoringWindows()
936 if (sMonThread != B_ERROR)
937 return;
939 sDoMonitor = true;
940 sMonThread = spawn_thread(monitor_team_windows,
941 "Expando Window Watcher", B_LOW_PRIORITY, this);
942 resume_thread(sMonThread);
946 void
947 TExpandoMenuBar::StopMonitoringWindows()
949 if (sMonThread == B_ERROR)
950 return;
952 sDoMonitor = false;
953 status_t returnCode;
954 wait_for_thread(sMonThread, &returnCode);
956 sMonThread = B_ERROR;
960 int32
961 TExpandoMenuBar::monitor_team_windows(void* arg)
963 TExpandoMenuBar* teamMenu = (TExpandoMenuBar*)arg;
965 while (teamMenu->sDoMonitor) {
966 sMonLocker.Lock();
968 if (teamMenu->Window()->LockWithTimeout(50000) == B_OK) {
969 int32 totalItems = teamMenu->CountItems();
971 // Set all WindowMenuItems to require an update.
972 TWindowMenuItem* item = NULL;
973 for (int32 i = 0; i < totalItems; i++) {
974 if (!teamMenu->SubmenuAt(i)) {
975 item = static_cast<TWindowMenuItem*>(teamMenu->ItemAt(i));
976 item->SetRequireUpdate(true);
980 // Perform SetTo() on all the items that still exist as well as add
981 // new items.
982 bool itemModified = false;
983 bool resize = false;
984 TTeamMenuItem* teamItem = NULL;
986 for (int32 i = 0; i < totalItems; i++) {
987 if (teamMenu->SubmenuAt(i) == NULL)
988 continue;
990 teamItem = static_cast<TTeamMenuItem*>(teamMenu->ItemAt(i));
991 if (teamItem->IsExpanded()) {
992 int32 teamCount = teamItem->Teams()->CountItems();
993 for (int32 j = 0; j < teamCount; j++) {
994 // The following code is almost a copy/paste from
995 // WindowMenu.cpp
996 team_id theTeam = (addr_t)teamItem->Teams()->ItemAt(j);
997 int32 count = 0;
998 int32* tokens = get_token_list(theTeam, &count);
1000 for (int32 k = 0; k < count; k++) {
1001 client_window_info* wInfo
1002 = get_window_info(tokens[k]);
1003 if (wInfo == NULL)
1004 continue;
1006 BString windowName(wInfo->name);
1008 BString teamPrefix(teamItem->Label());
1009 teamPrefix.Append(": ");
1011 BString teamSuffix(" - ");
1012 teamSuffix.Append(teamItem->Label());
1014 if (windowName.StartsWith(teamPrefix))
1015 windowName.RemoveFirst(teamPrefix);
1016 if (windowName.EndsWith(teamSuffix))
1017 windowName.RemoveLast(teamSuffix);
1019 if (TWindowMenu::WindowShouldBeListed(wInfo)) {
1020 // Check if we have a matching window item...
1021 item = teamItem->ExpandedWindowItem(
1022 wInfo->server_token);
1023 if (item != NULL) {
1024 item->SetTo(windowName,
1025 wInfo->server_token, wInfo->is_mini,
1026 ((1 << current_workspace())
1027 & wInfo->workspaces) != 0);
1029 if (strcasecmp(item->Label(), windowName) > 0)
1030 item->SetLabel(windowName);
1032 if (item->Modified())
1033 itemModified = true;
1034 } else if (teamItem->IsExpanded()) {
1035 // Add the item
1036 item = new TWindowMenuItem(windowName,
1037 wInfo->server_token, wInfo->is_mini,
1038 ((1 << current_workspace())
1039 & wInfo->workspaces) != 0, false);
1040 item->SetExpanded(true);
1041 teamMenu->AddItem(item,
1042 TWindowMenuItem::InsertIndexFor(
1043 teamMenu, i + 1, item));
1044 resize = true;
1047 free(wInfo);
1049 free(tokens);
1054 // Remove any remaining items which require an update.
1055 for (int32 i = 0; i < totalItems; i++) {
1056 if (!teamMenu->SubmenuAt(i)) {
1057 item = static_cast<TWindowMenuItem*>(teamMenu->ItemAt(i));
1058 if (item && item->RequiresUpdate()) {
1059 item = static_cast<TWindowMenuItem*>
1060 (teamMenu->RemoveItem(i));
1061 delete item;
1062 totalItems--;
1064 resize = true;
1069 // If any of the WindowMenuItems changed state, we need to force a
1070 // repaint.
1071 if (itemModified || resize) {
1072 teamMenu->Invalidate();
1073 if (resize)
1074 teamMenu->SizeWindow(1);
1077 teamMenu->Window()->Unlock();
1080 sMonLocker.Unlock();
1082 // sleep for a bit...
1083 snooze(150000);
1085 return B_OK;
1089 void
1090 TExpandoMenuBar::_FinishedDrag(bool invoke)
1092 if (fPreviousDragTargetItem != NULL) {
1093 if (invoke)
1094 fPreviousDragTargetItem->Invoke();
1096 fPreviousDragTargetItem->SetOverrideSelected(false);
1097 fPreviousDragTargetItem = NULL;
1100 if (!invoke && fBarView->Dragging())
1101 fBarView->DragStop(true);
1105 void
1106 TExpandoMenuBar::_DoneTracking(BPoint where)
1108 TTeamMenuItem* lastItem = dynamic_cast<TTeamMenuItem*>(fLastClickedItem);
1109 if (lastItem == NULL)
1110 return;
1112 if (!lastItem->ExpanderBounds().Contains(where))
1113 return;
1115 lastItem->ToggleExpandState(true);
1116 lastItem->SetArrowDirection(lastItem->IsExpanded()
1117 ? BControlLook::B_DOWN_ARROW
1118 : BControlLook::B_RIGHT_ARROW);
1120 Invalidate(lastItem->ExpanderBounds());
1124 void
1125 TExpandoMenuBar::_Track(BPoint where, uint32)
1127 TTeamMenuItem* lastItem = dynamic_cast<TTeamMenuItem*>(fLastClickedItem);
1128 if (lastItem == NULL)
1129 return;
1131 if (lastItem->ExpanderBounds().Contains(where))
1132 lastItem->SetArrowDirection(BControlLook::B_RIGHT_DOWN_ARROW);
1133 else {
1134 lastItem->SetArrowDirection(lastItem->IsExpanded()
1135 ? BControlLook::B_DOWN_ARROW
1136 : BControlLook::B_RIGHT_ARROW);
1139 Invalidate(lastItem->ExpanderBounds());