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