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