vfs: check userland buffers before reading them.
[haiku.git] / src / apps / deskbar / Switcher.cpp
blob268d6d8ec6f50e0d8925fe99260a3e3a83f77bd6
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
37 #include "Switcher.h"
39 #include <float.h>
40 #include <stdlib.h>
41 #include <strings.h>
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 #include <WindowInfo.h>
54 #include "BarApp.h"
55 #include "ResourceSet.h"
56 #include "WindowMenuItem.h"
57 #include "icons.h"
58 #include "tracker_private.h"
60 #define _ALLOW_STICKY_ 0
61 // allows you to press 's' to keep the switcher window on screen
64 static const color_space kIconFormat = B_RGBA32;
67 class TTeamGroup {
68 public:
69 TTeamGroup();
70 TTeamGroup(BList* teams, uint32 flags, char* name,
71 const char* signature);
72 virtual ~TTeamGroup();
74 void Draw(BView* view, BRect bounds, bool main);
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; }
89 private:
90 BList* fTeams;
91 uint32 fFlags;
92 char fSignature[B_MIME_TYPE_LENGTH];
93 char* fName;
94 BBitmap* fSmallIcon;
95 BBitmap* fLargeIcon;
98 class TSwitcherWindow : public BWindow {
99 public:
100 TSwitcherWindow(BRect frame,
101 TSwitchManager* manager);
102 virtual ~TSwitcherWindow();
104 virtual bool QuitRequested();
105 virtual void MessageReceived(BMessage* message);
106 virtual void Show();
107 virtual void Hide();
108 virtual void WindowActivated(bool state);
110 void DoKey(uint32 key, uint32 modifiers);
111 TIconView* IconView();
112 TWindowView* WindowView();
113 TBox* TopView();
114 bool HairTrigger();
115 void Update(int32 previous, int32 current,
116 int32 prevSlot, int32 currentSlot,
117 bool forward);
118 int32 SlotOf(int32);
119 void Redraw(int32 index);
121 private:
122 TSwitchManager* fManager;
123 TIconView* fIconView;
124 TBox* fTopView;
125 TWindowView* fWindowView;
126 bool fHairTrigger;
127 bool fSkipKeyRepeats;
130 class TWindowView : public BView {
131 public:
132 TWindowView(BRect frame, TSwitchManager* manager,
133 TSwitcherWindow* switcher);
135 void UpdateGroup(int32 groupIndex, int32 windowIndex);
137 virtual void AttachedToWindow();
138 virtual void Draw(BRect update);
139 virtual void Pulse();
140 virtual void GetPreferredSize(float* w, float* h);
141 void ScrollTo(float x, float y)
143 ScrollTo(BPoint(x, y));
145 virtual void ScrollTo(BPoint where);
147 void ShowIndex(int32 windex);
148 BRect FrameOf(int32 index) const;
150 private:
151 int32 fCurrentToken;
152 float fItemHeight;
153 TSwitcherWindow* fSwitcher;
154 TSwitchManager* fManager;
157 class TIconView : public BView {
158 public:
159 TIconView(BRect frame, TSwitchManager* manager,
160 TSwitcherWindow* switcher);
161 virtual ~TIconView();
163 void Showing();
164 void Hiding();
166 virtual void KeyDown(const char* bytes, int32 numBytes);
167 virtual void Pulse();
168 virtual void MouseDown(BPoint point);
169 virtual void Draw(BRect updateRect);
171 void ScrollTo(float x, float y)
173 ScrollTo(BPoint(x, y));
175 virtual void ScrollTo(BPoint where);
176 void Update(int32 previous, int32 current,
177 int32 previousSlot, int32 currentSlot,
178 bool forward);
179 void DrawTeams(BRect update);
180 int32 SlotOf(int32) const;
181 BRect FrameOf(int32) const;
182 int32 ItemAtPoint(BPoint) const;
183 int32 IndexAt(int32 slot) const;
184 void CenterOn(int32 index);
186 private:
187 void CacheIcons(TTeamGroup* group);
188 void AnimateIcon(BBitmap* startIcon, BBitmap* endIcon);
190 bool fAutoScrolling;
191 TSwitcherWindow* fSwitcher;
192 TSwitchManager* fManager;
193 BBitmap* fOffBitmap;
194 BView* fOffView;
195 BBitmap* fCurrentSmall;
196 BBitmap* fCurrentLarge;
199 class TBox : public BBox {
200 public:
201 TBox(BRect bounds, TSwitchManager* manager,
202 TSwitcherWindow* window, TIconView* iconView);
204 virtual void Draw(BRect update);
205 virtual void AllAttached();
206 virtual void DrawIconScrollers(bool force);
207 virtual void DrawWindowScrollers(bool force);
208 virtual void MouseDown(BPoint where);
210 private:
211 TSwitchManager* fManager;
212 TSwitcherWindow* fWindow;
213 TIconView* fIconView;
214 BRect fCenter;
215 bool fLeftScroller;
216 bool fRightScroller;
217 bool fUpScroller;
218 bool fDownScroller;
222 const int32 kHorizontalMargin = 11;
223 const int32 kVerticalMargin = 10;
225 // SLOT_SIZE must be divisible by 4. That's because of the scrolling
226 // animation. If this needs to change then look at TIconView::Update()
228 const int32 kSlotSize = 36;
229 const int32 kScrollStep = kSlotSize / 2;
230 const int32 kNumSlots = 7;
231 const int32 kCenterSlot = 3;
233 const int32 kWindowScrollSteps = 3;
236 // #pragma mark -
239 static int32
240 LowBitIndex(uint32 value)
242 int32 result = 0;
243 int32 bitMask = 1;
245 if (value == 0)
246 return -1;
248 while (result < 32 && (value & bitMask) == 0) {
249 result++;
250 bitMask = bitMask << 1;
252 return result;
256 inline bool
257 IsVisibleInCurrentWorkspace(const window_info* windowInfo)
259 // The window list is always ordered from the top front visible window
260 // (the first on the list), going down through all the other visible
261 // windows, then all hidden or non-workspace visible windows at the end.
262 // layer > 2 : normal visible window
263 // layer == 2 : reserved for the desktop window (visible also)
264 // layer < 2 : hidden (0) and non workspace visible window (1)
265 return windowInfo->layer > 2;
269 bool
270 IsKeyDown(int32 key)
272 key_info keyInfo;
274 get_key_info(&keyInfo);
275 return (keyInfo.key_states[key >> 3] & (1 << ((7 - key) & 7))) != 0;
279 bool
280 IsWindowOK(const window_info* windowInfo)
282 // is_mini (true means that the window is minimized).
283 // if not, then show_hide >= 1 means that the window is hidden.
284 // If the window is both minimized and hidden, then you get :
285 // TWindow->is_mini = false;
286 // TWindow->was_mini = true;
287 // TWindow->show_hide >= 1;
289 if (windowInfo->feel != _STD_W_TYPE_)
290 return false;
292 if (windowInfo->is_mini)
293 return true;
295 return windowInfo->show_hide_level <= 0;
300 SmartStrcmp(const char* s1, const char* s2)
302 if (strcasecmp(s1, s2) == 0)
303 return 0;
305 // if the strings on differ in spaces or underscores they still match
306 while (*s1 && *s2) {
307 if ((*s1 == ' ') || (*s1 == '_')) {
308 s1++;
309 continue;
311 if ((*s2 == ' ') || (*s2 == '_')) {
312 s2++;
313 continue;
315 if (*s1 != *s2) {
316 // they differ
317 return 1;
319 s1++;
320 s2++;
323 // if one of the strings ended before the other
324 // TODO: could process trailing spaces and underscores
325 if (*s1)
326 return 1;
327 if (*s2)
328 return 1;
330 return 0;
334 // #pragma mark -
337 TTeamGroup::TTeamGroup()
339 fTeams(NULL),
340 fFlags(0),
341 fName(NULL),
342 fSmallIcon(NULL),
343 fLargeIcon(NULL)
345 fSignature[0] = '\0';
349 TTeamGroup::TTeamGroup(BList* teams, uint32 flags, char* name,
350 const char* signature)
352 fTeams(teams),
353 fFlags(flags),
354 fName(name),
355 fSmallIcon(NULL),
356 fLargeIcon(NULL)
358 strlcpy(fSignature, signature, sizeof(fSignature));
360 fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), kIconFormat);
361 fLargeIcon = new BBitmap(BRect(0, 0, 31, 31), kIconFormat);
363 app_info appInfo;
364 if (be_roster->GetAppInfo(signature, &appInfo) == B_OK) {
365 BNode node(&(appInfo.ref));
366 if (node.InitCheck() == B_OK) {
367 BNodeInfo nodeInfo(&node);
368 if (nodeInfo.InitCheck() == B_OK) {
369 nodeInfo.GetTrackerIcon(fSmallIcon, B_MINI_ICON);
370 nodeInfo.GetTrackerIcon(fLargeIcon, B_LARGE_ICON);
377 TTeamGroup::~TTeamGroup()
379 delete fTeams;
380 free(fName);
381 delete fSmallIcon;
382 delete fLargeIcon;
386 void
387 TTeamGroup::Draw(BView* view, BRect bounds, bool main)
389 BRect rect;
390 if (main) {
391 rect = fLargeIcon->Bounds();
392 rect.OffsetTo(bounds.LeftTop());
393 rect.OffsetBy(2, 2);
394 view->DrawBitmap(fLargeIcon, rect);
395 } else {
396 rect = fSmallIcon->Bounds();
397 rect.OffsetTo(bounds.LeftTop());
398 rect.OffsetBy(10, 10);
399 view->DrawBitmap(fSmallIcon, rect);
404 // #pragma mark -
407 TSwitchManager::TSwitchManager(BPoint point)
408 : BHandler("SwitchManager"),
409 fMainMonitor(create_sem(1, "main_monitor")),
410 fBlock(false),
411 fSkipUntil(0),
412 fLastSwitch(0),
413 fQuickSwitchIndex(-1),
414 fQuickSwitchWindow(-1),
415 fGroupList(10),
416 fCurrentIndex(0),
417 fCurrentSlot(0),
418 fWindowID(-1)
420 BRect rect(point.x, point.y,
421 point.x + (kSlotSize * kNumSlots) - 1 + (2 * kHorizontalMargin),
422 point.y + 82);
423 fWindow = new TSwitcherWindow(rect, this);
424 fWindow->AddHandler(this);
426 fWindow->Lock();
427 fWindow->Run();
429 BList tmpList;
430 TBarApp::Subscribe(BMessenger(this), &tmpList);
432 for (int32 i = 0; ; i++) {
433 BarTeamInfo* barTeamInfo = (BarTeamInfo*)tmpList.ItemAt(i);
434 if (!barTeamInfo)
435 break;
437 TTeamGroup* tinfo = new TTeamGroup(barTeamInfo->teams,
438 barTeamInfo->flags, barTeamInfo->name, barTeamInfo->sig);
439 fGroupList.AddItem(tinfo);
441 barTeamInfo->teams = NULL;
442 barTeamInfo->name = NULL;
444 delete barTeamInfo;
446 fWindow->Unlock();
450 TSwitchManager::~TSwitchManager()
452 for (int32 i = fGroupList.CountItems() - 1; i >= 0; i--) {
453 TTeamGroup* teamInfo = static_cast<TTeamGroup*>(fGroupList.ItemAt(i));
454 delete teamInfo;
459 void
460 TSwitchManager::MessageReceived(BMessage* message)
462 switch (message->what) {
463 case B_SOME_APP_QUIT:
465 // This is only sent when last team of a matching set quits
466 team_id teamID;
467 int i = 0;
468 TTeamGroup* tinfo;
469 message->FindInt32("team", &teamID);
471 while ((tinfo = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) {
472 if (tinfo->TeamList()->HasItem((void*)(addr_t)teamID)) {
473 fGroupList.RemoveItem(i);
475 fWindow->Redraw(i);
476 if (i <= fCurrentIndex) {
477 fCurrentIndex--;
478 CycleApp(true);
480 delete tinfo;
481 break;
483 i++;
485 break;
488 case B_SOME_APP_LAUNCHED:
490 BList* teams;
491 const char* name;
492 BBitmap* smallIcon;
493 uint32 flags;
494 const char* signature;
496 if (message->FindPointer("teams", (void**)&teams) != B_OK)
497 break;
499 if (message->FindPointer("icon", (void**)&smallIcon) != B_OK) {
500 delete teams;
501 break;
504 delete smallIcon;
506 if (message->FindString("sig", &signature) != B_OK) {
507 delete teams;
508 break;
511 if (message->FindInt32("flags", (int32*)&flags) != B_OK) {
512 delete teams;
513 break;
516 if (message->FindString("name", &name) != B_OK) {
517 delete teams;
518 break;
521 TTeamGroup* tinfo = new TTeamGroup(teams, flags, strdup(name),
522 signature);
524 fGroupList.AddItem(tinfo);
525 fWindow->Redraw(fGroupList.CountItems() - 1);
527 break;
530 case kAddTeam:
532 const char* signature = message->FindString("sig");
533 team_id team = message->FindInt32("team");
534 int32 count = fGroupList.CountItems();
536 for (int32 i = 0; i < count; i++) {
537 TTeamGroup* tinfo = (TTeamGroup*)fGroupList.ItemAt(i);
538 if (strcasecmp(tinfo->Signature(), signature) == 0) {
539 if (!(tinfo->TeamList()->HasItem((void*)(addr_t)team)))
540 tinfo->TeamList()->AddItem((void*)(addr_t)team);
541 break;
544 break;
547 case kRemoveTeam:
549 team_id team = message->FindInt32("team");
550 int32 count = fGroupList.CountItems();
552 for (int32 i = 0; i < count; i++) {
553 TTeamGroup* tinfo = (TTeamGroup*)fGroupList.ItemAt(i);
554 if (tinfo->TeamList()->HasItem((void*)(addr_t)team)) {
555 tinfo->TeamList()->RemoveItem((void*)(addr_t)team);
556 break;
559 break;
562 case 'TASK':
564 // The first TASK message calls MainEntry. Subsequent ones
565 // call Process().
566 bigtime_t time;
567 message->FindInt64("when", (int64*)&time);
569 // The fSkipUntil stuff can be removed once the new input_server
570 // starts differentiating initial key_downs from KeyDowns generated
571 // by auto-repeat. Until then the fSkipUntil stuff helps, but it
572 // isn't perfect.
573 if (time < fSkipUntil)
574 break;
576 status_t status = acquire_sem_etc(fMainMonitor, 1, B_TIMEOUT, 0);
577 if (status != B_OK) {
578 if (!fWindow->IsHidden() && !fBlock) {
579 // Want to skip TASK msgs posted before the window
580 // was made visible. Better UI feel if we do this.
581 if (time > fSkipUntil) {
582 uint32 modifiers = 0;
583 message->FindInt32("modifiers", (int32*)&modifiers);
584 int32 key = 0;
585 message->FindInt32("key", &key);
587 Process((modifiers & B_SHIFT_KEY) == 0, key == 0x11);
590 } else
591 MainEntry(message);
593 break;
596 default:
597 break;
602 void
603 TSwitchManager::_SortApps()
605 team_id* teams;
606 int32 count;
607 if (BPrivate::get_application_order(current_workspace(), &teams, &count)
608 != B_OK)
609 return;
611 BList groups;
612 if (!groups.AddList(&fGroupList)) {
613 free(teams);
614 return;
617 fGroupList.MakeEmpty();
619 for (int32 i = 0; i < count; i++) {
620 // find team
621 TTeamGroup* info = NULL;
622 for (int32 j = 0; (info = (TTeamGroup*)groups.ItemAt(j)) != NULL; j++) {
623 if (info->TeamList()->HasItem((void*)(addr_t)teams[i])) {
624 groups.RemoveItem(j);
625 break;
629 if (info != NULL)
630 fGroupList.AddItem(info);
633 fGroupList.AddList(&groups);
634 // add the remaining entries
635 free(teams);
639 void
640 TSwitchManager::MainEntry(BMessage* message)
642 bigtime_t now = system_time();
643 bigtime_t timeout = now + 180000;
644 // The above delay has a good "feel" found by trial and error
646 app_info appInfo;
647 be_roster->GetActiveAppInfo(&appInfo);
649 bool resetQuickSwitch = false;
651 if (now > fLastSwitch + 400000) {
652 _SortApps();
653 resetQuickSwitch = true;
656 fLastSwitch = now;
658 int32 index;
659 fCurrentIndex = FindTeam(appInfo.team, &index) != NULL ? index : 0;
661 if (resetQuickSwitch) {
662 fQuickSwitchIndex = fCurrentIndex;
663 fQuickSwitchWindow = fCurrentWindow;
666 int32 key;
667 message->FindInt32("key", (int32*)&key);
669 uint32 modifierKeys = 0;
670 while (system_time() < timeout) {
671 modifierKeys = modifiers();
672 if (!IsKeyDown(key)) {
673 QuickSwitch(message);
674 return;
676 if ((modifierKeys & B_CONTROL_KEY) == 0) {
677 QuickSwitch(message);
678 return;
680 snooze(20000);
681 // Must be a multiple of the delay used above
684 Process((modifierKeys & B_SHIFT_KEY) == 0, key == 0x11);
688 void
689 TSwitchManager::Stop(bool do_action, uint32)
691 fWindow->Hide();
692 if (do_action)
693 ActivateApp(true, true);
695 release_sem(fMainMonitor);
699 TTeamGroup*
700 TSwitchManager::FindTeam(team_id teamID, int32* index)
702 int i = 0;
703 TTeamGroup* info;
704 while ((info = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) {
705 if (info->TeamList()->HasItem((void*)(addr_t)teamID)) {
706 *index = i;
707 return info;
709 i++;
712 return NULL;
716 void
717 TSwitchManager::Process(bool forward, bool byWindow)
719 bool hidden = false;
720 if (fWindow->Lock()) {
721 hidden = fWindow->IsHidden();
722 fWindow->Unlock();
724 if (byWindow) {
725 // If hidden we need to get things started by switching to correct app
726 if (hidden)
727 SwitchToApp(fCurrentIndex, fCurrentIndex, forward);
728 CycleWindow(forward, true);
729 } else
730 CycleApp(forward, false);
732 if (hidden) {
733 // more auto keyrepeat code
734 // Because of key repeats we don't want to respond to any extraneous
735 // 'TASK' messages until the window is completely shown. So block here.
736 // the WindowActivated hook function will unblock.
737 fBlock = true;
739 if (fWindow->Lock()) {
740 BRect screenFrame = BScreen().Frame();
741 BRect windowFrame = fWindow->Frame();
743 if (!screenFrame.Contains(windowFrame)) {
744 // center the window
745 BPoint point((screenFrame.left + screenFrame.right) / 2,
746 (screenFrame.top + screenFrame.bottom) / 2);
748 point.x -= (windowFrame.Width() / 2);
749 point.y -= (windowFrame.Height() / 2);
750 fWindow->MoveTo(point);
753 fWindow->Show();
754 fWindow->Unlock();
760 void
761 TSwitchManager::QuickSwitch(BMessage* message)
763 uint32 modifiers = 0;
764 message->FindInt32("modifiers", (int32*)&modifiers);
765 int32 key = 0;
766 message->FindInt32("key", &key);
768 team_id team;
769 if (message->FindInt32("team", &team) == B_OK) {
770 bool forward = (modifiers & B_SHIFT_KEY) == 0;
772 if (key == 0x11) {
773 // TODO: add the same switch logic we have for apps!
774 SwitchWindow(team, forward, true);
775 } else {
776 if (fQuickSwitchIndex >= 0) {
777 // Switch to the first app inbetween to make it always the next
778 // app to switch to after the quick switch.
779 int32 current = fCurrentIndex;
780 SwitchToApp(current, fQuickSwitchIndex, false);
781 ActivateApp(false, false);
783 fCurrentIndex = current;
786 CycleApp(forward, true);
790 release_sem(fMainMonitor);
794 void
795 TSwitchManager::CycleWindow(bool forward, bool wrap)
797 int32 max = CountWindows(fCurrentIndex);
798 int32 prev = fCurrentWindow;
799 int32 next = fCurrentWindow;
801 if (forward) {
802 next++;
803 if (next >= max) {
804 if (!wrap)
805 return;
806 next = 0;
808 } else {
809 next--;
810 if (next < 0) {
811 if (!wrap)
812 return;
813 next = max - 1;
816 fCurrentWindow = next;
818 if (fCurrentWindow != prev)
819 fWindow->WindowView()->ShowIndex(fCurrentWindow);
823 void
824 TSwitchManager::CycleApp(bool forward, bool activateNow)
826 int32 startIndex = fCurrentIndex;
828 if (_FindNextValidApp(forward)) {
829 // if we're here then we found a good one
830 SwitchToApp(startIndex, fCurrentIndex, forward);
832 if (!activateNow)
833 return;
835 ActivateApp(false, false);
840 bool
841 TSwitchManager::_FindNextValidApp(bool forward)
843 if (fGroupList.IsEmpty())
844 return false;
846 int32 max = fGroupList.CountItems();
847 if (forward) {
848 fCurrentIndex++;
849 if (fCurrentIndex >= max)
850 fCurrentIndex = 0;
851 } else {
852 fCurrentIndex--;
853 if (fCurrentIndex < 0)
854 fCurrentIndex = max - 1;
857 return true;
861 void
862 TSwitchManager::SwitchToApp(int32 previousIndex, int32 newIndex, bool forward)
864 int32 previousSlot = fCurrentSlot;
866 fCurrentIndex = newIndex;
867 fCurrentSlot = fWindow->SlotOf(fCurrentIndex);
868 fCurrentWindow = 0;
870 fWindow->Update(previousIndex, fCurrentIndex, previousSlot, fCurrentSlot,
871 forward);
875 bool
876 TSwitchManager::ActivateApp(bool forceShow, bool allowWorkspaceSwitch)
878 // Let's get the info about the selected window. If it doesn't exist
879 // anymore then get info about first window. If that doesn't exist then
880 // do nothing.
881 client_window_info* windowInfo = WindowInfo(fCurrentIndex, fCurrentWindow);
882 if (windowInfo == NULL) {
883 windowInfo = WindowInfo(fCurrentIndex, 0);
884 if (windowInfo == NULL)
885 return false;
888 int32 currentWorkspace = current_workspace();
889 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex);
891 // Let's handle the easy case first: There's only 1 team in the group
892 if (teamGroup->TeamList()->CountItems() == 1) {
893 bool result;
894 if (forceShow && (fCurrentWindow != 0 || windowInfo->is_mini)) {
895 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT,
896 BRect(0, 0, 0, 0), false);
899 if (!forceShow && windowInfo->is_mini) {
900 // we aren't unhiding minimized windows, so we can't do
901 // anything here
902 result = false;
903 } else if (!allowWorkspaceSwitch
904 && (windowInfo->workspaces & (1 << currentWorkspace)) == 0) {
905 // we're not supposed to switch workspaces so abort.
906 result = false;
907 } else {
908 result = true;
909 be_roster->ActivateApp((addr_t)teamGroup->TeamList()->ItemAt(0));
912 ASSERT(windowInfo);
913 free(windowInfo);
914 return result;
917 // Now the trickier case. We're trying to Bring to the Front a group
918 // of teams. The current window (defined by fCurrentWindow) will define
919 // which workspace we're going to. Then, once that is determined we
920 // want to bring to the front every window of the group of teams that
921 // lives in that workspace.
923 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) {
924 if (!allowWorkspaceSwitch) {
925 // If the first window in the list isn't in current workspace,
926 // then none are. So we can't switch to this app.
927 ASSERT(windowInfo);
928 free(windowInfo);
929 return false;
931 int32 destWorkspace = LowBitIndex(windowInfo->workspaces);
932 // now switch to that workspace
933 activate_workspace(destWorkspace);
936 if (!forceShow && windowInfo->is_mini) {
937 // If the first window in the list is hidden then no windows in
938 // this group are visible. So we can't switch to this app.
939 ASSERT(windowInfo);
940 free(windowInfo);
941 return false;
944 int32 tokenCount;
945 int32* tokens = get_token_list(-1, &tokenCount);
946 if (tokens == NULL) {
947 ASSERT(windowInfo);
948 free(windowInfo);
949 return true;
950 // weird error, so don't try to recover
953 BList windowsToActivate;
955 // Now we go through all the windows in the current workspace list in order.
956 // As we hit member teams we build the "activate" list.
957 for (int32 i = 0; i < tokenCount; i++) {
958 client_window_info* matchWindowInfo = get_window_info(tokens[i]);
959 if (!matchWindowInfo) {
960 // That window probably closed. Just go to the next one.
961 continue;
963 if (!IsVisibleInCurrentWorkspace(matchWindowInfo)) {
964 // first non-visible in workspace window means we're done.
965 free(matchWindowInfo);
966 break;
968 if (matchWindowInfo->server_token != windowInfo->server_token
969 && teamGroup->TeamList()->HasItem((void*)(addr_t)matchWindowInfo->team))
970 windowsToActivate.AddItem((void*)(addr_t)matchWindowInfo->server_token);
972 free(matchWindowInfo);
975 free(tokens);
977 // Want to go through the list backwards to keep windows in same relative
978 // order.
979 int32 i = windowsToActivate.CountItems() - 1;
980 for (; i >= 0; i--) {
981 int32 wid = (addr_t)windowsToActivate.ItemAt(i);
982 do_window_action(wid, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false);
985 // now bring the select window on top of everything.
987 do_window_action(windowInfo->server_token, B_BRING_TO_FRONT,
988 BRect(0, 0, 0, 0), false);
990 free(windowInfo);
991 return true;
996 \brief quit all teams in this group
998 void
999 TSwitchManager::QuitApp()
1001 // we should not be trying to quit an app if we have an empty list
1002 if (fGroupList.IsEmpty())
1003 return;
1005 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex);
1006 if (fCurrentIndex == fGroupList.CountItems() - 1) {
1007 // if we're in the last slot already (the last usable team group)
1008 // switch to previous app in the list so that we don't jump to
1009 // the start of the list (try to keep the same position when
1010 // the apps at the current index go away)
1011 CycleApp(false, false);
1014 // send the quit request to all teams in this group
1015 for (int32 i = teamGroup->TeamList()->CountItems() - 1; i >= 0; i--) {
1016 team_id team = (addr_t)teamGroup->TeamList()->ItemAt(i);
1017 app_info info;
1018 if (be_roster->GetRunningAppInfo(team, &info) == B_OK) {
1019 if (strcasecmp(info.signature, kTrackerSignature) == 0) {
1020 // Tracker can't be quit this way
1021 continue;
1024 BMessenger messenger(NULL, team);
1025 messenger.SendMessage(B_QUIT_REQUESTED);
1032 \brief hide all teams in this group
1034 void
1035 TSwitchManager::HideApp()
1037 // we should not be trying to hide an app if we have an empty list
1038 if (fGroupList.IsEmpty())
1039 return;
1041 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex);
1043 for (int32 i = teamGroup->TeamList()->CountItems() - 1; i >= 0; i--) {
1044 team_id team = (addr_t)teamGroup->TeamList()->ItemAt(i);
1045 app_info info;
1046 if (be_roster->GetRunningAppInfo(team, &info) == B_OK)
1047 do_minimize_team(BRect(), team, false);
1052 client_window_info*
1053 TSwitchManager::WindowInfo(int32 groupIndex, int32 windowIndex)
1055 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex);
1056 if (teamGroup == NULL)
1057 return NULL;
1059 int32 tokenCount;
1060 int32* tokens = get_token_list(-1, &tokenCount);
1061 if (tokens == NULL)
1062 return NULL;
1064 int32 matches = 0;
1066 // Want to find the "windowIndex'th" window in window order that belongs
1067 // the the specified group (groupIndex). Since multiple teams can belong to
1068 // the same group (multiple-launch apps) we get the list of _every_
1069 // window and go from there.
1071 client_window_info* result = NULL;
1072 for (int32 i = 0; i < tokenCount; i++) {
1073 client_window_info* windowInfo = get_window_info(tokens[i]);
1074 if (windowInfo) {
1075 // skip hidden/special windows
1076 if (IsWindowOK(windowInfo)
1077 && (teamGroup->TeamList()->HasItem((void*)(addr_t)windowInfo->team))) {
1078 // this window belongs to the team!
1079 if (matches == windowIndex) {
1080 // we found it!
1081 result = windowInfo;
1082 break;
1084 matches++;
1086 free(windowInfo);
1088 // else - that window probably closed. Just go to the next one.
1091 free(tokens);
1093 return result;
1097 int32
1098 TSwitchManager::CountWindows(int32 groupIndex, bool )
1100 TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex);
1101 if (!teamGroup)
1102 return 0;
1104 int32 result = 0;
1106 for (int32 i = 0; ; i++) {
1107 team_id teamID = (addr_t)teamGroup->TeamList()->ItemAt(i);
1108 if (teamID == 0)
1109 break;
1111 int32 count;
1112 int32* tokens = get_token_list(teamID, &count);
1113 if (!tokens)
1114 continue;
1116 for (int32 i = 0; i < count; i++) {
1117 window_info *windowInfo = get_window_info(tokens[i]);
1118 if (windowInfo) {
1119 if (IsWindowOK(windowInfo))
1120 result++;
1121 free(windowInfo);
1124 free(tokens);
1127 return result;
1131 void
1132 TSwitchManager::ActivateWindow(int32 windowID)
1134 if (windowID == -1)
1135 windowID = fWindowID;
1137 do_window_action(windowID, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false);
1141 void
1142 TSwitchManager::SwitchWindow(team_id team, bool, bool activate)
1144 // Find the _last_ window in the current workspace that belongs
1145 // to the group. This is the window to activate.
1147 int32 index;
1148 TTeamGroup* teamGroup = FindTeam(team, &index);
1150 // cycle through the windows in the active application
1151 int32 count;
1152 int32* tokens = get_token_list(-1, &count);
1153 if (tokens == NULL)
1154 return;
1156 for (int32 i = count - 1; i >= 0; i--) {
1157 client_window_info* windowInfo = get_window_info(tokens[i]);
1158 if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo)
1159 && teamGroup->TeamList()->HasItem((void*)(addr_t)windowInfo->team)) {
1160 fWindowID = windowInfo->server_token;
1161 if (activate)
1162 ActivateWindow(windowInfo->server_token);
1164 free(windowInfo);
1165 break;
1167 free(windowInfo);
1169 free(tokens);
1173 void
1174 TSwitchManager::Unblock()
1176 fBlock = false;
1177 fSkipUntil = system_time();
1181 int32
1182 TSwitchManager::CurrentIndex()
1184 return fCurrentIndex;
1188 int32
1189 TSwitchManager::CurrentWindow()
1191 return fCurrentWindow;
1195 int32
1196 TSwitchManager::CurrentSlot()
1198 return fCurrentSlot;
1202 BList*
1203 TSwitchManager::GroupList()
1205 return &fGroupList;
1209 // #pragma mark -
1212 TBox::TBox(BRect bounds, TSwitchManager* manager, TSwitcherWindow* window,
1213 TIconView* iconView)
1215 BBox(bounds, "top", B_FOLLOW_ALL, B_WILL_DRAW, B_NO_BORDER),
1216 fManager(manager),
1217 fWindow(window),
1218 fIconView(iconView),
1219 fLeftScroller(false),
1220 fRightScroller(false),
1221 fUpScroller(false),
1222 fDownScroller(false)
1227 void
1228 TBox::AllAttached()
1230 BRect centerRect(kCenterSlot * kSlotSize, 0,
1231 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1);
1232 BRect frame = fIconView->Frame();
1234 // scroll the centerRect to correct location
1235 centerRect.OffsetBy(frame.left, frame.top);
1237 // switch to local coords
1238 fIconView->ConvertToParent(&centerRect);
1240 fCenter = centerRect;
1244 void
1245 TBox::MouseDown(BPoint where)
1247 if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller)
1248 return;
1250 BRect frame = fIconView->Frame();
1251 BRect bounds = Bounds();
1253 if (fLeftScroller) {
1254 BRect lhit(0, frame.top, frame.left, frame.bottom);
1255 if (lhit.Contains(where)) {
1256 // Want to scroll by NUMSLOTS - 1 slots
1257 int32 previousIndex = fManager->CurrentIndex();
1258 int32 previousSlot = fManager->CurrentSlot();
1259 int32 newSlot = previousSlot - (kNumSlots - 1);
1260 if (newSlot < 0)
1261 newSlot = 0;
1263 int32 newIndex = fIconView->IndexAt(newSlot);
1264 fManager->SwitchToApp(previousIndex, newIndex, false);
1268 if (fRightScroller) {
1269 BRect rhit(frame.right, frame.top, bounds.right, frame.bottom);
1270 if (rhit.Contains(where)) {
1271 // Want to scroll by NUMSLOTS - 1 slots
1272 int32 previousIndex = fManager->CurrentIndex();
1273 int32 previousSlot = fManager->CurrentSlot();
1274 int32 newSlot = previousSlot + (kNumSlots - 1);
1275 int32 newIndex = fIconView->IndexAt(newSlot);
1277 if (newIndex < 0) {
1278 // don't have a page full to scroll
1279 newIndex = fManager->GroupList()->CountItems() - 1;
1281 fManager->SwitchToApp(previousIndex, newIndex, true);
1285 frame = fWindow->WindowView()->Frame();
1286 if (fUpScroller) {
1287 BRect hit1(frame.left - 10, frame.top, frame.left,
1288 (frame.top + frame.bottom) / 2);
1289 BRect hit2(frame.right, frame.top, frame.right + 10,
1290 (frame.top + frame.bottom) / 2);
1291 if (hit1.Contains(where) || hit2.Contains(where)) {
1292 // Want to scroll up 1 window
1293 fManager->CycleWindow(false, false);
1297 if (fDownScroller) {
1298 BRect hit1(frame.left - 10, (frame.top + frame.bottom) / 2,
1299 frame.left, frame.bottom);
1300 BRect hit2(frame.right, (frame.top + frame.bottom) / 2,
1301 frame.right + 10, frame.bottom);
1302 if (hit1.Contains(where) || hit2.Contains(where)) {
1303 // Want to scroll down 1 window
1304 fManager->CycleWindow(true, false);
1310 void
1311 TBox::Draw(BRect update)
1313 static const int32 kChildInset = 7;
1314 static const int32 kWedge = 6;
1316 BBox::Draw(update);
1318 // The fancy border around the icon view
1320 BRect bounds = Bounds();
1321 float height = fIconView->Bounds().Height();
1322 float center = (bounds.right + bounds.left) / 2;
1324 BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2);
1325 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
1326 rgb_color white = {255, 255, 255, 255};
1327 rgb_color standardGray = panelColor;
1328 rgb_color veryDarkGray = {128, 128, 128, 255};
1329 rgb_color darkGray = tint_color(panelColor, B_DARKEN_1_TINT);
1331 if (panelColor.Brightness() < 100) {
1332 standardGray = tint_color(panelColor, 0.8);
1333 darkGray = tint_color(panelColor, 0.85);
1334 white = make_color(200, 200, 200, 255);
1335 veryDarkGray = make_color(0, 0, 0, 255);
1338 // Fill the area with dark gray
1339 SetHighColor(darkGray);
1340 box.InsetBy(1, 1);
1341 FillRect(box);
1343 box.InsetBy(-1, -1);
1345 BeginLineArray(50);
1347 // The main frame around the icon view
1348 AddLine(box.LeftTop(), BPoint(center - kWedge, box.top), veryDarkGray);
1349 AddLine(BPoint(center + kWedge, box.top), box.RightTop(), veryDarkGray);
1351 AddLine(box.LeftBottom(), BPoint(center - kWedge, box.bottom),
1352 veryDarkGray);
1353 AddLine(BPoint(center + kWedge, box.bottom), box.RightBottom(),
1354 veryDarkGray);
1355 AddLine(box.LeftBottom() + BPoint(1, 1),
1356 BPoint(center - kWedge, box.bottom + 1), white);
1357 AddLine(BPoint(center + kWedge, box.bottom) + BPoint(0, 1),
1358 box.RightBottom() + BPoint(1, 1), white);
1360 AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray);
1361 AddLine(box.RightTop(), box.RightBottom(), veryDarkGray);
1362 AddLine(box.RightTop() + BPoint(1, 1), box.RightBottom() + BPoint(1, 1),
1363 white);
1365 // downward pointing area at top of frame
1366 BPoint point(center - kWedge, box.top);
1367 AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray);
1368 AddLine(point + BPoint(kWedge, kWedge), BPoint(center + kWedge, point.y),
1369 veryDarkGray);
1371 AddLine(point + BPoint(1, 0), point + BPoint(1, 0)
1372 + BPoint(kWedge - 1, kWedge - 1), white);
1374 AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1),
1375 BPoint(center + kWedge - 1, point.y), darkGray);
1377 BPoint topPoint = point;
1379 // upward pointing area at bottom of frame
1380 point.y = box.bottom;
1381 point.x = center - kWedge;
1382 AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray);
1383 AddLine(point + BPoint(kWedge, -kWedge),
1384 BPoint(center + kWedge, point.y), veryDarkGray);
1386 AddLine(point + BPoint(1, 0),
1387 point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white);
1389 AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)),
1390 BPoint(center + kWedge - 1, point.y), darkGray);
1392 BPoint bottomPoint = point;
1394 EndLineArray();
1396 // fill the downward pointing arrow area
1397 SetHighColor(standardGray);
1398 FillTriangle(topPoint + BPoint(2, 0),
1399 topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2),
1400 BPoint(center + kWedge - 2, topPoint.y));
1402 // fill the upward pointing arrow area
1403 SetHighColor(standardGray);
1404 FillTriangle(bottomPoint + BPoint(2, 0),
1405 bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)),
1406 BPoint(center + kWedge - 2, bottomPoint.y));
1408 DrawIconScrollers(false);
1409 DrawWindowScrollers(false);
1414 void
1415 TBox::DrawIconScrollers(bool force)
1417 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
1418 rgb_color backgroundColor;
1419 rgb_color dark;
1421 if (panelColor.Brightness() > 100) {
1422 backgroundColor = tint_color(panelColor, B_DARKEN_1_TINT);
1423 dark = tint_color(backgroundColor, B_DARKEN_3_TINT);
1424 } else {
1425 backgroundColor = tint_color(panelColor, 0.85);
1426 dark = tint_color(panelColor, B_LIGHTEN_1_TINT);
1429 bool updateLeft = false;
1430 bool updateRight = false;
1432 BRect rect = fIconView->Bounds();
1433 if (rect.left > (kSlotSize * kCenterSlot)) {
1434 updateLeft = true;
1435 fLeftScroller = true;
1436 } else {
1437 fLeftScroller = false;
1438 if (force)
1439 updateLeft = true;
1442 int32 maxIndex = fManager->GroupList()->CountItems() - 1;
1443 // last_frame is in fIconView coordinate space
1444 BRect lastFrame = fIconView->FrameOf(maxIndex);
1446 if (lastFrame.right > rect.right) {
1447 updateRight = true;
1448 fRightScroller = true;
1449 } else {
1450 fRightScroller = false;
1451 if (force)
1452 updateRight = true;
1455 PushState();
1456 SetDrawingMode(B_OP_COPY);
1458 rect = fIconView->Frame();
1459 if (updateLeft) {
1460 BPoint pt1, pt2, pt3;
1461 pt1.x = rect.left - 5;
1462 pt1.y = floorf((rect.bottom + rect.top) / 2);
1463 pt2.x = pt3.x = pt1.x + 3;
1464 pt2.y = pt1.y - 3;
1465 pt3.y = pt1.y + 3;
1467 if (fLeftScroller) {
1468 SetHighColor(dark);
1469 FillTriangle(pt1, pt2, pt3);
1470 } else if (force) {
1471 SetHighColor(backgroundColor);
1472 FillRect(BRect(pt1.x, pt2.y, pt3.x, pt3.y));
1475 if (updateRight) {
1476 BPoint pt1, pt2, pt3;
1477 pt1.x = rect.right + 4;
1478 pt1.y = rintf((rect.bottom + rect.top) / 2);
1479 pt2.x = pt3.x = pt1.x - 4;
1480 pt2.y = pt1.y - 4;
1481 pt3.y = pt1.y + 4;
1483 if (fRightScroller) {
1484 SetHighColor(dark);
1485 FillTriangle(pt1, pt2, pt3);
1486 } else if (force) {
1487 SetHighColor(backgroundColor);
1488 FillRect(BRect(pt3.x, pt2.y, pt1.x, pt3.y));
1492 PopState();
1496 void
1497 TBox::DrawWindowScrollers(bool force)
1499 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
1500 rgb_color backgroundColor;
1501 rgb_color dark;
1503 if (panelColor.Brightness() > 100) {
1504 backgroundColor = tint_color(panelColor, B_DARKEN_1_TINT);
1505 dark = tint_color(backgroundColor, B_DARKEN_2_TINT);
1506 } else {
1507 backgroundColor = panelColor;
1508 dark = tint_color(panelColor, B_LIGHTEN_2_TINT);
1511 bool updateUp = false;
1512 bool updateDown = false;
1514 BRect rect = fWindow->WindowView()->Bounds();
1515 if (rect.top != 0) {
1516 updateUp = true;
1517 fUpScroller = true;
1518 } else {
1519 fUpScroller = false;
1520 if (force)
1521 updateUp = true;
1524 int32 groupIndex = fManager->CurrentIndex();
1525 int32 maxIndex = fManager->CountWindows(groupIndex) - 1;
1527 BRect lastFrame(0, 0, 0, 0);
1528 if (maxIndex >= 0)
1529 lastFrame = fWindow->WindowView()->FrameOf(maxIndex);
1531 if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) {
1532 updateDown = true;
1533 fDownScroller = true;
1534 } else {
1535 fDownScroller = false;
1536 if (force)
1537 updateDown = true;
1540 PushState();
1541 SetDrawingMode(B_OP_COPY);
1543 rect = fWindow->WindowView()->Frame();
1544 rect.InsetBy(-3, 0);
1545 if (updateUp) {
1546 if (fUpScroller) {
1547 SetHighColor(dark);
1548 BPoint pt1, pt2, pt3;
1549 pt1.x = rect.left - 6;
1550 pt1.y = rect.top + 3;
1551 pt2.y = pt3.y = pt1.y + 4;
1552 pt2.x = pt1.x - 4;
1553 pt3.x = pt1.x + 4;
1554 FillTriangle(pt1, pt2, pt3);
1556 pt1.x += rect.Width() + 12;
1557 pt2.x += rect.Width() + 12;
1558 pt3.x += rect.Width() + 12;
1559 FillTriangle(pt1, pt2, pt3);
1560 } else if (force) {
1561 FillRect(BRect(rect.left - 10, rect.top + 3, rect.left - 2,
1562 rect.top + 7), B_SOLID_LOW);
1563 FillRect(BRect(rect.right + 2, rect.top + 3, rect.right + 10,
1564 rect.top + 7), B_SOLID_LOW);
1567 if (updateDown) {
1568 if (fDownScroller) {
1569 SetHighColor(dark);
1570 BPoint pt1, pt2, pt3;
1571 pt1.x = rect.left - 6;
1572 pt1.y = rect.bottom - 3;
1573 pt2.y = pt3.y = pt1.y - 4;
1574 pt2.x = pt1.x - 4;
1575 pt3.x = pt1.x + 4;
1576 FillTriangle(pt1, pt2, pt3);
1578 pt1.x += rect.Width() + 12;
1579 pt2.x += rect.Width() + 12;
1580 pt3.x += rect.Width() + 12;
1581 FillTriangle(pt1, pt2, pt3);
1582 } else if (force) {
1583 FillRect(BRect(rect.left - 10, rect.bottom - 7, rect.left - 2,
1584 rect.bottom - 3), B_SOLID_LOW);
1585 FillRect(BRect(rect.right + 2, rect.bottom - 7, rect.right + 10,
1586 rect.bottom - 3), B_SOLID_LOW);
1590 PopState();
1591 Sync();
1595 // #pragma mark -
1598 TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager* manager)
1600 BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL,
1601 B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES),
1602 fManager(manager),
1603 fHairTrigger(true)
1605 BRect rect = frame;
1606 rect.OffsetTo(B_ORIGIN);
1607 rect.InsetBy(kHorizontalMargin, 0);
1608 rect.top = kVerticalMargin;
1609 rect.bottom = rect.top + kSlotSize - 1;
1611 fIconView = new TIconView(rect, manager, this);
1613 rect.top = rect.bottom + (kVerticalMargin * 1 + 4);
1614 rect.InsetBy(9, 0);
1616 fWindowView = new TWindowView(rect, manager, this);
1617 fWindowView->ResizeToPreferred();
1619 fTopView = new TBox(Bounds(), fManager, this, fIconView);
1620 AddChild(fTopView);
1621 fTopView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1622 fTopView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1623 fTopView->SetHighUIColor(B_PANEL_TEXT_COLOR);
1625 SetPulseRate(0);
1626 fTopView->AddChild(fIconView);
1627 fTopView->AddChild(fWindowView);
1629 if (be_plain_font->Size() != 12) {
1630 float sizeDelta = be_plain_font->Size() - 12;
1631 ResizeBy(0, sizeDelta);
1636 TSwitcherWindow::~TSwitcherWindow()
1641 void
1642 TSwitcherWindow::MessageReceived(BMessage* message)
1644 switch (message->what) {
1645 case B_UNMAPPED_KEY_DOWN:
1646 case B_KEY_DOWN:
1648 int32 repeats = 0;
1649 if (message->FindInt32("be:key_repeat", &repeats) == B_OK
1650 && (fSkipKeyRepeats || (repeats % 6) != 0))
1651 break;
1653 // The first actual key press let's us listening to repeated keys
1654 fSkipKeyRepeats = false;
1656 uint32 rawChar;
1657 uint32 modifiers;
1658 message->FindInt32("raw_char", 0, (int32*)&rawChar);
1659 message->FindInt32("modifiers", 0, (int32*)&modifiers);
1660 DoKey(rawChar, modifiers);
1661 break;
1664 default:
1665 BWindow::MessageReceived(message);
1670 void
1671 TSwitcherWindow::Redraw(int32 index)
1673 BRect frame = fIconView->FrameOf(index);
1674 frame.right = fIconView->Bounds().right;
1675 fIconView->Invalidate(frame);
1679 void
1680 TSwitcherWindow::DoKey(uint32 key, uint32 modifiers)
1682 bool forward = ((modifiers & B_SHIFT_KEY) == 0);
1684 switch (key) {
1685 case B_RIGHT_ARROW:
1686 fManager->CycleApp(true, false);
1687 break;
1689 case B_LEFT_ARROW:
1690 case '1':
1691 fManager->CycleApp(false, false);
1692 break;
1694 case B_UP_ARROW:
1695 fManager->CycleWindow(false, false);
1696 break;
1698 case B_DOWN_ARROW:
1699 fManager->CycleWindow(true, false);
1700 break;
1702 case B_TAB:
1703 fManager->CycleApp(forward, false);
1704 break;
1706 case B_ESCAPE:
1707 fManager->Stop(false, 0);
1708 break;
1710 case B_SPACE:
1711 case B_ENTER:
1712 fManager->Stop(true, modifiers);
1713 break;
1715 case 'q':
1716 case 'Q':
1717 fManager->QuitApp();
1718 break;
1720 case 'h':
1721 case 'H':
1722 fManager->HideApp();
1723 break;
1725 #if _ALLOW_STICKY_
1726 case 's':
1727 case 'S':
1728 if (fHairTrigger) {
1729 SetLook(B_TITLED_WINDOW_LOOK);
1730 fHairTrigger = false;
1731 } else {
1732 SetLook(B_MODAL_WINDOW_LOOK);
1733 fHairTrigger = true;
1735 break;
1736 #endif
1741 bool
1742 TSwitcherWindow::QuitRequested()
1744 ((TBarApp*)be_app)->Settings()->switcherLoc = Frame().LeftTop();
1745 fManager->Stop(false, 0);
1746 return false;
1750 void
1751 TSwitcherWindow::WindowActivated(bool state)
1753 if (state)
1754 fManager->Unblock();
1758 void
1759 TSwitcherWindow::Update(int32 prev, int32 current, int32 previousSlot,
1760 int32 currentSlot, bool forward)
1762 if (!IsHidden())
1763 fIconView->Update(prev, current, previousSlot, currentSlot, forward);
1764 else
1765 fIconView->CenterOn(current);
1767 fWindowView->UpdateGroup(current, 0);
1771 void
1772 TSwitcherWindow::Hide()
1774 fIconView->Hiding();
1775 SetPulseRate(0);
1776 BWindow::Hide();
1780 void
1781 TSwitcherWindow::Show()
1783 fHairTrigger = true;
1784 fSkipKeyRepeats = true;
1785 fIconView->Showing();
1786 SetPulseRate(100000);
1787 SetLook(B_MODAL_WINDOW_LOOK);
1788 BWindow::Show();
1792 TBox*
1793 TSwitcherWindow::TopView()
1795 return fTopView;
1799 bool
1800 TSwitcherWindow::HairTrigger()
1802 return fHairTrigger;
1806 inline int32
1807 TSwitcherWindow::SlotOf(int32 i)
1809 return fIconView->SlotOf(i);
1813 inline TIconView*
1814 TSwitcherWindow::IconView()
1816 return fIconView;
1820 inline TWindowView*
1821 TSwitcherWindow::WindowView()
1823 return fWindowView;
1827 // #pragma mark -
1830 TIconView::TIconView(BRect frame, TSwitchManager* manager,
1831 TSwitcherWindow* switcherWindow)
1832 : BView(frame, "main_view", B_FOLLOW_NONE,
1833 B_WILL_DRAW | B_PULSE_NEEDED),
1834 fAutoScrolling(false),
1835 fSwitcher(switcherWindow),
1836 fManager(manager)
1838 BRect rect(0, 0, kSlotSize - 1, kSlotSize - 1);
1840 fOffView = new BView(rect, "off_view", B_FOLLOW_NONE, B_WILL_DRAW);
1841 fOffBitmap = new BBitmap(rect, B_RGB32, true);
1842 fOffBitmap->AddChild(fOffView);
1844 fCurrentSmall = new BBitmap(BRect(0, 0, 15, 15), kIconFormat);
1845 fCurrentLarge = new BBitmap(BRect(0, 0, 31, 31), kIconFormat);
1849 TIconView::~TIconView()
1851 delete fCurrentSmall;
1852 delete fCurrentLarge;
1853 delete fOffBitmap;
1857 void
1858 TIconView::KeyDown(const char* /*bytes*/, int32 /*numBytes*/)
1863 void
1864 TIconView::CacheIcons(TTeamGroup* teamGroup)
1866 const BBitmap* bitmap = teamGroup->SmallIcon();
1867 ASSERT(bitmap);
1868 fCurrentSmall->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0,
1869 bitmap->ColorSpace());
1871 bitmap = teamGroup->LargeIcon();
1872 ASSERT(bitmap);
1873 fCurrentLarge->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0,
1874 bitmap->ColorSpace());
1878 void
1879 TIconView::AnimateIcon(BBitmap* startIcon, BBitmap* endIcon)
1881 BRect centerRect(kCenterSlot * kSlotSize, 0,
1882 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1);
1883 BRect startIconBounds = startIcon->Bounds();
1884 BRect bounds = Bounds();
1885 float width = startIconBounds.Width();
1886 int32 amount = (width < 20) ? -2 : 2;
1888 // center the starting icon inside of centerRect
1889 float off = (centerRect.Width() - width) / 2;
1890 startIconBounds.OffsetTo(BPoint(off, off));
1892 // scroll the centerRect to correct location
1893 centerRect.OffsetBy(bounds.left, 0);
1895 BRect destRect = fOffBitmap->Bounds();
1896 // scroll to the centerRect location
1897 destRect.OffsetTo(centerRect.left, 0);
1898 // center the destRect inside of centerRect.
1899 off = (centerRect.Width() - destRect.Width()) / 2;
1900 destRect.OffsetBy(BPoint(off, off));
1902 fOffBitmap->Lock();
1903 rgb_color backgroundColor = ui_color(B_PANEL_BACKGROUND_COLOR);
1904 if (backgroundColor.Brightness() > 100)
1905 fOffView->SetHighColor(tint_color(backgroundColor, B_DARKEN_1_TINT));
1906 else
1907 fOffView->SetHighColor(tint_color(backgroundColor, 0.85));
1909 for (int i = 0; i < 2; i++) {
1910 startIconBounds.InsetBy(amount, amount);
1911 snooze(20000);
1912 fOffView->SetDrawingMode(B_OP_COPY);
1913 fOffView->FillRect(fOffView->Bounds());
1914 fOffView->SetDrawingMode(B_OP_ALPHA);
1915 fOffView->DrawBitmap(startIcon, startIconBounds);
1916 fOffView->Sync();
1917 DrawBitmap(fOffBitmap, destRect);
1919 for (int i = 0; i < 2; i++) {
1920 startIconBounds.InsetBy(amount, amount);
1921 snooze(20000);
1922 fOffView->SetDrawingMode(B_OP_COPY);
1923 fOffView->FillRect(fOffView->Bounds());
1924 fOffView->SetDrawingMode(B_OP_ALPHA);
1925 fOffView->DrawBitmap(endIcon, startIconBounds);
1926 fOffView->Sync();
1927 DrawBitmap(fOffBitmap, destRect);
1930 fOffBitmap->Unlock();
1934 void
1935 TIconView::Update(int32, int32 current, int32 previousSlot, int32 currentSlot,
1936 bool forward)
1938 // Animate the shrinking of the currently centered icon.
1939 AnimateIcon(fCurrentLarge, fCurrentSmall);
1941 int32 nslots = abs(previousSlot - currentSlot);
1942 int32 stepSize = kScrollStep;
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;
1957 int32 scrollValue = forward ? stepSize : -stepSize;
1958 int32 total = 0;
1960 fAutoScrolling = true;
1961 while (total < (nslots * kSlotSize)) {
1962 ScrollBy(scrollValue, 0);
1963 snooze(1000);
1964 total += stepSize;
1965 Window()->UpdateIfNeeded();
1967 fAutoScrolling = false;
1969 TTeamGroup* teamGroup = (TTeamGroup*)fManager->GroupList()->ItemAt(current);
1970 ASSERT(teamGroup);
1971 CacheIcons(teamGroup);
1973 // Animate the expansion of the currently centered icon
1974 AnimateIcon(fCurrentSmall, fCurrentLarge);
1978 void
1979 TIconView::CenterOn(int32 index)
1981 BRect rect = FrameOf(index);
1982 ScrollTo(rect.left - (kCenterSlot * kSlotSize), 0);
1986 int32
1987 TIconView::ItemAtPoint(BPoint point) const
1989 return IndexAt((int32)(point.x / kSlotSize) - kCenterSlot);
1993 void
1994 TIconView::ScrollTo(BPoint where)
1996 BView::ScrollTo(where);
1997 fSwitcher->TopView()->DrawIconScrollers(true);
2001 int32
2002 TIconView::IndexAt(int32 slot) const
2004 if (slot < 0 || slot >= fManager->GroupList()->CountItems())
2005 return -1;
2007 return slot;
2011 int32
2012 TIconView::SlotOf(int32 index) const
2014 BRect rect = FrameOf(index);
2016 return (int32)(rect.left / kSlotSize) - kCenterSlot;
2020 BRect
2021 TIconView::FrameOf(int32 index) const
2023 int32 visible = index + kCenterSlot;
2024 // first few slots in view are empty
2026 return BRect(visible * kSlotSize, 0, (visible + 1) * kSlotSize - 1,
2027 kSlotSize - 1);
2031 void
2032 TIconView::DrawTeams(BRect update)
2034 float tint = B_NO_TINT;
2035 rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
2037 if (panelColor.Brightness() < 100)
2038 tint = 0.85;
2039 else
2040 tint = B_DARKEN_1_TINT;
2042 SetHighUIColor(B_PANEL_BACKGROUND_COLOR, tint);
2043 SetLowUIColor(ViewUIColor(), tint);
2045 FillRect(update);
2046 int32 mainIndex = fManager->CurrentIndex();
2047 BList* list = fManager->GroupList();
2048 int32 count = list->CountItems();
2050 BRect rect(kCenterSlot * kSlotSize, 0,
2051 (kCenterSlot + 1) * kSlotSize - 1, kSlotSize - 1);
2053 for (int32 i = 0; i < count; i++) {
2054 TTeamGroup* teamGroup = (TTeamGroup*)list->ItemAt(i);
2055 if (rect.Intersects(update) && teamGroup) {
2056 SetDrawingMode(B_OP_ALPHA);
2057 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
2059 teamGroup->Draw(this, rect, !fAutoScrolling && (i == mainIndex));
2061 if (i == mainIndex)
2062 CacheIcons(teamGroup);
2064 SetDrawingMode(B_OP_COPY);
2066 rect.OffsetBy(kSlotSize, 0);
2071 void
2072 TIconView::Draw(BRect update)
2074 DrawTeams(update);
2078 void
2079 TIconView::MouseDown(BPoint where)
2081 int32 index = ItemAtPoint(where);
2082 if (index >= 0) {
2083 int32 previousIndex = fManager->CurrentIndex();
2084 int32 previousSlot = fManager->CurrentSlot();
2085 int32 currentSlot = SlotOf(index);
2086 fManager->SwitchToApp(previousIndex, index, (currentSlot
2087 > previousSlot));
2092 void
2093 TIconView::Pulse()
2095 uint32 modifiersKeys = modifiers();
2096 if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) {
2097 fManager->Stop(true, modifiersKeys);
2098 return;
2101 if (!fSwitcher->HairTrigger()) {
2102 uint32 buttons;
2103 BPoint point;
2104 GetMouse(&point, &buttons);
2105 if (buttons != 0) {
2106 point = ConvertToScreen(point);
2107 if (!Window()->Frame().Contains(point))
2108 fManager->Stop(false, 0);
2114 void
2115 TIconView::Showing()
2120 void
2121 TIconView::Hiding()
2123 ScrollTo(B_ORIGIN);
2127 // #pragma mark -
2130 TWindowView::TWindowView(BRect rect, TSwitchManager* manager,
2131 TSwitcherWindow* window)
2132 : BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
2133 fCurrentToken(-1),
2134 fItemHeight(-1),
2135 fSwitcher(window),
2136 fManager(manager)
2138 SetFont(be_plain_font);
2142 void
2143 TWindowView::AttachedToWindow()
2145 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
2149 void
2150 TWindowView::ScrollTo(BPoint where)
2152 BView::ScrollTo(where);
2153 fSwitcher->TopView()->DrawWindowScrollers(true);
2157 BRect
2158 TWindowView::FrameOf(int32 index) const
2160 return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1);
2164 void
2165 TWindowView::GetPreferredSize(float* _width, float* _height)
2167 font_height fh;
2168 be_plain_font->GetHeight(&fh);
2169 fItemHeight = (int32) fh.ascent + fh.descent;
2171 // top & bottom margin
2172 fItemHeight = fItemHeight + 3 + 3;
2174 // want fItemHeight to be divisible by kWindowScrollSteps.
2175 fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps)
2176 / kWindowScrollSteps) * kWindowScrollSteps;
2178 *_height = fItemHeight;
2180 // leave width alone
2181 *_width = Bounds().Width();
2185 void
2186 TWindowView::ShowIndex(int32 newIndex)
2188 // convert index to scroll location
2189 BPoint point(0, newIndex * fItemHeight);
2190 BRect bounds = Bounds();
2192 int32 groupIndex = fManager->CurrentIndex();
2193 TTeamGroup* teamGroup
2194 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex);
2195 if (teamGroup == NULL)
2196 return;
2198 window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex);
2199 if (windowInfo == NULL)
2200 return;
2202 fCurrentToken = windowInfo->server_token;
2203 free(windowInfo);
2205 if (bounds.top == point.y)
2206 return;
2208 int32 oldIndex = (int32) (bounds.top / fItemHeight);
2210 int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps);
2211 int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize;
2212 int32 total = 0;
2213 int32 nslots = abs(newIndex - oldIndex);
2215 while (total < (nslots * (int32)fItemHeight)) {
2216 ScrollBy(0, scrollValue);
2217 snooze(10000);
2218 total += stepSize;
2219 Window()->UpdateIfNeeded();
2224 void
2225 TWindowView::Draw(BRect update)
2227 int32 groupIndex = fManager->CurrentIndex();
2228 TTeamGroup* teamGroup
2229 = (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex);
2231 if (teamGroup == NULL)
2232 return;
2234 BRect bounds = Bounds();
2235 int32 windowIndex = (int32) (bounds.top / fItemHeight);
2236 BRect windowRect = bounds;
2238 windowRect.top = windowIndex * fItemHeight;
2239 windowRect.bottom = (windowIndex + 1) * fItemHeight - 1;
2241 for (int32 i = 0; i < 3; i++) {
2242 if (!update.Intersects(windowRect)) {
2243 windowIndex++;
2244 windowRect.OffsetBy(0, fItemHeight);
2245 continue;
2248 // is window in current workspace?
2250 bool local = true;
2251 bool minimized = false;
2252 BString title;
2254 client_window_info* windowInfo
2255 = fManager->WindowInfo(groupIndex, windowIndex);
2256 if (windowInfo != NULL) {
2257 if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0)
2258 title << teamGroup->Name() << ": " << windowInfo->name;
2259 else
2260 title = teamGroup->Name();
2262 int32 currentWorkspace = current_workspace();
2263 if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0)
2264 local = false;
2266 minimized = windowInfo->is_mini;
2267 free(windowInfo);
2268 } else
2269 title = teamGroup->Name();
2271 if (!title.Length())
2272 return;
2274 float stringWidth = StringWidth(title.String());
2275 float maxWidth = bounds.Width() - (14 + 5);
2277 if (stringWidth > maxWidth) {
2278 // window title is too long, need to truncate
2279 TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth);
2280 stringWidth = maxWidth;
2283 BPoint point((bounds.Width() - (stringWidth + 14 + 5)) / 2,
2284 windowRect.bottom - 4);
2285 BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2);
2286 SetDrawingMode(B_OP_OVER);
2287 const BBitmap* bitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE,
2288 minimized ? R_WindowHiddenIcon : R_WindowShownIcon);
2289 p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2;
2290 DrawBitmap(bitmap, p);
2292 if (!local) {
2293 rgb_color color = ui_color(B_PANEL_BACKGROUND_COLOR);
2294 if (color.Brightness() > 100)
2295 SetHighColor(tint_color(color, B_DARKEN_4_TINT));
2296 else
2297 SetHighColor(tint_color(color, B_LIGHTEN_1_TINT));
2299 p.x -= 8;
2300 p.y += 4;
2301 StrokeLine(p + BPoint(2, 2), p + BPoint(2, 2));
2302 StrokeLine(p + BPoint(4, 2), p + BPoint(6, 2));
2304 StrokeLine(p + BPoint(0, 5), p + BPoint(0, 5));
2305 StrokeLine(p + BPoint(2, 5), p + BPoint(6, 5));
2307 StrokeLine(p + BPoint(1, 8), p + BPoint(1, 8));
2308 StrokeLine(p + BPoint(3, 8), p + BPoint(6, 8));
2311 point.x += 21;
2312 MovePenTo(point);
2314 SetHighUIColor(B_PANEL_TEXT_COLOR);
2315 DrawString(title.String());
2316 SetDrawingMode(B_OP_COPY);
2318 windowIndex++;
2319 windowRect.OffsetBy(0, fItemHeight);
2324 void
2325 TWindowView::UpdateGroup(int32 , int32 windowIndex)
2327 ScrollTo(0, windowIndex * fItemHeight);
2328 Invalidate(Bounds());
2332 void
2333 TWindowView::Pulse()
2335 // If selected window went away then reset to first window
2336 window_info *windowInfo = get_window_info(fCurrentToken);
2337 if (windowInfo == NULL) {
2338 Invalidate();
2339 ShowIndex(0);
2340 } else
2341 free(windowInfo);