xref: /haiku/src/apps/deskbar/Switcher.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
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 
36 #include "tracker_private.h"
37 #include "BarApp.h"
38 #include "Switcher.h"
39 #include "ResourceSet.h"
40 #include "WindowMenuItem.h"
41 #include "icons.h"
42 
43 #include <Bitmap.h>
44 #include <Debug.h>
45 #include <Font.h>
46 #include <Mime.h>
47 #include <Node.h>
48 #include <NodeInfo.h>
49 #include <Roster.h>
50 #include <Screen.h>
51 #include <String.h>
52 
53 #include <string.h>
54 #include <stdlib.h>
55 #include <float.h>
56 
57 
58 #define _ALLOW_STICKY_ 0
59 	// allows you to press 's' to keep the switcher window on screen
60 
61 #if __HAIKU__
62 	static const color_space kIconFormat = B_RGBA32;
63 #else
64 	static const color_space kIconFormat = B_CMAP8;
65 #endif
66 
67 
68 class TTeamGroup {
69 	public:
70 		TTeamGroup();
71 		TTeamGroup(BList *teams, uint32 flags, char *name, const char *sig);
72 		virtual ~TTeamGroup();
73 
74 		void Draw(BView *, BRect bounds, bool main);
75 
76 		BList *TeamList() const
77 			{ return fTeams; }
78 		const char *Name() const
79 			{ return fName; }
80 		const char *Signature() const
81 			{ return fSignature; }
82 		uint32 Flags() const
83 			{ return fFlags; }
84 		const BBitmap *SmallIcon() const
85 			{ return fSmallIcon; }
86 		const BBitmap *LargeIcon() const
87 			{ return fLargeIcon; }
88 
89 	private:
90 		BList *fTeams;
91 		uint32 fFlags;
92 		char fSignature[B_MIME_TYPE_LENGTH];
93 		char *fName;
94 		BBitmap	*fSmallIcon;
95 		BBitmap	*fLargeIcon;
96 };
97 
98 class TSwitcherWindow : public BWindow {
99 	public:
100 		TSwitcherWindow(BRect frame, TSwitchManager *mgr);
101 		virtual	~TSwitcherWindow();
102 
103 		virtual	bool QuitRequested();
104 		virtual void MessageReceived(BMessage *message);
105 		virtual	void Show();
106 		virtual	void Hide();
107 		virtual void WindowActivated(bool state);
108 
109 		void DoKey(uint32 key, uint32 mods);
110 		TIconView *IconView();
111 		TWindowView *WindowView();
112 		TBox *TopView();
113 		bool HairTrigger();
114 		void Update(int32 previous, int32 current, int32 prevSlot,
115 			int32 currentSlot, bool forward);
116 		int32 SlotOf(int32);
117 		void Redraw(int32 index);
118 
119 	private:
120 		TSwitchManager *fManager;
121 		TIconView *fIconView;
122 		TBox *fTopView;
123 		TWindowView *fWindowView;
124 		bool fHairTrigger;
125 		bool fSkipKeyRepeats;
126 };
127 
128 class TWindowView : public BView {
129 	public:
130 		TWindowView(BRect frame, TSwitchManager *manager, TSwitcherWindow *switcher);
131 
132 		void UpdateGroup(int32 groupIndex, int32 windowIndex);
133 
134 		virtual void AttachedToWindow();
135 		virtual	void Draw(BRect update);
136 		virtual	void Pulse();
137 		virtual	void GetPreferredSize(float *w, float *h);
138 		void ScrollTo(float x, float y)
139 			{ ScrollTo(BPoint(x,y)); }
140 		virtual	void ScrollTo(BPoint where);
141 
142 		void ShowIndex(int32 windex);
143 		BRect FrameOf(int32 index) const;
144 
145 	private:
146 		int32 fCurrentToken;
147 		float fItemHeight;
148 		TSwitcherWindow	*fSwitcher;
149 		TSwitchManager *fManager;
150 		bool fLocal;
151 };
152 
153 class TIconView : public BView {
154 	public:
155 		TIconView(BRect frame, TSwitchManager *manager, TSwitcherWindow *switcher);
156 		~TIconView();
157 
158 		void Showing();
159 		void Hiding();
160 
161 		virtual void KeyDown(const char *bytes, int32 numBytes);
162 		virtual	void Pulse();
163 		virtual	void MouseDown(BPoint );
164 		virtual	void Draw(BRect );
165 
166 		void ScrollTo(float x, float y)
167 			{ ScrollTo(BPoint(x,y)); }
168 		virtual	void ScrollTo(BPoint where);
169 		void Update(int32 previous, int32 current, int32 previousSlot,
170 			int32 currentSlot, bool forward);
171 		void DrawTeams(BRect update);
172 		int32 SlotOf(int32) const;
173 		BRect FrameOf(int32) const;
174 		int32 ItemAtPoint(BPoint) const;
175 		int32 IndexAt(int32 slot) const;
176 		void CenterOn(int32 index);
177 
178 	private:
179 		void CacheIcons(TTeamGroup *);
180 		void AnimateIcon(BBitmap *startIcon, BBitmap *endIcon);
181 
182 		bool fAutoScrolling;
183 		bool fCapsState;
184 		TSwitcherWindow	*fSwitcher;
185 		TSwitchManager *fManager;
186 		BBitmap	*fOffBitmap;
187 		BView *fOffView;
188 		BBitmap	*fCurrentSmall;
189 		BBitmap	*fCurrentLarge;
190 };
191 
192 class TBox : public BBox {
193 	public:
194 		TBox(BRect bounds, TSwitchManager *manager, TSwitcherWindow *, TIconView *iconView);
195 
196 		virtual void Draw(BRect update);
197 		virtual void AllAttached();
198 		virtual	void DrawIconScrollers(bool force);
199 		virtual	void DrawWindowScrollers(bool force);
200 		virtual	void MouseDown(BPoint where);
201 
202 	private:
203 		TSwitchManager *fManager;
204 		TSwitcherWindow	*fWindow;
205 		TIconView *fIconView;
206 		BRect fCenter;
207 		bool fLeftScroller;
208 		bool fRightScroller;
209 		bool fUpScroller;
210 		bool fDownScroller;
211 };
212 
213 
214 const int32 kHorizontalMargin = 11;
215 const int32 kVerticalMargin = 10;
216 
217 // SLOT_SIZE must be divisible by 4. That's because of the scrolling
218 // animation. If this needs to change then look at TIconView::Update()
219 
220 const int32 kSlotSize = 36;
221 const int32 kScrollStep = kSlotSize / 2;
222 const int32 kNumSlots = 7;
223 const int32 kCenterSlot = 3;
224 
225 const int32 kWindowScrollSteps = 3;
226 
227 
228 //	#pragma mark -
229 
230 
231 static int32
232 LowBitIndex(uint32 value)
233 {
234 	int32 result = 0;
235 	int32 bitMask = 1;
236 
237 	if (value == 0)
238 		return -1;
239 
240 	while (result < 32 && (value & bitMask) == 0) {
241 		result++;
242 		bitMask = bitMask << 1;
243 	}
244 	return result;
245 }
246 
247 
248 inline bool
249 IsVisibleInCurrentWorkspace(const window_info *windowInfo)
250 {
251 	/*
252 	 The window list is always ordered from the top
253 	 front visible window (the first on the list), going down through all
254 	 the other visible windows, then all the hidden or non workspace
255 	 visible window at the end.
256 
257 	 layer > 2 : normal visible window.
258 	 layer == 2 : reserved for the desktop window (visible also).
259 	 layer < 2 : hidden (0) and non workspace visible window (1)
260 	*/
261 	return windowInfo->layer > 2;
262 }
263 
264 
265 bool
266 IsKeyDown(int32 key)
267 {
268 	key_info keyInfo;
269 
270 	get_key_info(&keyInfo);
271 	return (keyInfo.key_states[key >> 3] & (1 << ((7 - key) & 7))) != 0;
272 }
273 
274 
275 bool
276 IsWindowOK(const window_info *windowInfo)
277 {
278 	// is_mini (true means that the window is minimized).
279 	// if not, then
280 	// show_hide >= 1 means that the window is hidden.
281 	//
282 	// If the window is both minimized and hidden, then you get :
283 	//	 TWindow->is_mini = false;
284 	//	 TWindow->was_mini = true;
285 	//	 TWindow->show_hide >= 1;
286 
287 	if (windowInfo->feel != _STD_W_TYPE_)
288 		return false;
289 
290 	if (windowInfo->is_mini)
291 		return true;
292 
293 	return windowInfo->show_hide_level <= 0;
294 }
295 
296 
297 bool
298 OKToUse(const TTeamGroup *teamGroup)
299 {
300 	if (!teamGroup)
301 		return false;
302 
303 	// skip background applications
304 	if ((teamGroup->Flags() & B_BACKGROUND_APP) != 0)
305 		return false;
306 
307 	// skip the Deakbar itself
308 	if (strcasecmp(teamGroup->Signature(), kDeskbarSignature) == 0)
309 		return false;
310 
311 	return true;
312 }
313 
314 
315 int
316 SmartStrcmp(const char *s1, const char *s2)
317 {
318 	if (strcasecmp(s1, s2) == 0)
319 		return 0;
320 
321 	// if the strings on differ in spaces or underscores they still match
322 	while (*s1 && *s2) {
323 		if ((*s1 == ' ') || (*s1 == '_')) {
324 			s1++;
325 			continue;
326 		}
327 		if ((*s2 == ' ') || (*s2 == '_')) {
328 			s2++;
329 			continue;
330 		}
331 		if (*s1 != *s2)
332 			return 1;		// they differ
333 		s1++;
334 		s2++;
335 	}
336 
337 	// if one of the strings ended before the other
338 	// ??? could process trailing spaces & underscores!
339 	if (*s1)
340 		return 1;
341 	if (*s2)
342 		return 1;
343 
344 	return 0;
345 }
346 
347 
348 //	#pragma mark -
349 
350 
351 TTeamGroup::TTeamGroup()
352 	:
353 	fTeams(NULL),
354 	fFlags(0),
355 	fName(NULL),
356 	fSmallIcon(NULL),
357 	fLargeIcon(NULL)
358 {
359 	fSignature[0] = '\0';
360 }
361 
362 
363 TTeamGroup::TTeamGroup(BList *teams, uint32 flags, char *name,
364 	const char *signature)
365 	:
366 	fTeams(teams),
367 	fFlags(flags),
368 	fName(name),
369 	fSmallIcon(NULL),
370 	fLargeIcon(NULL)
371 {
372 	strcpy(fSignature, signature);
373 
374 	fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), kIconFormat);
375 	fLargeIcon = new BBitmap(BRect(0, 0, 31, 31), kIconFormat);
376 
377 	app_info appInfo;
378 	if (be_roster->GetAppInfo(signature, &appInfo) == B_OK) {
379 		BNode node(&(appInfo.ref));
380 		if (node.InitCheck() == B_OK) {
381 			BNodeInfo nodeInfo(&node);
382 			if (nodeInfo.InitCheck() == B_OK) {
383 				nodeInfo.GetTrackerIcon(fSmallIcon, B_MINI_ICON);
384 				nodeInfo.GetTrackerIcon(fLargeIcon, B_LARGE_ICON);
385 			}
386 		}
387 	}
388 }
389 
390 
391 TTeamGroup::~TTeamGroup()
392 {
393 	delete fTeams;
394 	free(fName);
395 	delete fSmallIcon;
396 	delete fLargeIcon;
397 }
398 
399 
400 void
401 TTeamGroup::Draw(BView *view, BRect bounds, bool main)
402 {
403 	BRect rect;
404 	if (main) {
405 		rect = fLargeIcon->Bounds();
406 		rect.OffsetTo(bounds.LeftTop());
407 		rect.OffsetBy(2, 2);
408 		view->DrawBitmap(fLargeIcon, rect);
409 	} else {
410 		rect = fSmallIcon->Bounds();
411 		rect.OffsetTo(bounds.LeftTop());
412 		rect.OffsetBy(10, 10);
413 		view->DrawBitmap(fSmallIcon, rect);
414 	}
415 }
416 
417 
418 //	#pragma mark -
419 
420 
421 TSwitchManager::TSwitchManager(BPoint point)
422 	: BHandler("SwitchManager"),
423 	fMainMonitor(create_sem(1, "main_monitor")),
424 	fBlock(false),
425 	fSkipUntil(0),
426 	fLastSwitch(0),
427 	fGroupList(10),
428 	fCurrentIndex(0),
429 	fCurrentSlot(0),
430 	fWindowID(-1)
431 {
432 	BRect rect(point.x, point.y,
433 		point.x + (kSlotSize * kNumSlots) - 1 + (2 * kHorizontalMargin),
434 		point.y + 82);
435 	fWindow = new TSwitcherWindow(rect, this);
436 	fWindow->AddHandler(this);
437 
438 	fWindow->Lock();
439 	fWindow->Run();
440 
441 	BList tmpList;
442 	TBarApp::Subscribe(BMessenger(this), &tmpList);
443 
444 	for (int32 i = 0; ; i++) {
445 		BarTeamInfo	*barTeamInfo = (BarTeamInfo	*)tmpList.ItemAt(i);
446 		if (!barTeamInfo)
447 			break;
448 
449 		TTeamGroup *tinfo = new TTeamGroup(barTeamInfo->teams, barTeamInfo->flags,
450 			barTeamInfo->name, barTeamInfo->sig);
451 		fGroupList.AddItem(tinfo);
452 
453 		barTeamInfo->teams = NULL;
454 		barTeamInfo->name = NULL;
455 
456 		delete barTeamInfo;
457 	}
458 	fWindow->Unlock();
459 }
460 
461 
462 TSwitchManager::~TSwitchManager()
463 {
464 	for (int32 i = fGroupList.CountItems(); i-- > 0;) {
465 		TTeamGroup *teamInfo = static_cast<TTeamGroup *>(fGroupList.ItemAt(i));
466 		delete teamInfo;
467 	}
468 }
469 
470 
471 void
472 TSwitchManager::MessageReceived(BMessage *message)
473 {
474 	switch (message->what) {
475 		case B_SOME_APP_QUIT:
476 		{
477 			// This is only sent when last team of a matching set quits
478 			team_id teamID;
479 			int i = 0;
480 			TTeamGroup *tinfo;
481 			message->FindInt32("team", &teamID);
482 			while ((tinfo = (TTeamGroup *) fGroupList.ItemAt(i)) != NULL) {
483 				if (tinfo->TeamList()->HasItem((void *)teamID)) {
484 					fGroupList.RemoveItem(i);
485 
486 					if (OKToUse(tinfo)) {
487 						fWindow->Redraw(i);
488 						if (i <= fCurrentIndex) {
489 							fCurrentIndex--;
490 							CycleApp(true);
491 						}
492 					}
493 					delete tinfo;
494 					break;
495 				}
496 				i++;
497 			}
498 			break;
499 		}
500 
501 		case B_SOME_APP_LAUNCHED:
502 		{
503 			BList *teams;
504 			const char *name;
505 			BBitmap	*smallIcon;
506 			uint32 flags;
507 			const char *signature;
508 
509 			if (message->FindPointer("teams", (void **)&teams) != B_OK)
510 				break;
511 
512 			if (message->FindPointer("icon", (void **)&smallIcon) != B_OK) {
513 				delete teams;
514 				break;
515 			}
516 			delete smallIcon;
517 			if (message->FindString("sig", &signature) != B_OK) {
518 				delete teams;
519 				break;
520 			}
521 			if (message->FindInt32("flags", (int32 *)&flags) != B_OK) {
522 				delete teams;
523 				break;
524 			}
525 			if (message->FindString("name", &name) != B_OK) {
526 				delete teams;
527 				break;
528 			}
529 
530 			TTeamGroup *tinfo = new TTeamGroup(teams, flags, strdup(name),
531 				signature);
532 
533 			fGroupList.AddItem(tinfo);
534 			if (OKToUse(tinfo))
535 				fWindow->Redraw(fGroupList.CountItems() - 1);
536 
537 			break;
538 		}
539 
540 		case msg_AddTeam:
541 		{
542 			const char *signature = message->FindString("sig");
543 			team_id team = message->FindInt32("team");
544 
545 			int32 numItems = fGroupList.CountItems();
546 			for (int32 i = 0; i < numItems; i++) {
547 				TTeamGroup *tinfo = (TTeamGroup *)fGroupList.ItemAt(i);
548 				if (strcasecmp(tinfo->Signature(), signature) == 0) {
549 					if (!(tinfo->TeamList()->HasItem((void *)team)))
550 						tinfo->TeamList()->AddItem((void *)team);
551 					break;
552 				}
553 			}
554 			break;
555 		}
556 
557 		case msg_RemoveTeam:
558 		{
559 			team_id team = message->FindInt32("team");
560 
561 			int32 numItems = fGroupList.CountItems();
562 			for (int32 i = 0; i < numItems; i++) {
563 				TTeamGroup *tinfo = (TTeamGroup *)fGroupList.ItemAt(i);
564 				if (tinfo->TeamList()->HasItem((void *)team)) {
565 					tinfo->TeamList()->RemoveItem((void *)team);
566 					break;
567 				}
568 			}
569 			break;
570 		}
571 
572 		case 'TASK':
573 		{
574 			// The first TASK message calls MainEntry. Subsequent ones
575 			// call Process().
576 			bigtime_t time;
577 			message->FindInt64("when", (int64 *)&time);
578 
579 			// The fSkipUntil stuff can be removed once the new input_server
580 			// starts differentiating initial key_downs from KeyDowns generated
581 			// by auto-repeat. Until then the fSkipUntil stuff helps, but it
582 			// isn't perfect.
583 
584 			if (time < fSkipUntil)
585 				break;
586 
587 			status_t status = acquire_sem_etc(fMainMonitor, 1, B_TIMEOUT, 0);
588 			if (status != B_OK) {
589 				if (!fWindow->IsHidden() && !fBlock) {
590 					// Want to skip TASK msgs posted before the window
591 					// was made visible. Better UI feel if we do this.
592 					if (time > fSkipUntil) {
593 						uint32 modifiers;
594 						message->FindInt32("modifiers", (int32 *)&modifiers);
595 						Process((modifiers & B_SHIFT_KEY) == 0,
596 							(modifiers & B_OPTION_KEY) != 0);
597 					}
598 				}
599 			} else
600 				MainEntry(message);
601 
602 			break;
603 		}
604 
605 		default:
606 			break;
607 	}
608 }
609 
610 
611 #ifdef __HAIKU__
612 void
613 TSwitchManager::_SortApps()
614 {
615 	team_id* teams;
616 	int32 count;
617 	if (BPrivate::get_application_order(B_CURRENT_WORKSPACE, &teams, &count)
618 			!= B_OK)
619 		return;
620 
621 	BList groups;
622 	if (!groups.AddList(&fGroupList)) {
623 		free(teams);
624 		return;
625 	}
626 
627 	fGroupList.MakeEmpty();
628 
629 	for (int32 i = 0; i < count; i++) {
630 		// find team
631 		TTeamGroup* info = NULL;
632 		for (int32 j = 0; (info = (TTeamGroup*)groups.ItemAt(j)) != NULL; j++) {
633 			if (info->TeamList()->HasItem((void*)teams[i])) {
634 				groups.RemoveItem(j);
635 				break;
636 			}
637 		}
638 
639 		if (info != NULL)
640 			fGroupList.AddItem(info);
641 	}
642 
643 	fGroupList.AddList(&groups);
644 		// add the remaining entries
645 	free(teams);
646 }
647 
648 
649 status_t
650 TSwitchManager::_GetSortedWindowTokens(int32** _tokens, int32* _count)
651 {
652 	int32* ordered;
653 	int32 orderedCount;
654 	status_t status = BPrivate::get_window_order(B_CURRENT_WORKSPACE, &ordered,
655 		&orderedCount);
656 	if (status != B_OK)
657 		return status;
658 
659 	int32 tokenCount;
660 	int32* unsorted = get_token_list(-1, &tokenCount);
661 	if (unsorted == NULL) {
662 		free(ordered);
663 		return B_NO_MEMORY;
664 	}
665 
666 	int32* tokens = (int32*)malloc(tokenCount * sizeof(int32));
667 	if (tokens == NULL) {
668 		free(unsorted);
669 		free(ordered);
670 		return B_NO_MEMORY;
671 	}
672 
673 	memcpy(tokens, ordered, orderedCount * sizeof(int32));
674 	free(ordered);
675 	int32 count = orderedCount;
676 
677 	for (int32 i = 0; i < tokenCount; i++) {
678 		// find token in ordered list
679 		bool found = false;
680 		for (int32 j = 0; j < orderedCount; j++) {
681 			if (tokens[j] == unsorted[i]) {
682 				found = true;
683 				break;
684 			}
685 		}
686 
687 		if (!found)
688 			tokens[count++] = unsorted[i];
689 	}
690 
691 	free(unsorted);
692 
693 	*_tokens = tokens;
694 	*_count = count;
695 	return B_OK;
696 }
697 #endif	// __HAIKU__
698 
699 
700 void
701 TSwitchManager::MainEntry(BMessage *message)
702 {
703 	bigtime_t keyRepeatRate;
704 	get_key_repeat_delay(&keyRepeatRate);
705 
706 	if (keyRepeatRate < 200000)
707 		keyRepeatRate = 200000;
708 
709 	bigtime_t now = system_time();
710 	bigtime_t timeout = now + keyRepeatRate;
711 
712 	app_info appInfo;
713 	be_roster->GetActiveAppInfo(&appInfo);
714 
715 #ifdef __HAIKU__
716 	if (now > fLastSwitch + 400000)
717 		_SortApps();
718 
719 	fLastSwitch = now;
720 #endif
721 
722 	int32 index;
723 	fCurrentIndex = FindTeam(appInfo.team, &index) != NULL ? index : 0;
724 
725 	int32 key;
726 	message->FindInt32("key", (int32 *)&key);
727 
728 	uint32 modifierKeys = 0;
729 	while (system_time() < timeout) {
730 		modifierKeys = modifiers();
731 		if (!IsKeyDown(key)) {
732 			QuickSwitch(message);
733 			return;
734 		}
735 		if ((modifierKeys & B_CONTROL_KEY) == 0) {
736 			QuickSwitch(message);
737 			return;
738 		}
739 		snooze(50000);
740 	}
741 
742 	Process((modifierKeys & B_SHIFT_KEY) == 0,
743 		(modifierKeys & B_OPTION_KEY) != 0);
744 }
745 
746 
747 void
748 TSwitchManager::Stop(bool do_action, uint32)
749 {
750 	fWindow->Hide();
751 	if (do_action)
752 		ActivateApp(true, true);
753 
754 	release_sem(fMainMonitor);
755 }
756 
757 
758 TTeamGroup *
759 TSwitchManager::FindTeam(team_id teamID, int32 *index)
760 {
761 	int i = 0;
762 	TTeamGroup* info;
763 	while ((info = (TTeamGroup *)fGroupList.ItemAt(i)) != NULL) {
764 		if (info->TeamList()->HasItem((void*)teamID)) {
765 			*index = i;
766 			return info;
767 		}
768 		i++;
769 	}
770 
771 	return NULL;
772 }
773 
774 
775 void
776 TSwitchManager::Process(bool forward, bool byWindow)
777 {
778 	bool hidden = false;
779 	if (fWindow->Lock()) {
780 		hidden = fWindow->IsHidden();
781 		fWindow->Unlock();
782 	}
783 	if (byWindow) {
784 		// If hidden we need to get things started by switching to correct app
785 		if (hidden)
786 			SwitchToApp(fCurrentIndex, fCurrentIndex, forward);
787 		CycleWindow(forward, false);
788 	} else
789 		CycleApp(forward, false);
790 
791 	if (hidden) {
792 		// more auto keyrepeat code
793 		// Because of key repeats we don't want to respond to any extraneous
794 		// 'TASK' messages until the window is completely shown. So block here.
795 		// the WindowActivated hook function will unblock.
796 		fBlock = true;
797 
798 		if (fWindow->Lock()) {
799 			BRect screenFrame = BScreen().Frame();
800 			BRect windowFrame = fWindow->Frame();
801 
802 			if (!screenFrame.Contains(windowFrame)) {
803 				// center the window
804 				BPoint point((screenFrame.left + screenFrame.right) / 2,
805 					(screenFrame.top + screenFrame.bottom) / 2);
806 
807 				point.x -= (windowFrame.Width() / 2);
808 				point.y -= (windowFrame.Height() / 2);
809 				fWindow->MoveTo(point);
810 			}
811 
812 			fWindow->Show();
813 			fWindow->Unlock();
814 		}
815 	}
816 }
817 
818 
819 void
820 TSwitchManager::QuickSwitch(BMessage *message)
821 {
822 	uint32 modifiers = 0;
823 	message->FindInt32("modifiers", (int32 *) &modifiers);
824 
825 	team_id team;
826 	if (message->FindInt32("team", &team) == B_OK) {
827 		bool forward = ((modifiers & B_SHIFT_KEY) == 0);
828 
829 		if ((modifiers & B_OPTION_KEY) != 0)
830 			SwitchWindow(team, forward, true);
831 		else
832 			CycleApp(forward, true);
833 	}
834 
835 	release_sem(fMainMonitor);
836 }
837 
838 
839 int32
840 TSwitchManager::CountVisibleGroups()
841 {
842 	int32 result = 0;
843 
844 	int32 count = fGroupList.CountItems();
845 	for (int32 i = 0; i < count; i++) {
846 		if (!OKToUse((TTeamGroup *) fGroupList.ItemAt(i)))
847 			continue;
848 
849 		result++;
850 	}
851 	return result;
852 }
853 
854 
855 void
856 TSwitchManager::CycleWindow(bool forward, bool wrap)
857 {
858 	int32 max = CountWindows(fCurrentIndex);
859 	int32 prev = fCurrentWindow;
860 	int32 next = fCurrentWindow;
861 
862 	if (forward) {
863 		next++;
864 		if (next >= max) {
865 			if (!wrap)
866 				return;
867 			next = 0;
868 		}
869 	} else {
870 		next--;
871 		if (next < 0) {
872 			if (!wrap)
873 				return;
874 			next = max - 1;
875 		}
876 	}
877 	fCurrentWindow = next;
878 
879 	if (fCurrentWindow != prev)
880 		fWindow->WindowView()->ShowIndex(fCurrentWindow);
881 }
882 
883 
884 void
885 TSwitchManager::CycleApp(bool forward, bool activateNow)
886 {
887 	int32 startIndex = fCurrentIndex;
888 	int32 max = fGroupList.CountItems();
889 
890 	for (;;) {
891 		if (forward) {
892 			fCurrentIndex++;
893 			if (fCurrentIndex >= max)
894 				fCurrentIndex = 0;
895 		} else {
896 			fCurrentIndex--;
897 			if (fCurrentIndex < 0)
898 				fCurrentIndex = max - 1;
899 		}
900 		if ((fCurrentIndex == startIndex))
901 			// we've gone completely through the list without finding
902 			// a good app. Oh well.
903 			return;
904 
905 		if (!OKToUse((TTeamGroup *)fGroupList.ItemAt(fCurrentIndex)))
906 			continue;
907 
908 		// if we're here then we found a good one
909 		SwitchToApp(startIndex, fCurrentIndex, forward);
910 
911 		if (!activateNow)
912 			break;
913 
914 		if (ActivateApp(false, false))
915 			break;
916 	}
917 }
918 
919 
920 void
921 TSwitchManager::SwitchToApp(int32 previousIndex, int32 newIndex, bool forward)
922 {
923 	int32 previousSlot = fCurrentSlot;
924 
925 	fCurrentIndex = newIndex;
926 	fCurrentSlot = fWindow->SlotOf(fCurrentIndex);
927 	fCurrentWindow = 0;
928 
929 	fWindow->Update(previousIndex, fCurrentIndex, previousSlot, fCurrentSlot,
930 		forward);
931 }
932 
933 
934 bool
935 TSwitchManager::ActivateApp(bool forceShow, bool allowWorkspaceSwitch)
936 {
937 	// Let's get the info about the selected window. If it doesn't exist
938 	// anymore then get info about first window. If that doesn't exist then
939 	// do nothing.
940 	client_window_info* windowInfo = WindowInfo(fCurrentIndex, fCurrentWindow);
941 	if (windowInfo == NULL) {
942 		windowInfo = WindowInfo(fCurrentIndex, 0);
943 		if (windowInfo == NULL)
944 			return false;
945 	}
946 
947 	int32 currentWorkspace = current_workspace();
948 	TTeamGroup *teamGroup = (TTeamGroup *)fGroupList.ItemAt(fCurrentIndex);
949 	// Let's handle the easy case first: There's only 1 team in the group
950 	if (teamGroup->TeamList()->CountItems() == 1) {
951 		bool result;
952 		if (forceShow && (fCurrentWindow != 0 || windowInfo->is_mini))
953 			do_window_action(windowInfo->server_token, B_BRING_TO_FRONT,
954 				BRect(0, 0, 0, 0), false);
955 
956 		if (!forceShow && windowInfo->is_mini) {
957 			// we aren't unhiding minimized windows, so we can't do
958 			// anything here
959 			result = false;
960 		} else if (!allowWorkspaceSwitch
961 			&& (windowInfo->workspaces & (1 << currentWorkspace)) == 0) {
962 			// we're not supposed to switch workspaces so abort.
963 			result = false;
964 		} else {
965 			result = true;
966 			be_roster->ActivateApp((team_id)teamGroup->TeamList()->ItemAt(0));
967 		}
968 
969 		ASSERT(windowInfo);
970 		free(windowInfo);
971 		return result;
972 	}
973 
974 	// Now the trickier case. We're trying to Bring to the Front a group
975 	// of teams. The current window (defined by fCurrentWindow) will define
976 	// which workspace we're going to. Then, once that is determined we
977 	// want to bring to the front every window of the group of teams that
978 	// lives in that workspace.
979 
980 	if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) {
981 		if (!allowWorkspaceSwitch) {
982 			// If the first window in the list isn't in current workspace,
983 			// then none are. So we can't switch to this app.
984 			ASSERT(windowInfo);
985 			free(windowInfo);
986 			return false;
987 		}
988 		int32 destWorkspace = LowBitIndex(windowInfo->workspaces);
989 		// now switch to that workspace
990 		activate_workspace(destWorkspace);
991 	}
992 
993 	if (!forceShow && windowInfo->is_mini) {
994 		// If the first window in the list is hidden then no windows in
995 		// this group are visible. So we can't switch to this app.
996 		ASSERT(windowInfo);
997 		free(windowInfo);
998 		return false;
999 	}
1000 
1001 	int32 tokenCount;
1002 	int32 *tokens = get_token_list(-1, &tokenCount);
1003 	if (!tokens) {
1004 		ASSERT(windowInfo);
1005 		free(windowInfo);
1006 		return true;	// weird error, so don't try to recover
1007 	}
1008 
1009 	BList windowsToActivate;
1010 
1011 	// Now we go through all the windows in the current workspace list in order.
1012 	// As we hit member teams we build the "activate" list.
1013 	for (int32 i = 0; i < tokenCount; i++) {
1014 		client_window_info* matchWindowInfo = get_window_info(tokens[i]);
1015 		if (!matchWindowInfo) {
1016 			// That window probably closed. Just go to the next one.
1017 			continue;
1018 		}
1019 		if (!IsVisibleInCurrentWorkspace(matchWindowInfo)) {
1020 			// first non-visible in workspace window means we're done.
1021 			free(matchWindowInfo);
1022 			break;
1023 		}
1024 		if (matchWindowInfo->server_token != windowInfo->server_token
1025 			&& teamGroup->TeamList()->HasItem((void *)matchWindowInfo->team))
1026 			windowsToActivate.AddItem((void *)matchWindowInfo->server_token);
1027 
1028 		free(matchWindowInfo);
1029 	}
1030 
1031 	free(tokens);
1032 
1033 	// Want to go through the list backwards to keep windows in same relative
1034 	// order.
1035 	int32 i = windowsToActivate.CountItems() - 1;
1036 	for (; i >= 0; i--) {
1037 		int32 wid = (int32) windowsToActivate.ItemAt(i);
1038 		do_window_action(wid, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false);
1039 	}
1040 
1041 	// now bring the select window on top of everything.
1042 
1043 	do_window_action(windowInfo->server_token, B_BRING_TO_FRONT,
1044 		BRect(0, 0, 0, 0), false);
1045 	free(windowInfo);
1046 	return true;
1047 }
1048 
1049 
1050 void
1051 TSwitchManager::QuitApp()
1052 {
1053 	// check if we're in the last slot already (the last usable team group)
1054 
1055 	TTeamGroup *teamGroup;
1056 	int32 count = 0;
1057 	for (int32 i = fCurrentIndex + 1; i < fGroupList.CountItems(); i++) {
1058 		teamGroup = (TTeamGroup *)fGroupList.ItemAt(i);
1059 
1060 		if (!OKToUse(teamGroup))
1061 			continue;
1062 
1063 		count++;
1064 	}
1065 
1066 	teamGroup = (TTeamGroup *)fGroupList.ItemAt(fCurrentIndex);
1067 
1068 	if (count == 0) {
1069 		// switch to previous app in the list so that we don't jump to
1070 		// the start of the list (try to keep the same position when
1071 		// the apps at the current index go away)
1072 		CycleApp(false, false);
1073 	}
1074 
1075 	// send the quit request to all teams in this group
1076 
1077 	for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) {
1078 		team_id team = (team_id)teamGroup->TeamList()->ItemAt(i);
1079 		app_info info;
1080 		if (be_roster->GetRunningAppInfo(team, &info) == B_OK) {
1081 			if (!strcasecmp(info.signature, kTrackerSignature)) {
1082 				// Tracker can't be quit this way
1083 				continue;
1084 			}
1085 
1086 			BMessenger messenger(NULL, team);
1087 			messenger.SendMessage(B_QUIT_REQUESTED);
1088 		}
1089 	}
1090 }
1091 
1092 
1093 void
1094 TSwitchManager::HideApp()
1095 {
1096 	// hide all teams in this group
1097 
1098 	TTeamGroup *teamGroup = (TTeamGroup *)fGroupList.ItemAt(fCurrentIndex);
1099 
1100 	for (int32 i = teamGroup->TeamList()->CountItems(); i-- > 0;) {
1101 		team_id team = (team_id)teamGroup->TeamList()->ItemAt(i);
1102 		app_info info;
1103 		if (be_roster->GetRunningAppInfo(team, &info) == B_OK)
1104 			do_minimize_team(BRect(), team, false);
1105 	}
1106 }
1107 
1108 
1109 client_window_info*
1110 TSwitchManager::WindowInfo(int32 groupIndex, int32 windowIndex)
1111 {
1112 	TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex);
1113 	if (teamGroup == NULL)
1114 		return NULL;
1115 
1116 	int32 tokenCount;
1117 #ifndef __HAIKU__
1118 	int32* tokens = get_token_list(-1, &tokenCount);
1119 	if (tokens == NULL)
1120 		return NULL;
1121 #else
1122 	int32* tokens;
1123 	if (_GetSortedWindowTokens(&tokens, &tokenCount) != B_OK)
1124 		return NULL;
1125 #endif
1126 
1127 	int32 matches = 0;
1128 
1129 	// Want to find the "windowIndex'th" window in window order that belongs
1130 	// the the specified group (groupIndex). Since multiple teams can belong to
1131 	// the same group (multiple-launch apps) we get the list of _every_
1132 	// window and go from there.
1133 
1134 	client_window_info* result = NULL;
1135 	for (int32 i = 0; i < tokenCount; i++) {
1136 		client_window_info* windowInfo = get_window_info(tokens[i]);
1137 		if (windowInfo) {
1138 			// skip hidden/special windows
1139 			if (IsWindowOK(windowInfo)
1140 				&& (teamGroup->TeamList()->HasItem((void*)windowInfo->team))) {
1141 				// this window belongs to the team!
1142 				if (matches == windowIndex) {
1143 					// we found it!
1144 					result = windowInfo;
1145 					break;
1146 				}
1147 				matches++;
1148 			}
1149 			free(windowInfo);
1150 		}
1151 		// else - that window probably closed. Just go to the next one.
1152 	}
1153 
1154 	free(tokens);
1155 
1156 	return result;
1157 }
1158 
1159 
1160 int32
1161 TSwitchManager::CountWindows(int32 groupIndex, bool )
1162 {
1163 	TTeamGroup *teamGroup = (TTeamGroup *)fGroupList.ItemAt(groupIndex);
1164 	if (!teamGroup)
1165 		return 0;
1166 
1167 	int32 result = 0;
1168 
1169 	for (int32 i = 0; ; i++) {
1170 		team_id	teamID = (team_id)teamGroup->TeamList()->ItemAt(i);
1171 		if (teamID == 0)
1172 			break;
1173 
1174 		int32 count;
1175 		int32 *tokens = get_token_list(teamID, &count);
1176 		if (!tokens)
1177 			continue;
1178 
1179 		for (int32 i = 0; i < count; i++) {
1180 			window_info	*windowInfo = get_window_info(tokens[i]);
1181 			if (windowInfo) {
1182 				if (IsWindowOK(windowInfo))
1183 					result++;
1184 				free(windowInfo);
1185 			}
1186 		}
1187 		free(tokens);
1188 	}
1189 
1190 	return result;
1191 }
1192 
1193 
1194 void
1195 TSwitchManager::ActivateWindow(int32 windowID)
1196 {
1197 	if (windowID == -1)
1198 		windowID = fWindowID;
1199 
1200 	do_window_action(windowID, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false);
1201 }
1202 
1203 
1204 void
1205 TSwitchManager::SwitchWindow(team_id team, bool, bool activate)
1206 {
1207 	// Find the _last_ window in the current workspace that belongs
1208 	// to the group. This is the window to activate.
1209 
1210 	int32 index;
1211 	TTeamGroup* teamGroup = FindTeam(team, &index);
1212 
1213 	// cycle through the windows in the active application
1214 	int32 count;
1215 #ifndef __HAIKU__
1216 	int32* tokens = get_token_list(-1, &count);
1217 #else
1218 	int32* tokens;
1219 	if (_GetSortedWindowTokens(&tokens, &count) != B_OK)
1220 		return;
1221 #endif
1222 
1223 	for (int32 i = count - 1; i >= 0; i--) {
1224 		client_window_info* windowInfo = get_window_info(tokens[i]);
1225 		if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo)
1226 			&& teamGroup->TeamList()->HasItem((void*)windowInfo->team)) {
1227 				fWindowID = windowInfo->server_token;
1228 				if (activate)
1229 					ActivateWindow(windowInfo->server_token);
1230 
1231 				free(windowInfo);
1232 				break;
1233 		}
1234 		free(windowInfo);
1235 	}
1236 	free(tokens);
1237 }
1238 
1239 
1240 void
1241 TSwitchManager::Unblock()
1242 {
1243 	fBlock = false;
1244 	fSkipUntil = system_time();
1245 }
1246 
1247 
1248 int32
1249 TSwitchManager::CurrentIndex()
1250 {
1251 	return fCurrentIndex;
1252 }
1253 
1254 
1255 int32
1256 TSwitchManager::CurrentWindow()
1257 {
1258 	return fCurrentWindow;
1259 }
1260 
1261 
1262 int32
1263 TSwitchManager::CurrentSlot()
1264 {
1265 	return fCurrentSlot;
1266 }
1267 
1268 
1269 BList *
1270 TSwitchManager::GroupList()
1271 {
1272 	return &fGroupList;
1273 }
1274 
1275 
1276 //	#pragma mark -
1277 
1278 
1279 TBox::TBox(BRect bounds, TSwitchManager *manager, TSwitcherWindow *window,
1280 	TIconView *iview)
1281 	: BBox(bounds, "top", B_FOLLOW_NONE, B_WILL_DRAW, B_NO_BORDER),
1282 	fManager(manager),
1283 	fWindow(window),
1284 	fIconView(iview),
1285 	fLeftScroller(false),
1286 	fRightScroller(false)
1287 {
1288 }
1289 
1290 
1291 void
1292 TBox::AllAttached()
1293 {
1294 	BRect centerRect(kCenterSlot * kSlotSize, 0,
1295 		(kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1);
1296 	BRect frame = fIconView->Frame();
1297 
1298 	// scroll the centerRect to correct location
1299 	centerRect.OffsetBy(frame.left, frame.top);
1300 
1301 	// switch to local coords
1302 	fIconView->ConvertToParent(&centerRect);
1303 
1304 	fCenter = centerRect;
1305 }
1306 
1307 
1308 void
1309 TBox::MouseDown(BPoint where)
1310 {
1311 	if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller)
1312 		return;
1313 
1314 	BRect frame = fIconView->Frame();
1315 	BRect bounds = Bounds();
1316 
1317 	if (fLeftScroller) {
1318 		BRect lhit(0, frame.top, frame.left, frame.bottom);
1319 		if (lhit.Contains(where)) {
1320 			// Want to scroll by NUMSLOTS-1 slots
1321 			int32 previousIndex = fManager->CurrentIndex();
1322 			int32 previousSlot = fManager->CurrentSlot();
1323 			int32 newSlot = previousSlot - (kNumSlots - 1);
1324 			if (newSlot < 0)
1325 				newSlot = 0;
1326 			int32 newIndex = fIconView->IndexAt(newSlot);
1327 
1328 			fManager->SwitchToApp(previousIndex, newIndex, false);
1329 		}
1330 	}
1331 
1332 	if (fRightScroller) {
1333 		BRect rhit(frame.right, frame.top, bounds.right, frame.bottom);
1334 		if (rhit.Contains(where)) {
1335 			// Want to scroll by NUMSLOTS-1 slots
1336 			int32 previousIndex = fManager->CurrentIndex();
1337 			int32 previousSlot = fManager->CurrentSlot();
1338 			int32 newSlot = previousSlot + (kNumSlots-1);
1339 			int32 newIndex = fIconView->IndexAt(newSlot);
1340 
1341 			if (newIndex < 0) {
1342 				// don't have a page full to scroll
1343 				int32 valid = fManager->CountVisibleGroups();
1344 				newIndex = fIconView->IndexAt(valid-1);
1345 			}
1346 			fManager->SwitchToApp(previousIndex, newIndex, true);
1347 		}
1348 	}
1349 
1350 	frame = fWindow->WindowView()->Frame();
1351 	if (fUpScroller) {
1352 		BRect hit1(frame.left - 10, frame.top, frame.left, (frame.top+frame.bottom)/2);
1353 		BRect hit2(frame.right, frame.top, frame.right + 10, (frame.top+frame.bottom)/2);
1354 		if (hit1.Contains(where) || hit2.Contains(where)) {
1355 			// Want to scroll up 1 window
1356 			fManager->CycleWindow(false, false);
1357 		}
1358 	}
1359 
1360 	if (fDownScroller) {
1361 		BRect hit1(frame.left - 10, (frame.top+frame.bottom) / 2, frame.left, frame.bottom);
1362 		BRect hit2(frame.right, (frame.top+frame.bottom) / 2, frame.right + 10, frame.bottom);
1363 		if (hit1.Contains(where) || hit2.Contains(where)) {
1364 			// Want to scroll down 1 window
1365 			fManager->CycleWindow(true, false);
1366 		}
1367 	}
1368 }
1369 
1370 
1371 void
1372 TBox::Draw(BRect update)
1373 {
1374 	static const int32 kChildInset = 7;
1375 	static const int32 kWedge = 6;
1376 
1377 	BBox::Draw(update);
1378 
1379 	// The fancy border around the icon view
1380 
1381 	BRect bounds = Bounds();
1382 	float height = fIconView->Bounds().Height();
1383 	float center = (bounds.right + bounds.left) / 2;
1384 
1385 	BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2);
1386 	rgb_color white = {255, 255, 255, 255};
1387 	rgb_color standardGray = ui_color(B_PANEL_BACKGROUND_COLOR);
1388 	rgb_color veryDarkGray = {128, 128, 128, 255};
1389 	rgb_color darkGray = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT);
1390 
1391 	// Fill the area with dark gray
1392 	SetHighColor(darkGray);
1393 	box.InsetBy(1,1);
1394 	FillRect(box);
1395 
1396 	box.InsetBy(-1,-1);
1397 
1398 	BeginLineArray(50);
1399 
1400 	// The main frame around the icon view
1401 	AddLine(box.LeftTop(), BPoint(center-kWedge, box.top), veryDarkGray);
1402 	AddLine(BPoint(center+kWedge, box.top), box.RightTop(), veryDarkGray);
1403 
1404 	AddLine(box.LeftBottom(), BPoint(center-kWedge, box.bottom), veryDarkGray);
1405 	AddLine(BPoint(center+kWedge, box.bottom), box.RightBottom(), veryDarkGray);
1406 	AddLine(box.LeftBottom() + BPoint(1, 1),
1407 		BPoint(center-kWedge, box.bottom + 1), white);
1408 	AddLine(BPoint(center+kWedge, box.bottom) + BPoint(0, 1),
1409 		box.RightBottom() + BPoint(1, 1), white);
1410 
1411 	AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray);
1412 	AddLine(box.RightTop(), box.RightBottom(), veryDarkGray);
1413 	AddLine(box.RightTop() + BPoint(1, 1),
1414 		box.RightBottom() + BPoint(1, 1), white);
1415 
1416 	// downward pointing area at top of frame
1417 	BPoint point(center - kWedge, box.top);
1418 	AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray);
1419 	AddLine(point + BPoint(kWedge, kWedge),
1420 		BPoint(center+kWedge, point.y), veryDarkGray);
1421 
1422 	AddLine(point + BPoint(1, 0),
1423 		point + BPoint(1, 0) + BPoint(kWedge - 1, kWedge - 1), white);
1424 
1425 	AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1),
1426 		BPoint(center+kWedge-1, point.y), darkGray);
1427 
1428 	BPoint topPoint = point;
1429 
1430 	// upward pointing area at bottom of frame
1431 	point.y = box.bottom;
1432 	point.x = center - kWedge;
1433 	AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray);
1434 	AddLine(point + BPoint(kWedge, -kWedge),
1435 		BPoint(center+kWedge, point.y), veryDarkGray);
1436 
1437 	AddLine(point + BPoint(1, 0),
1438 		point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white);
1439 
1440 	AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)),
1441 		BPoint(center + kWedge - 1, point.y), darkGray);
1442 
1443 	BPoint bottomPoint = point;
1444 
1445 	EndLineArray();
1446 
1447 	// fill the downward pointing arrow area
1448 	SetHighColor(standardGray);
1449 	FillTriangle(topPoint + BPoint(2, 0),
1450 		topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2),
1451 		BPoint(center + kWedge - 2, topPoint.y));
1452 
1453 	// fill the upward pointing arrow area
1454 	SetHighColor(standardGray);
1455 	FillTriangle(bottomPoint + BPoint(2,0),
1456 		bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)),
1457 		BPoint(center + kWedge - 2, bottomPoint.y));
1458 
1459 	DrawIconScrollers(false);
1460 	DrawWindowScrollers(false);
1461 
1462 }
1463 
1464 
1465 void
1466 TBox::DrawIconScrollers(bool force)
1467 {
1468 	bool updateLeft = false;
1469 	bool updateRight = false;
1470 	rgb_color leftc = {0, 0, 0, 255};
1471 	rgb_color rightc = {0, 0, 0, 255};
1472 	rgb_color bkg = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT);
1473 	rgb_color dark = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT);
1474 
1475 	BRect rect = fIconView->Bounds();
1476 	if (rect.left > (kSlotSize * kCenterSlot)) {
1477 		updateLeft = true;
1478 		fLeftScroller = true;
1479 		leftc = dark;
1480 	} else {
1481 		fLeftScroller = false;
1482 		if (force) {
1483 			updateLeft = true;
1484 			leftc = bkg;
1485 		}
1486 	}
1487 
1488 	int32 maxIndex = fManager->GroupList()->CountItems() - 1;
1489 			// last_frame is in fIconView coordinate space
1490 	BRect lastFrame = fIconView->FrameOf(maxIndex);
1491 
1492 	if (lastFrame.right > rect.right) {
1493 		updateRight = true;
1494 		fRightScroller = true;
1495 		rightc = dark;
1496 	} else {
1497 		fRightScroller = false;
1498 		if (force) {
1499 			updateRight = true;
1500 			rightc = bkg;
1501 		}
1502 	}
1503 
1504 	rect = fIconView->Frame();
1505 	if (updateLeft) {
1506 		SetHighColor(leftc);
1507 		BPoint	pt1, pt2, pt3;
1508 		pt1.x = rect.left - 5;
1509 		pt1.y = floorf((rect.bottom + rect.top) / 2);
1510 		pt2.x = pt3.x = pt1.x + 3;
1511 		pt2.y = pt1.y - 3;
1512 		pt3.y = pt1.y + 3;
1513 		FillTriangle(pt1, pt2, pt3);
1514 	}
1515 	if (updateRight) {
1516 		SetHighColor(rightc);
1517 		BPoint	pt1, pt2, pt3;
1518 		pt1.x = rect.right + 4;
1519 		pt1.y = rintf((rect.bottom + rect.top) / 2);
1520 		pt2.x = pt3.x = pt1.x - 4;
1521 		pt2.y = pt1.y - 4;
1522 		pt3.y = pt1.y + 4;
1523 		FillTriangle(pt1, pt2, pt3);
1524 	}
1525 }
1526 
1527 
1528 void
1529 TBox::DrawWindowScrollers(bool force)
1530 {
1531 	bool updateUp = false;
1532 	bool updateDown = false;
1533 	rgb_color upColor = {0, 0, 0, 255};
1534 	rgb_color downColor = {0, 0, 0, 255};
1535 	rgb_color bkg = ui_color(B_PANEL_BACKGROUND_COLOR);
1536 	rgb_color dark = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT);
1537 
1538 	BRect rect = fWindow->WindowView()->Bounds();
1539 	if (rect.top != 0) {
1540 		updateUp = true;
1541 		fUpScroller = true;
1542 		upColor = dark;
1543 	} else {
1544 		fUpScroller = false;
1545 		if (force) {
1546 			updateUp = true;
1547 			upColor = bkg;
1548 		}
1549 	}
1550 
1551 	int32 groupIndex = fManager->CurrentIndex();
1552 	int32 maxIndex = fManager->CountWindows(groupIndex) - 1;
1553 
1554 	BRect lastFrame(0, 0, 0, 0);
1555 	if (maxIndex >= 0) {
1556 		lastFrame = fWindow->WindowView()->FrameOf(maxIndex);
1557 	}
1558 	if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) {
1559 		updateDown = true;
1560 		fDownScroller = true;
1561 		downColor = dark;
1562 	} else {
1563 		fDownScroller = false;
1564 		if (force) {
1565 			updateDown = true;
1566 			downColor = bkg;
1567 		}
1568 	}
1569 
1570 	rect = fWindow->WindowView()->Frame();
1571 	rect.InsetBy(-3, 0);
1572 	if (updateUp) {
1573 		SetHighColor(upColor);
1574 		BPoint	pt1, pt2, pt3;
1575 		pt1.x = rect.left - 6;
1576 		pt1.y = rect.top + 3;
1577 		pt2.y = pt3.y = pt1.y + 4;
1578 		pt2.x = pt1.x - 4;
1579 		pt3.x = pt1.x + 4;
1580 		FillTriangle(pt1, pt2, pt3);
1581 
1582 		pt1.x += rect.Width() + 12;
1583 		pt2.x += rect.Width() + 12;
1584 		pt3.x += rect.Width() + 12;
1585 		FillTriangle(pt1, pt2, pt3);
1586 	}
1587 	if (updateDown) {
1588 		SetHighColor(downColor);
1589 		BPoint	pt1, pt2, pt3;
1590 		pt1.x = rect.left - 6;
1591 		pt1.y = rect.bottom - 3;
1592 		pt2.y = pt3.y = pt1.y - 4;
1593 		pt2.x = pt1.x - 4;
1594 		pt3.x = pt1.x + 4;
1595 		FillTriangle(pt1, pt2, pt3);
1596 
1597 		pt1.x += rect.Width() + 12;
1598 		pt2.x += rect.Width() + 12;
1599 		pt3.x += rect.Width() + 12;
1600 		FillTriangle(pt1, pt2, pt3);
1601 
1602 	}
1603 	Sync();
1604 }
1605 
1606 
1607 //	#pragma mark -
1608 
1609 
1610 TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager *manager)
1611 	: BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK,
1612 			B_MODAL_ALL_WINDOW_FEEL,
1613 			B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES),
1614 	fManager(manager),
1615 	fHairTrigger(true)
1616 {
1617 	BRect rect = frame;
1618 	rect.OffsetTo(B_ORIGIN);
1619 	rect.InsetBy(kHorizontalMargin, 0);
1620 	rect.top = kVerticalMargin;
1621 	rect.bottom = rect.top + kSlotSize - 1;
1622 
1623 	fIconView = new TIconView(rect, manager, this);
1624 
1625 	rect.top = rect.bottom + (kVerticalMargin * 1 + 4);
1626 	rect.InsetBy(9, 0);
1627 
1628 	fWindowView = new TWindowView(rect, manager, this);
1629 	fWindowView->ResizeToPreferred();
1630 
1631 	fTopView = new TBox(Bounds(), fManager, this, fIconView);
1632 	AddChild(fTopView);
1633 
1634 	SetPulseRate(0);
1635 	fTopView->AddChild(fIconView);
1636 	fTopView->AddChild(fWindowView);
1637 }
1638 
1639 
1640 TSwitcherWindow::~TSwitcherWindow()
1641 {
1642 }
1643 
1644 
1645 void
1646 TSwitcherWindow::MessageReceived(BMessage *message)
1647 {
1648 	switch (message->what) {
1649 		case B_KEY_DOWN:
1650 		{
1651 			int32 repeats = 0;
1652 			if (message->FindInt32("be:key_repeat", &repeats) == B_OK
1653 				&& (fSkipKeyRepeats || (repeats % 6) != 0))
1654 				break;
1655 
1656 			// The first actual key press let's us listening to repeated keys
1657 			fSkipKeyRepeats = false;
1658 
1659 			uint32 rawChar;
1660 			uint32 modifiers;
1661 			message->FindInt32("raw_char", 0, (int32 *)&rawChar);
1662 			message->FindInt32("modifiers", 0, (int32 *)&modifiers);
1663 			DoKey(rawChar, modifiers);
1664 			break;
1665 		}
1666 
1667 		default:
1668 			BWindow::MessageReceived(message);
1669 	}
1670 }
1671 
1672 
1673 void
1674 TSwitcherWindow::Redraw(int32 index)
1675 {
1676 	BRect frame = fIconView->FrameOf(index);
1677 	frame.right = fIconView->Bounds().right;
1678 	fIconView->Invalidate(frame);
1679 }
1680 
1681 
1682 void
1683 TSwitcherWindow::DoKey(uint32 key, uint32 modifiers)
1684 {
1685 	bool forward = ((modifiers & B_SHIFT_KEY) == 0);
1686 
1687 	switch (key) {
1688 		case B_RIGHT_ARROW:
1689 		case '`':
1690 			fManager->CycleApp(true, false);
1691 			break;
1692 
1693 		case B_LEFT_ARROW:
1694 		case '~':
1695 			fManager->CycleApp(false, false);
1696 			break;
1697 
1698 		case B_UP_ARROW:
1699 			fManager->CycleWindow(false, false);
1700 			break;
1701 
1702 		case B_DOWN_ARROW:
1703 			fManager->CycleWindow(true, false);
1704 			break;
1705 
1706 		case B_TAB:
1707 			fManager->CycleApp(forward, false);
1708 			break;
1709 
1710 		case B_ESCAPE:
1711 			fManager->Stop(false, 0);
1712 			break;
1713 
1714 		case B_SPACE:
1715 		case B_ENTER:
1716 			fManager->Stop(true, modifiers);
1717 			break;
1718 
1719 		case 'q':
1720 		case 'Q':
1721 			fManager->QuitApp();
1722 			break;
1723 
1724 		case 'h':
1725 		case 'H':
1726 			fManager->HideApp();
1727 			break;
1728 
1729 #if _ALLOW_STICKY_
1730 		case 's':
1731 		case 'S':
1732 			if (fHairTrigger) {
1733 				SetLook(B_TITLED_WINDOW_LOOK);
1734 				fHairTrigger = false;
1735 			} else {
1736 				SetLook(B_MODAL_WINDOW_LOOK);
1737 				fHairTrigger = true;
1738 			}
1739 			break;
1740 #endif
1741 	}
1742 }
1743 
1744 
1745 bool
1746 TSwitcherWindow::QuitRequested()
1747 {
1748 	((TBarApp *) be_app)->Settings()->switcherLoc = Frame().LeftTop();
1749 	fManager->Stop(false, 0);
1750 	return false;
1751 }
1752 
1753 
1754 void
1755 TSwitcherWindow::WindowActivated(bool state)
1756 {
1757 	if (state)
1758 		fManager->Unblock();
1759 }
1760 
1761 
1762 void
1763 TSwitcherWindow::Update(int32 prev, int32 current, int32 previousSlot,
1764 	int32 currentSlot, bool forward)
1765 {
1766 	if (!IsHidden())
1767 		fIconView->Update(prev, current, previousSlot, currentSlot, forward);
1768 	else
1769 		fIconView->CenterOn(current);
1770 
1771 	fWindowView->UpdateGroup(current, 0);
1772 }
1773 
1774 
1775 void
1776 TSwitcherWindow::Hide()
1777 {
1778 	fIconView->Hiding();
1779 	SetPulseRate(0);
1780 	BWindow::Hide();
1781 }
1782 
1783 
1784 void
1785 TSwitcherWindow::Show()
1786 {
1787 	fHairTrigger = true;
1788 	fSkipKeyRepeats = true;
1789 	fIconView->Showing();
1790 	SetPulseRate(100000);
1791 	SetLook(B_MODAL_WINDOW_LOOK);
1792 	BWindow::Show();
1793 }
1794 
1795 
1796 TBox *
1797 TSwitcherWindow::TopView()
1798 {
1799 	return fTopView;
1800 }
1801 
1802 
1803 bool
1804 TSwitcherWindow::HairTrigger()
1805 {
1806 	return fHairTrigger;
1807 }
1808 
1809 
1810 inline int32
1811 TSwitcherWindow::SlotOf(int32 i)
1812 {
1813 	return fIconView->SlotOf(i);
1814 }
1815 
1816 
1817 inline TIconView *
1818 TSwitcherWindow::IconView()
1819 {
1820 	return fIconView;
1821 }
1822 
1823 
1824 inline TWindowView *
1825 TSwitcherWindow::WindowView()
1826 {
1827 	return fWindowView;
1828 }
1829 
1830 
1831 //	#pragma mark -
1832 
1833 
1834 TIconView::TIconView(BRect frame, TSwitchManager *manager, TSwitcherWindow *switcherWindow)
1835 	: BView(frame, "main_view", B_FOLLOW_NONE,
1836 			B_WILL_DRAW | B_PULSE_NEEDED),
1837 	fAutoScrolling(false),
1838 	fSwitcher(switcherWindow),
1839 	fManager(manager)
1840 {
1841 	BRect rect(0, 0, kSlotSize - 1, kSlotSize - 1);
1842 	rgb_color color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_1_TINT);
1843 
1844 	fOffView = new BView(rect, "off_view", B_FOLLOW_NONE, B_WILL_DRAW);
1845 	fOffView->SetHighColor(color);
1846 	fOffBitmap = new BBitmap(rect, B_RGB32, true);
1847 	fOffBitmap->AddChild(fOffView);
1848 
1849 	fCurrentSmall = new BBitmap(BRect(0, 0, 15, 15), kIconFormat);
1850 	fCurrentLarge = new BBitmap(BRect(0, 0, 31, 31), kIconFormat);
1851 
1852 	SetViewColor(color);
1853 	SetLowColor(color);
1854 }
1855 
1856 
1857 TIconView::~TIconView()
1858 {
1859 	delete fCurrentSmall;
1860 	delete fCurrentLarge;
1861 	delete fOffBitmap;
1862 }
1863 
1864 
1865 void
1866 TIconView::KeyDown(const char *, int32)
1867 {
1868 }
1869 
1870 
1871 void
1872 TIconView::CacheIcons(TTeamGroup *teamGroup)
1873 {
1874 	const BBitmap *bitmap = teamGroup->SmallIcon();
1875 	ASSERT(bitmap);
1876 	fCurrentSmall->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0,
1877 		bitmap->ColorSpace());
1878 
1879 	bitmap = teamGroup->LargeIcon();
1880 	ASSERT(bitmap);
1881 	fCurrentLarge->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0,
1882 		bitmap->ColorSpace());
1883 }
1884 
1885 
1886 void
1887 TIconView::AnimateIcon(BBitmap *startIcon, BBitmap *endIcon)
1888 {
1889 	BRect centerRect(kCenterSlot*kSlotSize, 0,
1890 		(kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1);
1891 	BRect startIconBounds = startIcon->Bounds();
1892 	BRect bounds = Bounds();
1893 	float width = startIconBounds.Width();
1894 	int32 amount = (width < 20) ? -2 : 2;
1895 
1896 	// center the starting icon inside of centerRect
1897 	float off = (centerRect.Width() - width) / 2;
1898 	startIconBounds.OffsetTo(BPoint(off, off));
1899 
1900 	// scroll the centerRect to correct location
1901 	centerRect.OffsetBy(bounds.left, 0);
1902 
1903 	BRect destRect = fOffBitmap->Bounds();
1904 	// scroll to the centerRect location
1905 	destRect.OffsetTo(centerRect.left, 0);
1906 	// center the destRect inside of centerRect.
1907 	off = (centerRect.Width() - destRect.Width()) / 2;
1908 	destRect.OffsetBy(BPoint(off, off));
1909 
1910 	fOffBitmap->Lock();
1911 	fOffView->SetDrawingMode(B_OP_ALPHA);
1912 	for (int i = 0; i < 2; i++) {
1913 		startIconBounds.InsetBy(amount,amount);
1914 		snooze(20000);
1915 		fOffView->FillRect(fOffView->Bounds());
1916 		fOffView->DrawBitmap(startIcon, startIconBounds);
1917 		fOffView->Sync();
1918 		DrawBitmap(fOffBitmap, destRect);
1919 	}
1920 	for (int i = 0; i < 2; i++) {
1921 		startIconBounds.InsetBy(amount,amount);
1922 		snooze(20000);
1923 		fOffView->FillRect(fOffView->Bounds());
1924 		fOffView->DrawBitmap(endIcon, startIconBounds);
1925 		fOffView->Sync();
1926 		DrawBitmap(fOffBitmap, destRect);
1927 	}
1928 
1929 	fOffView->SetDrawingMode(B_OP_COPY);
1930 	fOffBitmap->Unlock();
1931 }
1932 
1933 
1934 void
1935 TIconView::Update(int32, int32 current, int32 previousSlot, int32 currentSlot,
1936 	bool forward)
1937 {
1938 	// Animate the shrinking of the currently centered icon.
1939 	AnimateIcon(fCurrentLarge, fCurrentSmall);
1940 
1941 	int32 nslots = abs(previousSlot - currentSlot);
1942 	int32 stepSize = kScrollStep;
1943 
1944 	if (forward && (currentSlot < previousSlot)) {
1945 		// we were at the end of the list and we just moved to the start
1946 		forward = false;
1947 		if (previousSlot - currentSlot > 4)
1948 			stepSize *= 2;
1949 	} else if (!forward && (currentSlot > previousSlot)) {
1950 		// we're are moving backwards and we just hit start of list and
1951 		// we wrapped to the end.
1952 		forward = true;
1953 		if (currentSlot - previousSlot > 4)
1954 			stepSize *= 2;
1955 	}
1956 
1957 	int32 scrollValue = forward ? stepSize : -stepSize;
1958 	int32 total = 0;
1959 
1960 	fAutoScrolling = true;
1961 	while (total < (nslots * kSlotSize)) {
1962 		ScrollBy(scrollValue, 0);
1963 		snooze(1000);
1964 		total += stepSize;
1965 		Window()->UpdateIfNeeded();
1966 	}
1967 	fAutoScrolling = false;
1968 
1969 	TTeamGroup *teamGroup = (TTeamGroup *)fManager->GroupList()->ItemAt(current);
1970 	ASSERT(teamGroup);
1971 	CacheIcons(teamGroup);
1972 
1973 	// Animate the expansion of the currently centered icon
1974 	AnimateIcon(fCurrentSmall, fCurrentLarge);
1975 }
1976 
1977 
1978 void
1979 TIconView::CenterOn(int32 index)
1980 {
1981 	BRect rect = FrameOf(index);
1982 	ScrollTo(rect.left - (kCenterSlot * kSlotSize), 0);
1983 }
1984 
1985 
1986 int32
1987 TIconView::ItemAtPoint(BPoint point) const
1988 {
1989 	float tmpPointVerticalIndex = (point.x / kSlotSize) - kCenterSlot;
1990 	if (tmpPointVerticalIndex < 0)
1991 		return -1;
1992 
1993 	int32 pointVerticalIndex = (int32)tmpPointVerticalIndex;
1994 
1995 	for (int32 i = 0, verticalIndex = 0; ; i++) {
1996 
1997 		TTeamGroup *teamGroup = (TTeamGroup *)fManager->GroupList()->ItemAt(i);
1998 		if (teamGroup == NULL)
1999 			break;
2000 
2001 		if (!OKToUse(teamGroup))
2002 			continue;
2003 
2004 		if (verticalIndex == pointVerticalIndex)
2005 			return i;
2006 
2007 		verticalIndex++;
2008 	}
2009 	return -1;
2010 }
2011 
2012 
2013 void
2014 TIconView::ScrollTo(BPoint where)
2015 {
2016 	BView::ScrollTo(where);
2017 	fSwitcher->TopView()->DrawIconScrollers(true);
2018 }
2019 
2020 
2021 int32
2022 TIconView::IndexAt(int32 slot) const
2023 {
2024 	BList *list = fManager->GroupList();
2025 	int32 count = list->CountItems();
2026 	int32 slotIndex = 0;
2027 
2028 	for (int32 i = 0; i < count; i++) {
2029 		TTeamGroup *teamGroup = (TTeamGroup *)list->ItemAt(i);
2030 
2031 		if (!OKToUse(teamGroup))
2032 			continue;
2033 
2034 		if (slotIndex == slot) {
2035 			return i;
2036 		}
2037 		slotIndex++;
2038 	}
2039 	return -1;
2040 }
2041 
2042 
2043 int32
2044 TIconView::SlotOf(int32 index) const
2045 {
2046 	BRect rect = FrameOf(index);
2047 	return (int32)(rect.left / kSlotSize) - kCenterSlot;
2048 }
2049 
2050 
2051 BRect
2052 TIconView::FrameOf(int32 index) const
2053 {
2054 	BList *list = fManager->GroupList();
2055 	int32 visible = kCenterSlot - 1;
2056 		// first few slots in view are empty
2057 
2058 	TTeamGroup *teamGroup;
2059 	for (int32 i = 0; i <= index; i++) {
2060 		teamGroup = (TTeamGroup *)list->ItemAt(i);
2061 
2062 		if (!OKToUse(teamGroup))
2063 			continue;
2064 
2065 		visible++;
2066 	}
2067 
2068 	return BRect(visible * kSlotSize, 0, (visible + 1) * kSlotSize - 1, kSlotSize - 1);
2069 }
2070 
2071 
2072 void
2073 TIconView::DrawTeams(BRect update)
2074 {
2075 	int32 mainIndex = fManager->CurrentIndex();
2076 	BList *list = fManager->GroupList();
2077 	int32 count = list->CountItems();
2078 
2079 	BRect rect(kCenterSlot * kSlotSize, 0,
2080 		(kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1);
2081 
2082 	for (int32 i = 0; i < count; i++) {
2083 		TTeamGroup *teamGroup = (TTeamGroup *) list->ItemAt(i);
2084 
2085 		if (!OKToUse(teamGroup))
2086 			continue;
2087 
2088 		if (rect.Intersects(update) && teamGroup) {
2089 #ifdef __HAIKU__
2090 			SetDrawingMode(B_OP_ALPHA);
2091 			SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
2092 #else
2093 			SetDrawingMode(B_OP_OVER);
2094 #endif
2095 			teamGroup->Draw(this, rect, !fAutoScrolling && (i == mainIndex));
2096 
2097 			if (i == mainIndex)
2098 				CacheIcons(teamGroup);
2099 
2100 			SetDrawingMode(B_OP_COPY);
2101 		}
2102 		rect.OffsetBy(kSlotSize,0);
2103 	}
2104 }
2105 
2106 
2107 void
2108 TIconView::Draw(BRect update)
2109 {
2110 	DrawTeams(update);
2111 }
2112 
2113 
2114 void
2115 TIconView::MouseDown(BPoint where)
2116 {
2117 	int32 index = ItemAtPoint(where);
2118 	if (index >= 0) {
2119 		int32 previousIndex = fManager->CurrentIndex();
2120 		int32 previousSlot = fManager->CurrentSlot();
2121 		int32 currentSlot = SlotOf(index);
2122 		fManager->SwitchToApp(previousIndex, index, (currentSlot > previousSlot));
2123 	}
2124 }
2125 
2126 
2127 void
2128 TIconView::Pulse()
2129 {
2130 	uint32 modifiersKeys = modifiers();
2131 	if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) {
2132 		fManager->Stop(true, modifiersKeys);
2133 		return;
2134 	}
2135 
2136 	if (!fSwitcher->HairTrigger()) {
2137 		uint32 buttons;
2138 		BPoint point;
2139 		GetMouse(&point, &buttons);
2140 		if (buttons != 0) {
2141 			point = ConvertToScreen(point);
2142 			if (!Window()->Frame().Contains(point))
2143 				fManager->Stop(false, 0);
2144 		}
2145 	}
2146 }
2147 
2148 
2149 void
2150 TIconView::Showing()
2151 {
2152 }
2153 
2154 
2155 void
2156 TIconView::Hiding()
2157 {
2158 	ScrollTo(B_ORIGIN);
2159 }
2160 
2161 
2162 //	#pragma mark -
2163 
2164 
2165 TWindowView::TWindowView(BRect rect, TSwitchManager* manager,
2166 		TSwitcherWindow* window)
2167 	: BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
2168 	fCurrentToken(-1),
2169 	fSwitcher(window),
2170 	fManager(manager)
2171 {
2172 	SetFont(be_plain_font);
2173 }
2174 
2175 
2176 void
2177 TWindowView::AttachedToWindow()
2178 {
2179 	if (Parent())
2180 		SetViewColor(Parent()->ViewColor());
2181 	else
2182 		SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2183 }
2184 
2185 
2186 void
2187 TWindowView::ScrollTo(BPoint where)
2188 {
2189 	BView::ScrollTo(where);
2190 	fSwitcher->TopView()->DrawWindowScrollers(true);
2191 }
2192 
2193 
2194 
2195 BRect
2196 TWindowView::FrameOf(int32 index) const
2197 {
2198 	return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1);
2199 }
2200 
2201 
2202 void
2203 TWindowView::GetPreferredSize(float *_width, float *_height)
2204 {
2205 	font_height	fh;
2206 	be_plain_font->GetHeight(&fh);
2207 	fItemHeight = (int32) fh.ascent + fh.descent;
2208 
2209 	// top & bottom margin
2210 	fItemHeight = fItemHeight + 3 + 3;
2211 
2212 	// want fItemHeight to be divisible by kWindowScrollSteps.
2213 	fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps)
2214 		/ kWindowScrollSteps) * kWindowScrollSteps;
2215 
2216 	*_height = fItemHeight;
2217 
2218 	// leave width alone
2219 	*_width = Bounds().Width();
2220 }
2221 
2222 
2223 void
2224 TWindowView::ShowIndex(int32 newIndex)
2225 {
2226 	// convert index to scroll location
2227 	BPoint point(0, newIndex * fItemHeight);
2228 	BRect bounds = Bounds();
2229 
2230 	int32 groupIndex = fManager->CurrentIndex();
2231 	TTeamGroup* teamGroup
2232 		= (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex);
2233 	if (teamGroup == NULL)
2234 		return;
2235 
2236 	window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex);
2237 	if (windowInfo == NULL)
2238 		return;
2239 
2240 	fCurrentToken = windowInfo->server_token;
2241 	free(windowInfo);
2242 
2243 	if (bounds.top == point.y)
2244 		return;
2245 
2246 	int32 oldIndex = (int32) (bounds.top / fItemHeight);
2247 
2248 	int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps);
2249 	int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize;
2250 	int32 total = 0;
2251 	int32 nslots = abs(newIndex - oldIndex);
2252 
2253 	while (total < (nslots * (int32)fItemHeight)) {
2254 		ScrollBy(0, scrollValue);
2255 		snooze(10000);
2256 		total += stepSize;
2257 		Window()->UpdateIfNeeded();
2258 	}
2259 }
2260 
2261 
2262 void
2263 TWindowView::Draw(BRect update)
2264 {
2265 	int32 groupIndex = fManager->CurrentIndex();
2266 	TTeamGroup *teamGroup = (TTeamGroup *) fManager->GroupList()->ItemAt(groupIndex);
2267 	if (!teamGroup)
2268 		return;
2269 
2270 	BRect bounds = Bounds();
2271 	int32 windowIndex = (int32) (bounds.top / fItemHeight);
2272 	BRect windowRect = bounds;
2273 
2274 	windowRect.top = windowIndex * fItemHeight;
2275 	windowRect.bottom = ((windowIndex+1) * fItemHeight) - 1;
2276 
2277 	for (int32 i = 0; i < 3; i++) {
2278 		if (!update.Intersects(windowRect)) {
2279 			windowIndex++;
2280 			windowRect.OffsetBy(0, fItemHeight);
2281 			continue;
2282 		}
2283 
2284 		// is window in current workspace?
2285 
2286 		bool local = true;
2287 		bool minimized = false;
2288 		BString title;
2289 
2290 		client_window_info* windowInfo
2291 			= fManager->WindowInfo(groupIndex, windowIndex);
2292 		if (windowInfo != NULL) {
2293 			if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0)
2294 				title << teamGroup->Name() << ": " << windowInfo->name;
2295 			else
2296 				title = teamGroup->Name();
2297 
2298 			int32 currentWorkspace = current_workspace();
2299 			if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0)
2300 				local = false;
2301 
2302 			minimized = windowInfo->is_mini;
2303 			free(windowInfo);
2304 		} else
2305 			title = teamGroup->Name();
2306 
2307 		if (!title.Length())
2308 			return;
2309 
2310 		float stringWidth = StringWidth(title.String());
2311 		float maxWidth = bounds.Width() - (14 + 5);
2312 
2313 		if (stringWidth > maxWidth) {
2314 			// window title is too long, need to truncate
2315 			TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth);
2316 			stringWidth = maxWidth;
2317 		}
2318 
2319 		BPoint point((bounds.Width() - (stringWidth + 14 + 5)) / 2, windowRect.bottom - 4);
2320 		BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2);
2321 		SetDrawingMode(B_OP_OVER);
2322 		const BBitmap *bitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE,
2323 			minimized ? R_WindowHiddenIcon : R_WindowShownIcon);
2324 		p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2;
2325 		DrawBitmap(bitmap, p);
2326 
2327 		if (!local) {
2328 			SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_4_TINT));
2329 			p.x -= 8;
2330 			p.y += 4;
2331 			StrokeLine(p + BPoint(2, 2), p + BPoint(2, 2));
2332 			StrokeLine(p + BPoint(4, 2), p + BPoint(6, 2));
2333 
2334 			StrokeLine(p + BPoint(0, 5), p + BPoint(0, 5));
2335 			StrokeLine(p + BPoint(2, 5), p + BPoint(6, 5));
2336 
2337 			StrokeLine(p + BPoint(1, 8), p + BPoint(1, 8));
2338 			StrokeLine(p + BPoint(3, 8), p + BPoint(6, 8));
2339 
2340 			SetHighColor(0, 0, 0);
2341 		}
2342 
2343 		point.x += 21;
2344 		MovePenTo(point);
2345 
2346 		DrawString(title.String());
2347 		SetDrawingMode(B_OP_COPY);
2348 
2349 		windowIndex++;
2350 		windowRect.OffsetBy(0, fItemHeight);
2351 	}
2352 }
2353 
2354 
2355 void
2356 TWindowView::UpdateGroup(int32 , int32 windowIndex)
2357 {
2358 	ScrollTo(0, windowIndex * fItemHeight);
2359 	Invalidate(Bounds());
2360 }
2361 
2362 
2363 void
2364 TWindowView::Pulse()
2365 {
2366 	// If selected window went away then reset to first window
2367 	window_info	*windowInfo = get_window_info(fCurrentToken);
2368 	if (windowInfo == NULL) {
2369 		Invalidate();
2370 		ShowIndex(0);
2371 	} else
2372 		free(windowInfo);
2373 }
2374 
2375