xref: /haiku/src/apps/deskbar/ExpandoMenuBar.cpp (revision 3cb015b1ee509d69c643506e8ff573808c86dcfc)
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 trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include <Debug.h>
36 #include <string.h>
37 #include <NodeInfo.h>
38 #include <Roster.h>
39 #include <Screen.h>
40 #include <Bitmap.h>
41 #include <Autolock.h>
42 
43 #include "icons.h"
44 #include "icons_logo.h"
45 #include "BarApp.h"
46 #include "BarMenuTitle.h"
47 #include "BarView.h"
48 #include "BeMenu.h"
49 #include "DeskBarUtils.h"
50 #include "ExpandoMenuBar.h"
51 #include "ResourceSet.h"
52 #include "ShowHideMenuItem.h"
53 #include "StatusView.h"
54 #include "TeamMenuItem.h"
55 #include "WindowMenu.h"
56 #include "WindowMenuItem.h"
57 
58 const float kBeMenuWidth = 50.0f;
59 const float kSepItemWidth = 5.0f;
60 
61 const uint32 M_MINIMIZE_TEAM = 'mntm';
62 const uint32 M_BRING_TEAM_TO_FRONT = 'bftm';
63 
64 
65 bool TExpandoMenuBar::sDoMonitor = false;
66 thread_id TExpandoMenuBar::sMonThread = B_ERROR;
67 BLocker TExpandoMenuBar::sMonLocker("expando monitor");
68 
69 
70 TExpandoMenuBar::TExpandoMenuBar(TBarView *bar, BRect frame, const char *name,
71 	bool vertical, bool drawLabel)
72 	: BMenuBar(frame, name, B_FOLLOW_NONE,
73 			vertical ? B_ITEMS_IN_COLUMN : B_ITEMS_IN_ROW, vertical),
74 	fVertical(vertical),
75 	fOverflow(false),
76 	fDrawLabel(drawLabel),
77 	fIsScrolling(false),
78 	fShowTeamExpander(static_cast<TBarApp *>(be_app)->Settings()->superExpando),
79 	fExpandNewTeams(static_cast<TBarApp *>(be_app)->Settings()->expandNewTeams),
80 	fBarView(bar),
81 	fFirstApp(0)
82 {
83 #ifdef DOUBLECLICKBRINGSTOFRONT
84 	fLastClickItem = -1;
85 	fLastClickTime = 0;
86 #endif
87 
88 	SetItemMargins(0.0f, 0.0f, 0.0f, 0.0f);
89 	SetFont(be_plain_font);
90 	SetMaxContentWidth(kMinimumWindowWidth);
91 }
92 
93 
94 int
95 TExpandoMenuBar::CompareByName(const void *first, const void *second)
96 {
97 	return strcasecmp((*(static_cast<BarTeamInfo * const*>(first)))->name,
98 		(*(static_cast<BarTeamInfo * const*>(second)))->name);
99 }
100 
101 
102 void
103 TExpandoMenuBar::AttachedToWindow()
104 {
105 	BMessenger self(this);
106 	BList teamList;
107 	TBarApp::Subscribe(self, &teamList);
108 	float width = fVertical ? Frame().Width() : kMinimumWindowWidth;
109 	float height = -1.0f;
110 
111 	// top or bottom mode, add be menu and sep for menubar tracking consistency
112 	if (!fVertical) {
113 		TBeMenu *beMenu = new TBeMenu(fBarView);
114 		TBarWindow::SetBeMenu(beMenu);
115  		fBeMenuItem = new TBarMenuTitle(kBeMenuWidth, Frame().Height(),
116  			AppResSet()->FindBitmap(B_MESSAGE_TYPE, R_BeLogoIcon), beMenu, true);
117 		AddItem(fBeMenuItem);
118 
119 		fSeparatorItem = new TTeamMenuItem(kSepItemWidth, height, fVertical);
120 		AddItem(fSeparatorItem);
121 		fSeparatorItem->SetEnabled(false);
122 		fFirstApp = 2;
123 	} else {
124 		fBeMenuItem = NULL;
125 		fSeparatorItem = NULL;
126 	}
127 
128 	desk_settings *settings = ((TBarApp *)be_app)->Settings();
129 
130 	if (settings->sortRunningApps)
131 		teamList.SortItems(CompareByName);
132 
133 	int32 count = teamList.CountItems();
134 	for (int32 i = 0; i < count; i++) {
135 		BarTeamInfo *barInfo = (BarTeamInfo *)teamList.ItemAt(i);
136 		if ((barInfo->flags & B_BACKGROUND_APP) == 0
137 			&& strcasecmp(barInfo->sig, kDeskbarSignature) != 0) {
138 			if (settings->trackerAlwaysFirst
139 				&& !strcmp(barInfo->sig, kTrackerSignature)) {
140 				AddItem(new TTeamMenuItem(barInfo->teams, barInfo->icon,
141 					barInfo->name, barInfo->sig, width, height,
142 					fDrawLabel, fVertical), fFirstApp);
143 			} else {
144 				AddItem(new TTeamMenuItem(barInfo->teams, barInfo->icon,
145 					barInfo->name, barInfo->sig, width, height,
146 					fDrawLabel, fVertical));
147 			}
148 
149 			barInfo->teams = NULL;
150 			barInfo->icon = NULL;
151 			barInfo->name = NULL;
152 			barInfo->sig = NULL;
153 		}
154 
155 		delete barInfo;
156 	}
157 
158 	BMenuBar::AttachedToWindow();
159 
160 	if (CountItems() == 0) {
161 		// If we're empty, BMenuBar::AttachedToWindow() resizes us to some
162 		// weird value - we just override it again
163 		ResizeTo(width, 0);
164 	}
165 
166 	if (fVertical) {
167 		sDoMonitor = true;
168 		sMonThread = spawn_thread(monitor_team_windows,
169 			"Expando Window Watcher", B_LOW_PRIORITY, this);
170 		resume_thread(sMonThread);
171 	}
172 }
173 
174 
175 void
176 TExpandoMenuBar::DetachedFromWindow()
177 {
178 	BMenuBar::DetachedFromWindow();
179 
180 	if (sMonThread != B_ERROR) {
181 		sDoMonitor = false;
182 
183 		status_t returnCode;
184 		wait_for_thread(sMonThread, &returnCode);
185 
186 		sMonThread = B_ERROR;
187 	}
188 
189 	BMessenger self(this);
190 	BMessage message(msg_Unsubscribe);
191 	message.AddMessenger("messenger", self);
192 	be_app->PostMessage(&message);
193 
194 	BMenuItem *item = NULL;
195 	while ((item = RemoveItem(0L)) != NULL)
196 		delete item;
197 }
198 
199 
200 void
201 TExpandoMenuBar::MessageReceived(BMessage *message)
202 {
203 	int32 index;
204 	TTeamMenuItem *item;
205 
206 	switch (message->what) {
207 		case B_SOME_APP_LAUNCHED: {
208 			BList *teams = NULL;
209 			message->FindPointer("teams", (void **)&teams);
210 
211 			BBitmap *icon = NULL;
212 			message->FindPointer("icon", (void **)&icon);
213 
214 			const char *signature;
215 			if (message->FindString("sig", &signature) == B_OK
216 				&&strcasecmp(signature, kDeskbarSignature) == 0) {
217 				delete teams;
218 				delete icon;
219 				break;
220 			}
221 
222 			uint32 flags;
223 			if (message->FindInt32("flags", ((int32*) &flags)) == B_OK
224 				&& (flags & B_BACKGROUND_APP) != 0) {
225 				delete teams;
226 				delete icon;
227 				break;
228 			}
229 
230 			const char *name = NULL;
231 			message->FindString("name", &name);
232 
233 			AddTeam(teams, icon, strdup(name), strdup(signature));
234 			break;
235 		}
236 
237 		case msg_AddTeam:
238 			AddTeam(message->FindInt32("team"), message->FindString("sig"));
239 			break;
240 
241 		case msg_RemoveTeam:
242 		{
243 			team_id team = -1;
244 			message->FindInt32("team", &team);
245 
246 			RemoveTeam(team, true);
247 			break;
248 		}
249 
250 		case B_SOME_APP_QUIT:
251 		{
252 			team_id team = -1;
253 			message->FindInt32("team", &team);
254 
255 			RemoveTeam(team, false);
256 			break;
257 		}
258 
259 		case M_MINIMIZE_TEAM:
260 		{
261 			index = message->FindInt32("itemIndex");
262 			item = dynamic_cast<TTeamMenuItem *>(ItemAt(index));
263 			if (item == NULL)
264 				break;
265 
266 			TShowHideMenuItem::TeamShowHideCommon(B_MINIMIZE_WINDOW,
267 				item->Teams(),
268 				item->Menu()->ConvertToScreen(item->Frame()),
269 				true);
270 			break;
271 		}
272 
273 		case M_BRING_TEAM_TO_FRONT:
274 		{
275 			index = message->FindInt32("itemIndex");
276 			item = dynamic_cast<TTeamMenuItem *>(ItemAt(index));
277 			if (item == NULL)
278 				break;
279 
280 			TShowHideMenuItem::TeamShowHideCommon(B_BRING_TO_FRONT,
281 				item->Teams(), item->Menu()->ConvertToScreen(item->Frame()),
282 				true);
283 			break;
284 		}
285 
286 		default:
287 			BMenuBar::MessageReceived(message);
288 			break;
289 	}
290 }
291 
292 
293 void
294 TExpandoMenuBar::MouseDown(BPoint where)
295 {
296 	BMessage *message = Window()->CurrentMessage();
297 
298 	// check for three finger salute, a.k.a. Vulcan Death Grip
299 	if (message != NULL) {
300 		int32 modifiers = 0;
301 		message->FindInt32("modifiers", &modifiers);
302 
303 		if ((modifiers & B_COMMAND_KEY) != 0
304 			&& (modifiers & B_OPTION_KEY) != 0
305 			&& (modifiers & B_SHIFT_KEY) != 0
306 			&& !fBarView->Dragging()) {
307 			TTeamMenuItem *item = TeamItemAtPoint(where);
308 
309 			if (item != NULL) {
310 				const BList	*teams = item->Teams();
311 				int32 teamCount = teams->CountItems();
312 
313 				team_id teamID;
314 				for (int32 team = 0; team < teamCount; team++) {
315 					teamID = (team_id)teams->ItemAt(team);
316 					kill_team(teamID);
317 					//	remove the team immediately
318 					//	from display
319 					RemoveTeam(teamID, false);
320 				}
321 
322 				return;
323 			}
324 		}
325 	}
326 
327 // This feature is broken because the menu bar never receives
328 // the second click
329 #ifdef DOUBLECLICKBRINGSTOFRONT
330 	// doubleclick on an item brings all to front
331 	for (int32 i = fFirstApp; i < count; i++) {
332 		TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(i);
333 		if (item->Frame().Contains(where)) {
334 			bigtime_t clickSpeed = 0;
335 			get_click_speed(&clickSpeed);
336 			if ( (fLastClickItem == i) &&
337 				 (clickSpeed > (system_time() - fLastClickTime)) ) {
338 				// bring this team's window to the front
339 				BMessage showMessage(M_BRING_TEAM_TO_FRONT);
340 				showMessage.AddInt32("itemIndex", i);
341 				Window()->PostMessage(&showMessage, this);
342 				return;
343 			}
344 
345 			fLastClickItem = i;
346 			fLastClickTime = system_time();
347 			break;
348 		}
349 	}
350 #endif
351 
352 	// control click - show all/hide all shortcut
353 	if (message != NULL) {
354 		int32 modifiers = 0;
355 		message->FindInt32("modifiers", &modifiers);
356 		if ((modifiers & B_CONTROL_KEY) != 0
357 			&& ! fBarView->Dragging()) {
358 			TTeamMenuItem *item = TeamItemAtPoint(where);
359 			if (item != NULL) {
360 				// show/hide item's teams
361 				BMessage showMessage((modifiers & B_SHIFT_KEY) != 0
362 					? M_MINIMIZE_TEAM : M_BRING_TEAM_TO_FRONT);
363 				showMessage.AddInt32("itemIndex", IndexOf(item));
364 				Window()->PostMessage(&showMessage, this);
365 				return;
366 			}
367 		}
368 	}
369 
370 	// Check the bounds of the expand Team icon
371 	if (fShowTeamExpander && fVertical && !fBarView->Dragging()) {
372 		TTeamMenuItem *item = TeamItemAtPoint(where);
373 		if (item != NULL) {
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 
389 	BMenuBar::MouseDown(where);
390 }
391 
392 
393 void
394 TExpandoMenuBar::MouseMoved(BPoint where, uint32 code, const BMessage *message)
395 {
396 	if (!message) {
397 		//	force a cleanup
398 		fBarView->DragStop(true);
399 		BMenuBar::MouseMoved(where, code, message);
400 		return;
401 	}
402 
403 	uint32 buttons;
404 	if (!(Window()->CurrentMessage())
405 		|| Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) < B_OK)
406 		buttons = 0;
407 
408 	switch (code) {
409 		case B_ENTERED_VIEW:
410 			if (message && buttons != 0) {
411 				fBarView->CacheDragData(message);
412 				MouseDown(where);
413 			}
414 			break;
415 
416 		case B_EXITED_VIEW:
417 			if (fBarView->Dragging() && buttons != 0) {
418 				if (!TeamItemAtPoint(where)
419 					&& !InBeMenu(where)
420 					&& (fSeparatorItem && !fSeparatorItem->Frame().Contains(where))
421 					&& !Frame().Contains(where)) {
422 					fBarView->DragStop();
423 				}
424 			}
425 			break;
426 	}
427 	BMenuBar::MouseMoved(where, code, message);
428 }
429 
430 
431 bool
432 TExpandoMenuBar::InBeMenu(BPoint loc) const
433 {
434 	if (!fVertical) {
435 		if (fBeMenuItem && fBeMenuItem->Frame().Contains(loc))
436 			return true;
437 	} else {
438 		TBarWindow *window = dynamic_cast<TBarWindow*>(Window());
439 		if (window) {
440 			if (TBeMenu *bemenu = window->BeMenu()) {
441 				bool inBeMenu = false;
442 				if (bemenu->LockLooper()) {
443 					inBeMenu = bemenu->Frame().Contains(loc);
444 					bemenu->UnlockLooper();
445 				}
446 				return inBeMenu;
447 			}
448 		}
449 	}
450 
451 	return false;
452 }
453 
454 
455 /**	Returns the team menu item that belongs to the item under the
456  *	specified \a point.
457  *	If \a _item is given, it will return the exact menu item under
458  *	that point (which might be a window item when the expander is on).
459  */
460 
461 TTeamMenuItem *
462 TExpandoMenuBar::TeamItemAtPoint(BPoint point, BMenuItem **_item)
463 {
464 	TTeamMenuItem *lastApp = NULL;
465 	int32 count = CountItems();
466 
467 	for (int32 i = fFirstApp; i < count; i++) {
468 		BMenuItem *item = ItemAt(i);
469 
470 		if (dynamic_cast<TTeamMenuItem *>(item) != NULL)
471 			lastApp = (TTeamMenuItem *)item;
472 
473 		if (item && item->Frame().Contains(point)) {
474 			if (_item != NULL)
475 				*_item = item;
476 
477 			return lastApp;
478 		}
479 	}
480 
481 	// no item found
482 
483 	if (_item != NULL)
484 		*_item = NULL;
485 
486 	return NULL;
487 }
488 
489 
490 void
491 TExpandoMenuBar::AddTeam(BList *team, BBitmap *icon, char *name, char *signature)
492 {
493 	float itemWidth = fVertical ? Frame().Width() : kMinimumWindowWidth;
494 	float itemHeight = -1.0f;
495 
496 	desk_settings *settings = ((TBarApp *)be_app)->Settings();
497 	TTeamMenuItem *item = new TTeamMenuItem(team, icon, name, signature, itemWidth,
498 		itemHeight, fDrawLabel, fVertical);
499 
500 	if (settings->trackerAlwaysFirst && !strcmp(signature, kTrackerSignature)) {
501 		AddItem(item, fFirstApp);
502 	} else if (settings->sortRunningApps) {
503 		TTeamMenuItem *teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(fFirstApp));
504 		int32 firstApp = fFirstApp;
505 
506 		// if Tracker should always be the first item, we need to skip it
507 		// when sorting in the current item
508 		if (settings->trackerAlwaysFirst && teamItem != NULL
509 			&& !strcmp(teamItem->Signature(), kTrackerSignature)) {
510 			firstApp++;
511 		}
512 
513 		int32 count = CountItems(), i;
514 		for (i = firstApp; i < count; i++) {
515 			teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(i));
516 			if (teamItem != NULL && strcasecmp(teamItem->Name(), name) > 0) {
517 				AddItem(item, i);
518 				break;
519 			}
520 		}
521 		// was the item added to the list yet?
522 		if (i == count)
523 			AddItem(item);
524 	} else
525 		AddItem(item);
526 
527 	if (fVertical) {
528 		if (item && fShowTeamExpander && fExpandNewTeams)
529 			item->ToggleExpandState(false);
530 
531 		fBarView->SizeWindow(BScreen(Window()).Frame());
532 	} else
533 		CheckItemSizes(1);
534 
535 	Window()->UpdateIfNeeded();
536 }
537 
538 
539 void
540 TExpandoMenuBar::AddTeam(team_id team, const char *signature)
541 {
542 	int32 count = CountItems();
543 	for (int32 i = fFirstApp; i < count; i++) {
544 		// Only add to team menu items
545 		if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) {
546 			if (strcasecmp(item->Signature(), signature) == 0) {
547 				if (!(item->Teams()->HasItem((void *)team)))
548 					item->Teams()->AddItem((void *)team);
549 
550 				break;
551 			}
552 		}
553 	}
554 }
555 
556 
557 void
558 TExpandoMenuBar::RemoveTeam(team_id team, bool partial)
559 {
560 	int32 count = CountItems();
561 	for (int32 i = fFirstApp; i < count; i++) {
562 		if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) {
563 			if (item->Teams()->HasItem((void *)team)) {
564 				item->Teams()->RemoveItem(team);
565 
566 				if (partial)
567 					return;
568 
569 #ifdef DOUBLECLICKBRINGSTOFRONT
570 				if (fLastClickItem == i)
571 					fLastClickItem = -1;
572 #endif
573 
574 				RemoveItem(i);
575 
576 				if (fVertical) {
577 					//	instead of resizing the window here and there in the code
578 					//	the resize method will be centered in one place
579 					//	thus, the same behavior (good or bad) will be used whereever
580 					//	window sizing is done
581 					fBarView->SizeWindow(BScreen(Window()).Frame());
582 				} else
583 					CheckItemSizes(-1);
584 
585 				Window()->UpdateIfNeeded();
586 
587 				delete item;
588 				return;
589 			}
590 		}
591 	}
592 }
593 
594 
595 void
596 TExpandoMenuBar::CheckItemSizes(int32 delta)
597 {
598 	float width = Frame().Width();
599 	int32 count = CountItems();
600 	bool reset = false;
601 	float newWidth = 0;
602 	float fullWidth = (kMinimumWindowWidth * count);
603 
604 	if (!fBarView->Vertical()) {
605 		// in this case there are 2 extra items:
606 		//		The Be Menu
607 		//		The little separator item
608 		fullWidth = fullWidth - (kMinimumWindowWidth * 2) + (kBeMenuWidth + kSepItemWidth);
609 		width -= (kBeMenuWidth + kSepItemWidth);
610 		count -= 2;
611 	}
612 
613 	if (delta >= 0 && fullWidth > width) {
614 		fOverflow = true;
615 		reset = true;
616 		newWidth = floorf(width/count);
617 	} else if (delta < 0 && fOverflow) {
618 		reset = true;
619 		if (fullWidth > width)
620 			newWidth = floorf(width/count);
621 		else
622 			newWidth = kMinimumWindowWidth;
623 	}
624 	if (newWidth > kMinimumWindowWidth)
625 		newWidth = kMinimumWindowWidth;
626 
627 	if (reset) {
628 		SetMaxContentWidth(newWidth);
629 		if (newWidth == kMinimumWindowWidth)
630 			fOverflow = false;
631 		InvalidateLayout();
632 
633 		for (int32 index = fFirstApp; ; index++) {
634 			TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(index);
635 			if (!item)
636 				break;
637 			item->SetOverrideWidth(newWidth);
638 		}
639 
640 		Invalidate();
641 		Window()->UpdateIfNeeded();
642 	}
643 }
644 
645 
646 menu_layout
647 TExpandoMenuBar::MenuLayout() const
648 {
649 	return Layout();
650 }
651 
652 
653 void
654 TExpandoMenuBar::Draw(BRect update)
655 {
656 	BMenu::Draw(update);
657 }
658 
659 
660 void
661 TExpandoMenuBar::DrawBackground(BRect)
662 {
663 	BRect bounds(Bounds());
664 	rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
665 	rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
666 	rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT);
667 	rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT);
668 	int32 last = CountItems() - 1;
669 	float start;
670 
671 	if (last >= 0)
672 		start = ItemAt(last)->Frame().right + 1;
673 	else
674 		start = 0;
675 
676 	if (!fVertical) {
677 		SetHighColor(vlight);
678 		StrokeLine(BPoint(start, bounds.top+1), bounds.RightTop() + BPoint(0,1));
679 		StrokeLine(BPoint(start, bounds.top+1), BPoint(start, bounds.bottom));
680 		SetHighColor(hilite);
681 		StrokeLine(BPoint(start+1, bounds.bottom), bounds.RightBottom());
682 	}
683 }
684 
685 
686 /**	Something to help determine if we are showing too many apps
687  *	need to add in scrolling functionality.
688  */
689 
690 void
691 TExpandoMenuBar::CheckForSizeOverrun()
692 {
693 	BRect screenFrame = (BScreen(Window())).Frame();
694 	if (fVertical)
695 		fIsScrolling = Window()->Frame().bottom > screenFrame.bottom;
696 	else
697 		fIsScrolling = false;
698 }
699 
700 
701 void
702 TExpandoMenuBar::SizeWindow()
703 {
704 	if (fVertical)
705 		fBarView->SizeWindow(BScreen(Window()).Frame());
706 	else
707 		CheckItemSizes(1);
708 }
709 
710 
711 int32
712 TExpandoMenuBar::monitor_team_windows(void *arg)
713 {
714 	TExpandoMenuBar *teamMenu = (TExpandoMenuBar *)arg;
715 
716 	while (teamMenu->sDoMonitor) {
717 		sMonLocker.Lock();
718 
719 		if (teamMenu->Window()->LockWithTimeout(50000) == B_OK) {
720 			int32 totalItems = teamMenu->CountItems();
721 
722 			// Set all WindowMenuItems to require an update.
723 			TWindowMenuItem *item = NULL;
724 			for (int32 i = 0; i < totalItems; i++) {
725 				if (!teamMenu->SubmenuAt(i)){
726 					item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i));
727 					item->SetRequireUpdate();
728 				}
729 			}
730 
731 			// Perform SetTo() on all the items that still exist as well as add new items.
732 			bool itemModified = false, resize = false;
733 			TTeamMenuItem *teamItem = NULL;
734 
735 			for (int32 i = 0; i < totalItems; i++) {
736 				if (teamMenu->SubmenuAt(i) == NULL)
737 					continue;
738 
739 				teamItem = static_cast<TTeamMenuItem *>(teamMenu->ItemAt(i));
740 				if (teamItem->IsExpanded()) {
741 					int32 teamCount = teamItem->Teams()->CountItems();
742 					for (int32 j = 0; j < teamCount; j++) {
743 						// The following code is almost a copy/paste from
744 						// WindowMenu.cpp
745 						team_id	theTeam = (team_id)teamItem->Teams()->ItemAt(j);
746 						int32 count = 0;
747 						int32 *tokens = get_token_list(theTeam, &count);
748 
749 						for (int32 k = 0; k < count; k++) {
750 							window_info *wInfo = get_window_info(tokens[k]);
751 							if (wInfo == NULL)
752 								continue;
753 
754 							if (TWindowMenu::WindowShouldBeListed(wInfo->w_type)
755 								&& (wInfo->show_hide_level <= 0 || wInfo->is_mini)) {
756 								// Check if we have a matching window item...
757 								item = teamItem->ExpandedWindowItem(wInfo->id);
758 								if (item) {
759 									item->SetTo(wInfo->name, wInfo->id, wInfo->is_mini,
760 										((1 << current_workspace()) & wInfo->workspaces) != 0);
761 
762 									if (strcmp(wInfo->name, item->Label()) != 0)
763 										item->SetLabel(wInfo->name);
764 
765 									if (item->ChangedState())
766 										itemModified = true;
767 								} else if (teamItem->IsExpanded()) {
768 									// Add the item
769 									item = new TWindowMenuItem(wInfo->name, wInfo->id,
770 										wInfo->is_mini,
771 										((1 << current_workspace()) & wInfo->workspaces) != 0,
772 										false);
773 									item->ExpandedItem(true);
774 									teamMenu->AddItem(item, i + 1);
775 									resize = true;
776 								}
777 							}
778 							free(wInfo);
779 						}
780 						free(tokens);
781 					}
782 				}
783 			}
784 
785 			// Remove any remaining items which require an update.
786 			for (int32 i = 0; i < totalItems; i++) {
787 				if (!teamMenu->SubmenuAt(i)){
788 					item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i));
789 					if (item && item->RequiresUpdate()) {
790 						item = static_cast<TWindowMenuItem *>(teamMenu->RemoveItem(i));
791 						delete item;
792 						totalItems--;
793 
794 						resize = true;
795 					}
796 				}
797 			}
798 
799 			// If any of the WindowMenuItems changed state, we need to force a repaint.
800 			if (itemModified || resize) {
801 				teamMenu->Invalidate();
802 				if (resize)
803 					teamMenu->SizeWindow();
804 			}
805 
806 			teamMenu->Window()->Unlock();
807 		}
808 
809 		sMonLocker.Unlock();
810 
811 		// sleep for a bit...
812 		snooze(150000);
813 	}
814 	return B_OK;
815 }
816 
817