xref: /haiku/src/apps/deskbar/ExpandoMenuBar.cpp (revision be3db2942c0e8dda63cdd226ec3c99309d3eab0c)
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 	RemoveItems(0, CountItems(), true);
195 }
196 
197 
198 void
199 TExpandoMenuBar::MessageReceived(BMessage *message)
200 {
201 	int32 index;
202 	TTeamMenuItem *item;
203 
204 	switch (message->what) {
205 		case B_SOME_APP_LAUNCHED: {
206 			BList *teams = NULL;
207 			message->FindPointer("teams", (void **)&teams);
208 
209 			BBitmap *icon = NULL;
210 			message->FindPointer("icon", (void **)&icon);
211 
212 			const char *signature;
213 			if (message->FindString("sig", &signature) == B_OK
214 				&&strcasecmp(signature, kDeskbarSignature) == 0) {
215 				delete teams;
216 				delete icon;
217 				break;
218 			}
219 
220 			uint32 flags;
221 			if (message->FindInt32("flags", ((int32*) &flags)) == B_OK
222 				&& (flags & B_BACKGROUND_APP) != 0) {
223 				delete teams;
224 				delete icon;
225 				break;
226 			}
227 
228 			const char *name = NULL;
229 			message->FindString("name", &name);
230 
231 			AddTeam(teams, icon, strdup(name), strdup(signature));
232 			break;
233 		}
234 
235 		case msg_AddTeam:
236 			AddTeam(message->FindInt32("team"), message->FindString("sig"));
237 			break;
238 
239 		case msg_RemoveTeam:
240 		{
241 			team_id team = -1;
242 			message->FindInt32("team", &team);
243 
244 			RemoveTeam(team, true);
245 			break;
246 		}
247 
248 		case B_SOME_APP_QUIT:
249 		{
250 			team_id team = -1;
251 			message->FindInt32("team", &team);
252 
253 			RemoveTeam(team, false);
254 			break;
255 		}
256 
257 		case M_MINIMIZE_TEAM:
258 		{
259 			index = message->FindInt32("itemIndex");
260 			item = dynamic_cast<TTeamMenuItem *>(ItemAt(index));
261 			if (item == NULL)
262 				break;
263 
264 			TShowHideMenuItem::TeamShowHideCommon(B_MINIMIZE_WINDOW,
265 				item->Teams(),
266 				item->Menu()->ConvertToScreen(item->Frame()),
267 				true);
268 			break;
269 		}
270 
271 		case M_BRING_TEAM_TO_FRONT:
272 		{
273 			index = message->FindInt32("itemIndex");
274 			item = dynamic_cast<TTeamMenuItem *>(ItemAt(index));
275 			if (item == NULL)
276 				break;
277 
278 			TShowHideMenuItem::TeamShowHideCommon(B_BRING_TO_FRONT,
279 				item->Teams(), item->Menu()->ConvertToScreen(item->Frame()),
280 				true);
281 			break;
282 		}
283 
284 		default:
285 			BMenuBar::MessageReceived(message);
286 			break;
287 	}
288 }
289 
290 
291 void
292 TExpandoMenuBar::MouseDown(BPoint where)
293 {
294 	BMessage *message = Window()->CurrentMessage();
295 
296 	// check for three finger salute, a.k.a. Vulcan Death Grip
297 	if (message != NULL) {
298 		int32 modifiers = 0;
299 		message->FindInt32("modifiers", &modifiers);
300 
301 		if ((modifiers & B_COMMAND_KEY) != 0
302 			&& (modifiers & B_OPTION_KEY) != 0
303 			&& (modifiers & B_SHIFT_KEY) != 0
304 			&& !fBarView->Dragging()) {
305 			TTeamMenuItem *item = TeamItemAtPoint(where);
306 
307 			if (item != NULL) {
308 				const BList	*teams = item->Teams();
309 				int32 teamCount = teams->CountItems();
310 
311 				team_id teamID;
312 				for (int32 team = 0; team < teamCount; team++) {
313 					teamID = (team_id)teams->ItemAt(team);
314 					kill_team(teamID);
315 					//	remove the team immediately
316 					//	from display
317 					RemoveTeam(teamID, false);
318 				}
319 
320 				return;
321 			}
322 		}
323 	}
324 
325 // This feature is broken because the menu bar never receives
326 // the second click
327 #ifdef DOUBLECLICKBRINGSTOFRONT
328 	// doubleclick on an item brings all to front
329 	for (int32 i = fFirstApp; i < count; i++) {
330 		TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(i);
331 		if (item->Frame().Contains(where)) {
332 			bigtime_t clickSpeed = 0;
333 			get_click_speed(&clickSpeed);
334 			if ( (fLastClickItem == i) &&
335 				 (clickSpeed > (system_time() - fLastClickTime)) ) {
336 				// bring this team's window to the front
337 				BMessage showMessage(M_BRING_TEAM_TO_FRONT);
338 				showMessage.AddInt32("itemIndex", i);
339 				Window()->PostMessage(&showMessage, this);
340 				return;
341 			}
342 
343 			fLastClickItem = i;
344 			fLastClickTime = system_time();
345 			break;
346 		}
347 	}
348 #endif
349 
350 	// control click - show all/hide all shortcut
351 	if (message != NULL) {
352 		int32 modifiers = 0;
353 		message->FindInt32("modifiers", &modifiers);
354 		if ((modifiers & B_CONTROL_KEY) != 0
355 			&& ! fBarView->Dragging()) {
356 			TTeamMenuItem *item = TeamItemAtPoint(where);
357 			if (item != NULL) {
358 				// show/hide item's teams
359 				BMessage showMessage((modifiers & B_SHIFT_KEY) != 0
360 					? M_MINIMIZE_TEAM : M_BRING_TEAM_TO_FRONT);
361 				showMessage.AddInt32("itemIndex", IndexOf(item));
362 				Window()->PostMessage(&showMessage, this);
363 				return;
364 			}
365 		}
366 	}
367 
368 	// Check the bounds of the expand Team icon
369 	if (fShowTeamExpander && fVertical && !fBarView->Dragging()) {
370 		TTeamMenuItem *item = TeamItemAtPoint(where);
371 		if (item != NULL) {
372 			BRect expanderRect = item->ExpanderBounds();
373 			if (expanderRect.Contains(where)) {
374 				// Let the update thread wait...
375 				BAutolock locker(sMonLocker);
376 
377 				// Toggle the item
378 				item->ToggleExpandState(true);
379 				item->Draw();
380 
381 				// Absorb the message.
382 				return;
383 			}
384 		}
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) {
395 		//	force a cleanup
396 		fBarView->DragStop(true);
397 		BMenuBar::MouseMoved(where, code, message);
398 		return;
399 	}
400 
401 	uint32 buttons;
402 	if (!(Window()->CurrentMessage())
403 		|| Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons) < B_OK)
404 		buttons = 0;
405 
406 	switch (code) {
407 		case B_ENTERED_VIEW:
408 			if (message && buttons != 0) {
409 				fBarView->CacheDragData(message);
410 				MouseDown(where);
411 			}
412 			break;
413 
414 		case B_EXITED_VIEW:
415 			if (fBarView->Dragging() && buttons != 0) {
416 				if (!TeamItemAtPoint(where)
417 					&& !InBeMenu(where)
418 					&& (fSeparatorItem && !fSeparatorItem->Frame().Contains(where))
419 					&& !Frame().Contains(where)) {
420 					fBarView->DragStop();
421 				}
422 			}
423 			break;
424 	}
425 	BMenuBar::MouseMoved(where, code, message);
426 }
427 
428 
429 bool
430 TExpandoMenuBar::InBeMenu(BPoint loc) const
431 {
432 	if (!fVertical) {
433 		if (fBeMenuItem && fBeMenuItem->Frame().Contains(loc))
434 			return true;
435 	} else {
436 		TBarWindow *window = dynamic_cast<TBarWindow*>(Window());
437 		if (window) {
438 			if (TBeMenu *bemenu = window->BeMenu()) {
439 				bool inBeMenu = false;
440 				if (bemenu->LockLooper()) {
441 					inBeMenu = bemenu->Frame().Contains(loc);
442 					bemenu->UnlockLooper();
443 				}
444 				return inBeMenu;
445 			}
446 		}
447 	}
448 
449 	return false;
450 }
451 
452 
453 /**	Returns the team menu item that belongs to the item under the
454  *	specified \a point.
455  *	If \a _item is given, it will return the exact menu item under
456  *	that point (which might be a window item when the expander is on).
457  */
458 
459 TTeamMenuItem *
460 TExpandoMenuBar::TeamItemAtPoint(BPoint point, BMenuItem **_item)
461 {
462 	TTeamMenuItem *lastApp = NULL;
463 	int32 count = CountItems();
464 
465 	for (int32 i = fFirstApp; i < count; i++) {
466 		BMenuItem *item = ItemAt(i);
467 
468 		if (dynamic_cast<TTeamMenuItem *>(item) != NULL)
469 			lastApp = (TTeamMenuItem *)item;
470 
471 		if (item && item->Frame().Contains(point)) {
472 			if (_item != NULL)
473 				*_item = item;
474 
475 			return lastApp;
476 		}
477 	}
478 
479 	// no item found
480 
481 	if (_item != NULL)
482 		*_item = NULL;
483 
484 	return NULL;
485 }
486 
487 
488 void
489 TExpandoMenuBar::AddTeam(BList *team, BBitmap *icon, char *name, char *signature)
490 {
491 	float itemWidth = fVertical ? Frame().Width() : kMinimumWindowWidth;
492 	float itemHeight = -1.0f;
493 
494 	desk_settings *settings = ((TBarApp *)be_app)->Settings();
495 	TTeamMenuItem *item = new TTeamMenuItem(team, icon, name, signature, itemWidth,
496 		itemHeight, fDrawLabel, fVertical);
497 
498 	if (settings->trackerAlwaysFirst && !strcmp(signature, kTrackerSignature)) {
499 		AddItem(item, fFirstApp);
500 	} else if (settings->sortRunningApps) {
501 		TTeamMenuItem *teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(fFirstApp));
502 		int32 firstApp = fFirstApp;
503 
504 		// if Tracker should always be the first item, we need to skip it
505 		// when sorting in the current item
506 		if (settings->trackerAlwaysFirst && teamItem != NULL
507 			&& !strcmp(teamItem->Signature(), kTrackerSignature)) {
508 			firstApp++;
509 		}
510 
511 		int32 count = CountItems(), i;
512 		for (i = firstApp; i < count; i++) {
513 			teamItem = dynamic_cast<TTeamMenuItem *>(ItemAt(i));
514 			if (teamItem != NULL && strcasecmp(teamItem->Name(), name) > 0) {
515 				AddItem(item, i);
516 				break;
517 			}
518 		}
519 		// was the item added to the list yet?
520 		if (i == count)
521 			AddItem(item);
522 	} else
523 		AddItem(item);
524 
525 	if (fVertical) {
526 		if (item && fShowTeamExpander && fExpandNewTeams)
527 			item->ToggleExpandState(false);
528 
529 		fBarView->SizeWindow(BScreen(Window()).Frame());
530 	} else
531 		CheckItemSizes(1);
532 
533 	Window()->UpdateIfNeeded();
534 }
535 
536 
537 void
538 TExpandoMenuBar::AddTeam(team_id team, const char *signature)
539 {
540 	int32 count = CountItems();
541 	for (int32 i = fFirstApp; i < count; i++) {
542 		// Only add to team menu items
543 		if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) {
544 			if (strcasecmp(item->Signature(), signature) == 0) {
545 				if (!(item->Teams()->HasItem((void *)team)))
546 					item->Teams()->AddItem((void *)team);
547 
548 				break;
549 			}
550 		}
551 	}
552 }
553 
554 
555 void
556 TExpandoMenuBar::RemoveTeam(team_id team, bool partial)
557 {
558 	int32 count = CountItems();
559 	for (int32 i = fFirstApp; i < count; i++) {
560 		if (TTeamMenuItem *item = dynamic_cast<TTeamMenuItem *>(ItemAt(i))) {
561 			if (item->Teams()->HasItem((void *)team)) {
562 				item->Teams()->RemoveItem(team);
563 
564 				if (partial)
565 					return;
566 
567 #ifdef DOUBLECLICKBRINGSTOFRONT
568 				if (fLastClickItem == i)
569 					fLastClickItem = -1;
570 #endif
571 
572 				RemoveItem(i);
573 
574 				if (fVertical) {
575 					//	instead of resizing the window here and there in the code
576 					//	the resize method will be centered in one place
577 					//	thus, the same behavior (good or bad) will be used whereever
578 					//	window sizing is done
579 					fBarView->SizeWindow(BScreen(Window()).Frame());
580 				} else
581 					CheckItemSizes(-1);
582 
583 				Window()->UpdateIfNeeded();
584 
585 				delete item;
586 				return;
587 			}
588 		}
589 	}
590 }
591 
592 
593 void
594 TExpandoMenuBar::CheckItemSizes(int32 delta)
595 {
596 	float width = Frame().Width();
597 	int32 count = CountItems();
598 	bool reset = false;
599 	float newWidth = 0;
600 	float fullWidth = (kMinimumWindowWidth * count);
601 
602 	if (!fBarView->Vertical()) {
603 		// in this case there are 2 extra items:
604 		//		The Be Menu
605 		//		The little separator item
606 		fullWidth = fullWidth - (kMinimumWindowWidth * 2) + (kBeMenuWidth + kSepItemWidth);
607 		width -= (kBeMenuWidth + kSepItemWidth);
608 		count -= 2;
609 	}
610 
611 	if (delta >= 0 && fullWidth > width) {
612 		fOverflow = true;
613 		reset = true;
614 		newWidth = floorf(width/count);
615 	} else if (delta < 0 && fOverflow) {
616 		reset = true;
617 		if (fullWidth > width)
618 			newWidth = floorf(width/count);
619 		else
620 			newWidth = kMinimumWindowWidth;
621 	}
622 	if (newWidth > kMinimumWindowWidth)
623 		newWidth = kMinimumWindowWidth;
624 
625 	if (reset) {
626 		SetMaxContentWidth(newWidth);
627 		if (newWidth == kMinimumWindowWidth)
628 			fOverflow = false;
629 		InvalidateLayout();
630 
631 		for (int32 index = fFirstApp; ; index++) {
632 			TTeamMenuItem *item = (TTeamMenuItem *)ItemAt(index);
633 			if (!item)
634 				break;
635 			item->SetOverrideWidth(newWidth);
636 		}
637 
638 		Invalidate();
639 		Window()->UpdateIfNeeded();
640 	}
641 }
642 
643 
644 menu_layout
645 TExpandoMenuBar::MenuLayout() const
646 {
647 	return Layout();
648 }
649 
650 
651 void
652 TExpandoMenuBar::Draw(BRect update)
653 {
654 	BMenu::Draw(update);
655 }
656 
657 
658 void
659 TExpandoMenuBar::DrawBackground(BRect)
660 {
661 	BRect bounds(Bounds());
662 	rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
663 	rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
664 	rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT);
665 	rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT);
666 	int32 last = CountItems() - 1;
667 	float start;
668 
669 	if (last >= 0)
670 		start = ItemAt(last)->Frame().right + 1;
671 	else
672 		start = 0;
673 
674 	if (!fVertical) {
675 		SetHighColor(vlight);
676 		StrokeLine(BPoint(start, bounds.top+1), bounds.RightTop() + BPoint(0,1));
677 		StrokeLine(BPoint(start, bounds.top+1), BPoint(start, bounds.bottom));
678 		SetHighColor(hilite);
679 		StrokeLine(BPoint(start+1, bounds.bottom), bounds.RightBottom());
680 	}
681 }
682 
683 
684 /**	Something to help determine if we are showing too many apps
685  *	need to add in scrolling functionality.
686  */
687 
688 void
689 TExpandoMenuBar::CheckForSizeOverrun()
690 {
691 	BRect screenFrame = (BScreen(Window())).Frame();
692 	if (fVertical)
693 		fIsScrolling = Window()->Frame().bottom > screenFrame.bottom;
694 	else
695 		fIsScrolling = false;
696 }
697 
698 
699 void
700 TExpandoMenuBar::SizeWindow()
701 {
702 	if (fVertical)
703 		fBarView->SizeWindow(BScreen(Window()).Frame());
704 	else
705 		CheckItemSizes(1);
706 }
707 
708 
709 int32
710 TExpandoMenuBar::monitor_team_windows(void *arg)
711 {
712 	TExpandoMenuBar *teamMenu = (TExpandoMenuBar *)arg;
713 
714 	while (teamMenu->sDoMonitor) {
715 		sMonLocker.Lock();
716 
717 		if (teamMenu->Window()->LockWithTimeout(50000) == B_OK) {
718 			int32 totalItems = teamMenu->CountItems();
719 
720 			// Set all WindowMenuItems to require an update.
721 			TWindowMenuItem *item = NULL;
722 			for (int32 i = 0; i < totalItems; i++) {
723 				if (!teamMenu->SubmenuAt(i)){
724 					item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i));
725 					item->SetRequireUpdate();
726 				}
727 			}
728 
729 			// Perform SetTo() on all the items that still exist as well as add new items.
730 			bool itemModified = false, resize = false;
731 			TTeamMenuItem *teamItem = NULL;
732 
733 			for (int32 i = 0; i < totalItems; i++) {
734 				if (teamMenu->SubmenuAt(i) == NULL)
735 					continue;
736 
737 				teamItem = static_cast<TTeamMenuItem *>(teamMenu->ItemAt(i));
738 				if (teamItem->IsExpanded()) {
739 					int32 teamCount = teamItem->Teams()->CountItems();
740 					for (int32 j = 0; j < teamCount; j++) {
741 						// The following code is almost a copy/paste from
742 						// WindowMenu.cpp
743 						team_id	theTeam = (team_id)teamItem->Teams()->ItemAt(j);
744 						int32 count = 0;
745 						int32 *tokens = get_token_list(theTeam, &count);
746 
747 						for (int32 k = 0; k < count; k++) {
748 							window_info *wInfo = get_window_info(tokens[k]);
749 							if (wInfo == NULL)
750 								continue;
751 
752 							if (TWindowMenu::WindowShouldBeListed(wInfo->w_type)
753 								&& (wInfo->show_hide_level <= 0 || wInfo->is_mini)) {
754 								// Check if we have a matching window item...
755 								item = teamItem->ExpandedWindowItem(wInfo->id);
756 								if (item) {
757 									item->SetTo(wInfo->name, wInfo->id, wInfo->is_mini,
758 										((1 << current_workspace()) & wInfo->workspaces) != 0);
759 
760 									if (strcmp(wInfo->name, item->Label()) != 0)
761 										item->SetLabel(wInfo->name);
762 
763 									if (item->ChangedState())
764 										itemModified = true;
765 								} else if (teamItem->IsExpanded()) {
766 									// Add the item
767 									item = new TWindowMenuItem(wInfo->name, wInfo->id,
768 										wInfo->is_mini,
769 										((1 << current_workspace()) & wInfo->workspaces) != 0,
770 										false);
771 									item->ExpandedItem(true);
772 									teamMenu->AddItem(item, i + 1);
773 									resize = true;
774 								}
775 							}
776 							free(wInfo);
777 						}
778 						free(tokens);
779 					}
780 				}
781 			}
782 
783 			// Remove any remaining items which require an update.
784 			for (int32 i = 0; i < totalItems; i++) {
785 				if (!teamMenu->SubmenuAt(i)){
786 					item = static_cast<TWindowMenuItem *>(teamMenu->ItemAt(i));
787 					if (item && item->RequiresUpdate()) {
788 						item = static_cast<TWindowMenuItem *>(teamMenu->RemoveItem(i));
789 						delete item;
790 						totalItems--;
791 
792 						resize = true;
793 					}
794 				}
795 			}
796 
797 			// If any of the WindowMenuItems changed state, we need to force a repaint.
798 			if (itemModified || resize) {
799 				teamMenu->Invalidate();
800 				if (resize)
801 					teamMenu->SizeWindow();
802 			}
803 
804 			teamMenu->Window()->Unlock();
805 		}
806 
807 		sMonLocker.Unlock();
808 
809 		// sleep for a bit...
810 		snooze(150000);
811 	}
812 	return B_OK;
813 }
814 
815