#include "Switcher.h"
#include <float.h>
#include <stdlib.h>
#include <strings.h>
#include <Bitmap.h>
#include <ControlLook.h>
#include <Debug.h>
#include <Font.h>
#include <LayoutUtils.h>
#include <Mime.h>
#include <Node.h>
#include <NodeInfo.h>
#include <Roster.h>
#include <Screen.h>
#include <String.h>
#include <WindowInfo.h>
#include "BarApp.h"
#include "ResourceSet.h"
#include "WindowMenuItem.h"
#include "icons.h"
#include "tracker_private.h"
#define _ALLOW_STICKY_ 0
class TTeamGroup {
public:
TTeamGroup();
TTeamGroup(BList* teams, uint32 flags, char* name,
const char* signature);
virtual ~TTeamGroup();
void Draw(BView* view, BRect bounds, bool main);
BList* TeamList() const
{ return fTeams; }
const char* Name() const
{ return fName; }
const char* Signature() const
{ return fSignature; }
uint32 Flags() const
{ return fFlags; }
const BBitmap* SmallIcon() const { return fSmallIcon; }
const BBitmap* LargeIcon() const { return fLargeIcon; }
void CacheTeamIcons(int32 small, int32 large);
private:
BList* fTeams;
uint32 fFlags;
char fSignature[B_MIME_TYPE_LENGTH];
char* fName;
BBitmap* fSmallIcon;
BBitmap* fLargeIcon;
};
class TSwitcherWindow : public BWindow {
public:
TSwitcherWindow(BRect frame,
TSwitchManager* manager);
virtual ~TSwitcherWindow();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* message);
virtual void ScreenChanged(BRect screenFrame, color_space);
virtual void Show();
virtual void Hide();
virtual void WindowActivated(bool state);
virtual void WorkspaceActivated(int32, bool active);
void DoKey(uint32 key, uint32 modifiers);
TIconView* IconView();
TWindowView* WindowView();
TBox* TopView();
bool HairTrigger();
void Update(int32 previous, int32 current,
int32 prevSlot, int32 currentSlot,
bool forward);
int32 SlotOf(int32);
void Redraw(int32 index);
protected:
void CenterWindow(BRect screenFrame, BSize);
private:
TSwitchManager* fManager;
TIconView* fIconView;
TBox* fTopView;
TWindowView* fWindowView;
bool fHairTrigger;
bool fSkipKeyRepeats;
};
class TWindowView : public BView {
public:
TWindowView(BRect frame, TSwitchManager* manager,
TSwitcherWindow* switcher);
void UpdateGroup(int32 groupIndex, int32 windowIndex);
virtual void AttachedToWindow();
virtual void Draw(BRect update);
virtual void Pulse();
virtual void GetPreferredSize(float* w, float* h);
void ScrollTo(float x, float y)
{
ScrollTo(BPoint(x, y));
}
virtual void ScrollTo(BPoint where);
void ShowIndex(int32 windex);
BRect FrameOf(int32 index) const;
private:
int32 fCurrentToken;
float fItemHeight;
TSwitcherWindow* fSwitcher;
TSwitchManager* fManager;
};
class TIconView : public BView {
public:
TIconView(BRect frame, TSwitchManager* manager,
TSwitcherWindow* switcher);
virtual ~TIconView();
void Showing();
void Hiding();
virtual void KeyDown(const char* bytes, int32 numBytes);
virtual void Pulse();
virtual void MouseDown(BPoint point);
virtual void Draw(BRect updateRect);
void ScrollTo(float x, float y)
{ ScrollTo(BPoint(x, y)); }
virtual void ScrollTo(BPoint where);
void Update(int32 previous, int32 current,
int32 previousSlot, int32 currentSlot,
bool forward);
void DrawTeams(BRect update);
int32 SlotOf(int32) const;
BRect FrameOf(int32) const;
int32 ItemAtPoint(BPoint) const;
int32 IndexAt(int32 slot) const;
void CenterOn(int32 index);
private:
void AnimateIcon(const BBitmap* start, const BBitmap* end);
bool fAutoScrolling;
TSwitcherWindow* fSwitcher;
TSwitchManager* fManager;
BBitmap* fOffBitmap;
BView* fOffView;
};
class TBox : public BBox {
public:
TBox(BRect bounds, TSwitchManager* manager,
TSwitcherWindow* window, TIconView* iconView);
virtual void Draw(BRect update);
virtual void AllAttached();
virtual void DrawIconScrollers(bool force);
virtual void DrawWindowScrollers(bool force);
virtual void MouseDown(BPoint where);
private:
TSwitchManager* fManager;
TSwitcherWindow* fWindow;
TIconView* fIconView;
BRect fCenter;
bool fLeftScroller;
bool fRightScroller;
bool fUpScroller;
bool fDownScroller;
};
static const int32 kHorizontalMargin = 11;
static const int32 kVerticalMargin = 10;
static const int32 kTeamIconSize = 48;
static const int32 kWindowScrollSteps = 3;
static int32
LowBitIndex(uint32 value)
{
int32 result = 0;
int32 bitMask = 1;
if (value == 0)
return -1;
while (result < 32 && (value & bitMask) == 0) {
result++;
bitMask = bitMask << 1;
}
return result;
}
inline bool
IsVisibleInCurrentWorkspace(const window_info* windowInfo)
{
return windowInfo->layer > 2;
}
bool
IsKeyDown(int32 key)
{
key_info keyInfo;
get_key_info(&keyInfo);
return (keyInfo.key_states[key >> 3] & (1 << ((7 - key) & 7))) != 0;
}
bool
IsWindowOK(const window_info* windowInfo)
{
if (windowInfo->feel != _STD_W_TYPE_)
return false;
if (windowInfo->is_mini)
return true;
return windowInfo->show_hide_level <= 0;
}
int
SmartStrcmp(const char* s1, const char* s2)
{
if (strcasecmp(s1, s2) == 0)
return 0;
while (*s1 && *s2) {
if ((*s1 == ' ') || (*s1 == '_')) {
s1++;
continue;
}
if ((*s2 == ' ') || (*s2 == '_')) {
s2++;
continue;
}
if (*s1 != *s2) {
return 1;
}
s1++;
s2++;
}
if (*s1)
return 1;
if (*s2)
return 1;
return 0;
}
TTeamGroup::TTeamGroup()
:
fTeams(NULL),
fFlags(0),
fName(NULL),
fSmallIcon(NULL),
fLargeIcon(NULL)
{
fSignature[0] = '\0';
}
TTeamGroup::TTeamGroup(BList* teams, uint32 flags, char* name,
const char* signature)
:
fTeams(teams),
fFlags(flags),
fName(name),
fSmallIcon(NULL),
fLargeIcon(NULL)
{
strlcpy(fSignature, signature, sizeof(fSignature));
}
TTeamGroup::~TTeamGroup()
{
delete fTeams;
free(fName);
}
void
TTeamGroup::Draw(BView* view, BRect bounds, bool main)
{
BRect largeRect = fLargeIcon->Bounds();
largeRect.OffsetTo(bounds.LeftTop());
int32 offset = (bounds.IntegerWidth()
- largeRect.IntegerWidth()) / 2;
largeRect.OffsetBy(offset, offset);
if (main)
view->DrawBitmap(fLargeIcon, largeRect);
else {
BRect smallRect = fSmallIcon->Bounds();
smallRect.OffsetTo(largeRect.LeftTop());
int32 offset = (largeRect.IntegerWidth()
- smallRect.IntegerWidth()) / 2;
smallRect.OffsetBy(BPoint(offset, offset));
view->DrawBitmap(fSmallIcon, smallRect);
}
}
void
TTeamGroup::CacheTeamIcons(int32 smallIconSize, int32 largeIconSize)
{
TBarApp* app = static_cast<TBarApp*>(be_app);
int32 teamCount = TeamList()->CountItems();
for (int32 index = 0; index < teamCount; index++) {
team_id team = (addr_t)TeamList()->ItemAt(index);
fSmallIcon = app->FetchTeamIcon(team, smallIconSize);
fLargeIcon = app->FetchTeamIcon(team, largeIconSize);
}
}
TSwitchManager::TSwitchManager()
: BHandler("SwitchManager"),
fMainMonitor(create_sem(1, "main_monitor")),
fBlock(false),
fSkipUntil(0),
fLastSwitch(0),
fQuickSwitchIndex(-1),
fQuickSwitchWindow(-1),
fGroupList(10),
fCurrentIndex(0),
fCurrentSlot(0),
fWindowID(-1)
{
fLargeIconSize = kTeamIconSize;
fSmallIconSize = kTeamIconSize / 2;
int32 composed = be_control_look->ComposeIconSize(kTeamIconSize)
.IntegerWidth() + 1;
fSlotSize = (composed + composed / 4) & ~3u;
fScrollStep = fSlotSize / 2;
fSlotCount = 7;
fCenterSlot = 3;
font_height plainFontHeight;
be_plain_font->GetHeight(&plainFontHeight);
float plainHeight = plainFontHeight.ascent + plainFontHeight.descent;
BRect rect(0, 0,
(fSlotSize * fSlotCount) - 1 + (2 * kHorizontalMargin),
fSlotSize + (4 * kVerticalMargin) + plainHeight);
fWindow = new TSwitcherWindow(rect, this);
fWindow->AddHandler(this);
fWindow->Lock();
fWindow->Run();
BList tmpList;
TBarApp::Subscribe(BMessenger(this), &tmpList);
for (int32 i = 0; ; i++) {
BarTeamInfo* barTeamInfo = (BarTeamInfo*)tmpList.ItemAt(i);
if (!barTeamInfo)
break;
TTeamGroup* group = new TTeamGroup(barTeamInfo->teams,
barTeamInfo->flags, barTeamInfo->name, barTeamInfo->sig);
group->CacheTeamIcons(fSmallIconSize, fLargeIconSize);
fGroupList.AddItem(group);
barTeamInfo->teams = NULL;
barTeamInfo->name = NULL;
delete barTeamInfo;
}
fWindow->Unlock();
}
TSwitchManager::~TSwitchManager()
{
for (int32 i = fGroupList.CountItems() - 1; i >= 0; i--) {
TTeamGroup* teamInfo = static_cast<TTeamGroup*>(fGroupList.ItemAt(i));
delete teamInfo;
}
}
void
TSwitchManager::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SOME_APP_QUIT:
{
team_id teamID;
int i = 0;
TTeamGroup* tinfo;
message->FindInt32("team", &teamID);
while ((tinfo = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) {
if (tinfo->TeamList()->HasItem((void*)(addr_t)teamID)) {
fGroupList.RemoveItem(i);
fWindow->Redraw(i);
if (i <= fCurrentIndex) {
fCurrentIndex--;
CycleApp(true);
}
delete tinfo;
break;
}
i++;
}
break;
}
case B_SOME_APP_LAUNCHED:
{
const char* name;
uint32 flags;
const char* signature;
BList* teams;
if (message->FindString("sig", &signature) != B_OK)
break;
if (message->FindInt32("flags", (int32*)&flags) != B_OK)
break;
if (message->FindString("name", &name) != B_OK)
break;
if (message->FindPointer("teams", (void**)&teams) != B_OK)
break;
TTeamGroup* group = new TTeamGroup(teams, flags, strdup(name),
signature);
group->CacheTeamIcons(fSmallIconSize, fLargeIconSize);
fGroupList.AddItem(group);
fWindow->Redraw(fGroupList.CountItems() - 1);
break;
}
case kAddTeam:
{
team_id team = message->FindInt32("team");
const char* signature = message->FindString("sig");
int32 teamCount = fGroupList.CountItems();
for (int32 index = 0; index < teamCount; index++) {
TTeamGroup* group = (TTeamGroup*)fGroupList.ItemAt(index);
ASSERT(group);
if (strcasecmp(group->Signature(), signature) == 0) {
if (!group->TeamList()->HasItem((void*)(addr_t)team)) {
group->CacheTeamIcons(fSmallIconSize, fLargeIconSize);
group->TeamList()->AddItem((void*)(addr_t)team);
}
break;
}
}
break;
}
case kRemoveTeam:
{
team_id team = message->FindInt32("team");
int32 teamCount = fGroupList.CountItems();
for (int32 index = 0; index < teamCount; index++) {
TTeamGroup* group = (TTeamGroup*)fGroupList.ItemAt(index);
ASSERT(group);
if (group->TeamList()->HasItem((void*)(addr_t)team)) {
group->TeamList()->RemoveItem((void*)(addr_t)team);
break;
}
}
break;
}
case 'TASK':
{
bigtime_t time;
message->FindInt64("when", (int64*)&time);
if (time < fSkipUntil)
break;
status_t status = acquire_sem_etc(fMainMonitor, 1, B_TIMEOUT, 0);
if (status != B_OK) {
if (!fWindow->IsHidden() && !fBlock) {
if (time > fSkipUntil) {
uint32 modifiers = 0;
message->FindInt32("modifiers", (int32*)&modifiers);
int32 key = 0;
message->FindInt32("key", &key);
Process((modifiers & B_SHIFT_KEY) == 0, key == 0x11);
}
}
} else
MainEntry(message);
break;
}
default:
break;
}
}
void
TSwitchManager::_SortApps()
{
team_id* teams;
int32 count;
if (BPrivate::get_application_order(current_workspace(), &teams, &count)
!= B_OK)
return;
BList groups;
if (!groups.AddList(&fGroupList)) {
free(teams);
return;
}
fGroupList.MakeEmpty();
for (int32 i = 0; i < count; i++) {
TTeamGroup* info = NULL;
for (int32 j = 0; (info = (TTeamGroup*)groups.ItemAt(j)) != NULL; j++) {
if (info->TeamList()->HasItem((void*)(addr_t)teams[i])) {
groups.RemoveItem(j);
break;
}
}
if (info != NULL)
fGroupList.AddItem(info);
}
fGroupList.AddList(&groups);
free(teams);
}
void
TSwitchManager::MainEntry(BMessage* message)
{
bigtime_t now = system_time();
bigtime_t timeout = now + 180000;
app_info appInfo;
be_roster->GetActiveAppInfo(&appInfo);
bool resetQuickSwitch = false;
if (now > fLastSwitch + 400000) {
_SortApps();
resetQuickSwitch = true;
}
fLastSwitch = now;
int32 index;
fCurrentIndex = FindTeam(appInfo.team, &index) != NULL ? index : 0;
if (resetQuickSwitch) {
fQuickSwitchIndex = fCurrentIndex;
fQuickSwitchWindow = fCurrentWindow;
}
int32 key;
message->FindInt32("key", (int32*)&key);
uint32 modifierKeys = 0;
while (system_time() < timeout) {
modifierKeys = modifiers();
if (!IsKeyDown(key)) {
QuickSwitch(message);
return;
}
if ((modifierKeys & B_CONTROL_KEY) == 0) {
QuickSwitch(message);
return;
}
snooze(20000);
}
Process((modifierKeys & B_SHIFT_KEY) == 0, key == 0x11);
}
void
TSwitchManager::Stop(bool do_action, uint32)
{
fWindow->Hide();
if (do_action)
ActivateApp(true, true);
release_sem(fMainMonitor);
}
TTeamGroup*
TSwitchManager::FindTeam(team_id teamID, int32* index)
{
int i = 0;
TTeamGroup* info;
while ((info = (TTeamGroup*)fGroupList.ItemAt(i)) != NULL) {
if (info->TeamList()->HasItem((void*)(addr_t)teamID)) {
*index = i;
return info;
}
i++;
}
return NULL;
}
void
TSwitchManager::Process(bool forward, bool byWindow)
{
bool hidden = false;
if (fWindow->Lock()) {
hidden = fWindow->IsHidden();
fWindow->Unlock();
}
if (byWindow) {
if (hidden)
SwitchToApp(fCurrentIndex, fCurrentIndex, forward);
CycleWindow(forward, true);
} else
CycleApp(forward, false);
if (hidden) {
fBlock = true;
if (fWindow->Lock()) {
BRect screenFrame = BScreen(B_MAIN_SCREEN_ID).Frame();
BRect windowFrame = fWindow->Frame();
if (!screenFrame.Contains(windowFrame)) {
BPoint point((screenFrame.left + screenFrame.right) / 2,
(screenFrame.top + screenFrame.bottom) / 2);
point.x -= (windowFrame.Width() / 2);
point.y -= (windowFrame.Height() / 2);
fWindow->MoveTo(point);
}
fWindow->Show();
fWindow->Unlock();
}
}
}
void
TSwitchManager::QuickSwitch(BMessage* message)
{
uint32 modifiers = 0;
message->FindInt32("modifiers", (int32*)&modifiers);
int32 key = 0;
message->FindInt32("key", &key);
team_id team;
if (message->FindInt32("team", &team) == B_OK) {
bool forward = (modifiers & B_SHIFT_KEY) == 0;
if (key == 0x11) {
SwitchWindow(team, forward, true);
} else {
if (fQuickSwitchIndex >= 0) {
int32 current = fCurrentIndex;
SwitchToApp(current, fQuickSwitchIndex, false);
ActivateApp(false, false);
fCurrentIndex = current;
}
CycleApp(forward, true);
}
}
release_sem(fMainMonitor);
}
void
TSwitchManager::CycleWindow(bool forward, bool wrap)
{
int32 max = CountWindows(fCurrentIndex);
int32 prev = fCurrentWindow;
int32 next = fCurrentWindow;
if (forward) {
next++;
if (next >= max) {
if (!wrap)
return;
next = 0;
}
} else {
next--;
if (next < 0) {
if (!wrap)
return;
next = max - 1;
}
}
fCurrentWindow = next;
if (fCurrentWindow != prev)
fWindow->WindowView()->ShowIndex(fCurrentWindow);
}
void
TSwitchManager::CycleApp(bool forward, bool activateNow)
{
int32 startIndex = fCurrentIndex;
if (_FindNextValidApp(forward)) {
SwitchToApp(startIndex, fCurrentIndex, forward);
if (!activateNow)
return;
ActivateApp(false, false);
}
}
bool
TSwitchManager::_FindNextValidApp(bool forward)
{
if (fGroupList.IsEmpty())
return false;
int32 max = fGroupList.CountItems();
if (forward) {
fCurrentIndex++;
if (fCurrentIndex >= max)
fCurrentIndex = 0;
} else {
fCurrentIndex--;
if (fCurrentIndex < 0)
fCurrentIndex = max - 1;
}
return true;
}
void
TSwitchManager::SwitchToApp(int32 previous, int32 current, bool forward)
{
int32 previousSlot = fCurrentSlot;
fCurrentIndex = current;
fCurrentSlot = fWindow->SlotOf(fCurrentIndex);
fCurrentWindow = 0;
fWindow->Update(previous, fCurrentIndex, previousSlot, fCurrentSlot,
forward);
}
bool
TSwitchManager::ActivateApp(bool forceShow, bool allowWorkspaceSwitch)
{
client_window_info* windowInfo = WindowInfo(fCurrentIndex, fCurrentWindow);
if (windowInfo == NULL) {
windowInfo = WindowInfo(fCurrentIndex, 0);
if (windowInfo == NULL)
return false;
}
int32 currentWorkspace = current_workspace();
TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex);
if (teamGroup->TeamList()->CountItems() == 1) {
bool result;
if (forceShow && (fCurrentWindow != 0 || windowInfo->is_mini)) {
do_window_action(windowInfo->server_token, B_BRING_TO_FRONT,
BRect(0, 0, 0, 0), false);
}
if (!forceShow && windowInfo->is_mini) {
result = false;
} else if (!allowWorkspaceSwitch
&& (windowInfo->workspaces & (1 << currentWorkspace)) == 0) {
result = false;
} else {
result = true;
be_roster->ActivateApp((addr_t)teamGroup->TeamList()->ItemAt(0));
}
ASSERT(windowInfo);
free(windowInfo);
return result;
}
if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0) {
if (!allowWorkspaceSwitch) {
ASSERT(windowInfo);
free(windowInfo);
return false;
}
int32 destWorkspace = LowBitIndex(windowInfo->workspaces);
activate_workspace(destWorkspace);
}
if (!forceShow && windowInfo->is_mini) {
ASSERT(windowInfo);
free(windowInfo);
return false;
}
int32 tokenCount;
int32* tokens = get_token_list(-1, &tokenCount);
if (tokens == NULL) {
ASSERT(windowInfo);
free(windowInfo);
return true;
}
BList windowsToActivate;
for (int32 i = 0; i < tokenCount; i++) {
client_window_info* matchWindowInfo = get_window_info(tokens[i]);
if (!matchWindowInfo) {
continue;
}
if (!IsVisibleInCurrentWorkspace(matchWindowInfo)) {
free(matchWindowInfo);
break;
}
if (matchWindowInfo->server_token != windowInfo->server_token
&& teamGroup->TeamList()->HasItem(
(void*)(addr_t)matchWindowInfo->team))
windowsToActivate.AddItem(
(void*)(addr_t)matchWindowInfo->server_token);
free(matchWindowInfo);
}
free(tokens);
int32 i = windowsToActivate.CountItems() - 1;
for (; i >= 0; i--) {
int32 wid = (addr_t)windowsToActivate.ItemAt(i);
do_window_action(wid, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false);
}
do_window_action(windowInfo->server_token, B_BRING_TO_FRONT,
BRect(0, 0, 0, 0), false);
free(windowInfo);
return true;
}
void
TSwitchManager::QuitApp()
{
if (fGroupList.IsEmpty())
return;
TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex);
if (fCurrentIndex == fGroupList.CountItems() - 1) {
CycleApp(false, false);
}
for (int32 i = teamGroup->TeamList()->CountItems() - 1; i >= 0; i--) {
team_id team = (addr_t)teamGroup->TeamList()->ItemAt(i);
app_info info;
if (be_roster->GetRunningAppInfo(team, &info) == B_OK) {
if (strcasecmp(info.signature, kTrackerSignature) == 0) {
continue;
}
BMessenger messenger(NULL, team);
messenger.SendMessage(B_QUIT_REQUESTED);
}
}
}
void
TSwitchManager::HideApp()
{
if (fGroupList.IsEmpty())
return;
TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(fCurrentIndex);
for (int32 i = teamGroup->TeamList()->CountItems() - 1; i >= 0; i--) {
team_id team = (addr_t)teamGroup->TeamList()->ItemAt(i);
app_info info;
if (be_roster->GetRunningAppInfo(team, &info) == B_OK)
do_minimize_team(BRect(), team, false);
}
}
client_window_info*
TSwitchManager::WindowInfo(int32 groupIndex, int32 windowIndex)
{
TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex);
if (teamGroup == NULL)
return NULL;
int32 tokenCount;
int32* tokens = get_token_list(-1, &tokenCount);
if (tokens == NULL)
return NULL;
int32 matches = 0;
client_window_info* result = NULL;
for (int32 i = 0; i < tokenCount; i++) {
client_window_info* windowInfo = get_window_info(tokens[i]);
if (windowInfo) {
if (IsWindowOK(windowInfo) && (teamGroup->TeamList()->HasItem(
(void*)(addr_t)windowInfo->team))) {
if (matches == windowIndex) {
result = windowInfo;
break;
}
matches++;
}
free(windowInfo);
}
}
free(tokens);
return result;
}
int32
TSwitchManager::CountWindows(int32 groupIndex, bool )
{
TTeamGroup* teamGroup = (TTeamGroup*)fGroupList.ItemAt(groupIndex);
if (teamGroup == NULL)
return 0;
int32 result = 0;
for (int32 i = 0; ; i++) {
team_id teamID = (addr_t)teamGroup->TeamList()->ItemAt(i);
if (teamID == 0)
break;
int32 count;
int32* tokens = get_token_list(teamID, &count);
if (!tokens)
continue;
for (int32 i = 0; i < count; i++) {
window_info *windowInfo = get_window_info(tokens[i]);
if (windowInfo) {
if (IsWindowOK(windowInfo))
result++;
free(windowInfo);
}
}
free(tokens);
}
return result;
}
void
TSwitchManager::ActivateWindow(int32 windowID)
{
if (windowID == -1)
windowID = fWindowID;
do_window_action(windowID, B_BRING_TO_FRONT, BRect(0, 0, 0, 0), false);
}
void
TSwitchManager::SwitchWindow(team_id team, bool, bool activate)
{
int32 index;
TTeamGroup* teamGroup = FindTeam(team, &index);
if (teamGroup == NULL)
return;
int32 count;
int32* tokens = get_token_list(-1, &count);
if (tokens == NULL)
return;
for (int32 i = count - 1; i >= 0; i--) {
client_window_info* windowInfo = get_window_info(tokens[i]);
if (windowInfo && IsVisibleInCurrentWorkspace(windowInfo)
&& teamGroup->TeamList()->HasItem((void*)(addr_t)windowInfo->team)) {
fWindowID = windowInfo->server_token;
if (activate)
ActivateWindow(windowInfo->server_token);
free(windowInfo);
break;
}
free(windowInfo);
}
free(tokens);
}
void
TSwitchManager::Unblock()
{
fBlock = false;
fSkipUntil = system_time();
}
int32
TSwitchManager::CurrentIndex()
{
return fCurrentIndex;
}
int32
TSwitchManager::CurrentWindow()
{
return fCurrentWindow;
}
int32
TSwitchManager::CurrentSlot()
{
return fCurrentSlot;
}
BList*
TSwitchManager::GroupList()
{
return &fGroupList;
}
BRect
TSwitchManager::CenterRect()
{
return BRect(fCenterSlot * fSlotSize, 0,
(fCenterSlot + 1) * fSlotSize - 1, fSlotSize - 1);
}
TBox::TBox(BRect bounds, TSwitchManager* manager, TSwitcherWindow* window,
TIconView* iconView)
:
BBox(bounds, "top", B_FOLLOW_ALL, B_WILL_DRAW, B_NO_BORDER),
fManager(manager),
fWindow(window),
fIconView(iconView),
fLeftScroller(false),
fRightScroller(false),
fUpScroller(false),
fDownScroller(false)
{
}
void
TBox::AllAttached()
{
BRect centerRect(fManager->CenterRect());
BRect frame = fIconView->Frame();
centerRect.OffsetBy(frame.left, frame.top);
fIconView->ConvertToParent(¢erRect);
fCenter = centerRect;
}
void
TBox::MouseDown(BPoint where)
{
if (!fLeftScroller && !fRightScroller && !fUpScroller && !fDownScroller)
return;
BRect frame = fIconView->Frame();
BRect bounds = Bounds();
if (fLeftScroller) {
BRect lhit(0, frame.top, frame.left, frame.bottom);
if (lhit.Contains(where)) {
int32 previousIndex = fManager->CurrentIndex();
int32 previousSlot = fManager->CurrentSlot();
int32 newSlot = previousSlot - (fManager->SlotCount() - 1);
if (newSlot < 0)
newSlot = 0;
int32 newIndex = fIconView->IndexAt(newSlot);
fManager->SwitchToApp(previousIndex, newIndex, false);
}
}
if (fRightScroller) {
BRect rhit(frame.right, frame.top, bounds.right, frame.bottom);
if (rhit.Contains(where)) {
int32 previousIndex = fManager->CurrentIndex();
int32 previousSlot = fManager->CurrentSlot();
int32 newSlot = previousSlot + (fManager->SlotCount() - 1);
int32 newIndex = fIconView->IndexAt(newSlot);
if (newIndex < 0) {
newIndex = fManager->GroupList()->CountItems() - 1;
}
fManager->SwitchToApp(previousIndex, newIndex, true);
}
}
frame = fWindow->WindowView()->Frame();
if (fUpScroller) {
BRect hit1(frame.left - 10, frame.top, frame.left,
(frame.top + frame.bottom) / 2);
BRect hit2(frame.right, frame.top, frame.right + 10,
(frame.top + frame.bottom) / 2);
if (hit1.Contains(where) || hit2.Contains(where)) {
fManager->CycleWindow(false, false);
}
}
if (fDownScroller) {
BRect hit1(frame.left - 10, (frame.top + frame.bottom) / 2,
frame.left, frame.bottom);
BRect hit2(frame.right, (frame.top + frame.bottom) / 2,
frame.right + 10, frame.bottom);
if (hit1.Contains(where) || hit2.Contains(where)) {
fManager->CycleWindow(true, false);
}
}
}
void
TBox::Draw(BRect update)
{
static const int32 kChildInset = 7;
static const int32 kWedge = 6;
BBox::Draw(update);
BRect bounds = Bounds();
float height = fIconView->Bounds().Height();
float center = (bounds.right + bounds.left) / 2;
BRect box(3, 3, bounds.right - 3, 3 + height + kChildInset * 2);
rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color white = {255, 255, 255, 255};
rgb_color standardGray = panelColor;
rgb_color veryDarkGray = {128, 128, 128, 255};
rgb_color darkGray = tint_color(panelColor, B_DARKEN_1_TINT);
if (panelColor.IsDark()) {
standardGray = tint_color(panelColor, 0.8);
darkGray = tint_color(panelColor, 0.85);
white = make_color(200, 200, 200, 255);
veryDarkGray = make_color(0, 0, 0, 255);
}
SetHighColor(darkGray);
box.InsetBy(1, 1);
FillRect(box);
box.InsetBy(-1, -1);
BeginLineArray(50);
AddLine(box.LeftTop(), BPoint(center - kWedge, box.top), veryDarkGray);
AddLine(BPoint(center + kWedge, box.top), box.RightTop(), veryDarkGray);
AddLine(box.LeftBottom(), BPoint(center - kWedge, box.bottom),
veryDarkGray);
AddLine(BPoint(center + kWedge, box.bottom), box.RightBottom(),
veryDarkGray);
AddLine(box.LeftBottom() + BPoint(1, 1),
BPoint(center - kWedge, box.bottom + 1), white);
AddLine(BPoint(center + kWedge, box.bottom) + BPoint(0, 1),
box.RightBottom() + BPoint(1, 1), white);
AddLine(box.LeftTop(), box.LeftBottom(), veryDarkGray);
AddLine(box.RightTop(), box.RightBottom(), veryDarkGray);
AddLine(box.RightTop() + BPoint(1, 1), box.RightBottom() + BPoint(1, 1),
white);
BPoint point(center - kWedge, box.top);
AddLine(point, point + BPoint(kWedge, kWedge), veryDarkGray);
AddLine(point + BPoint(kWedge, kWedge), BPoint(center + kWedge, point.y),
veryDarkGray);
AddLine(point + BPoint(1, 0), point + BPoint(1, 0)
+ BPoint(kWedge - 1, kWedge - 1), white);
AddLine(point + BPoint(2, -1) + BPoint(kWedge - 1, kWedge - 1),
BPoint(center + kWedge - 1, point.y), darkGray);
BPoint topPoint = point;
point.y = box.bottom;
point.x = center - kWedge;
AddLine(point, point + BPoint(kWedge, -kWedge), veryDarkGray);
AddLine(point + BPoint(kWedge, -kWedge),
BPoint(center + kWedge, point.y), veryDarkGray);
AddLine(point + BPoint(1, 0),
point + BPoint(1, 0) + BPoint(kWedge - 1, -(kWedge - 1)), white);
AddLine(point + BPoint(2 , 1) + BPoint(kWedge - 1, -(kWedge - 1)),
BPoint(center + kWedge - 1, point.y), darkGray);
BPoint bottomPoint = point;
EndLineArray();
SetHighColor(standardGray);
FillTriangle(topPoint + BPoint(2, 0),
topPoint + BPoint(2, 0) + BPoint(kWedge - 2, kWedge - 2),
BPoint(center + kWedge - 2, topPoint.y));
SetHighColor(standardGray);
FillTriangle(bottomPoint + BPoint(2, 0),
bottomPoint + BPoint(2, 0) + BPoint(kWedge - 2, -(kWedge - 2)),
BPoint(center + kWedge - 2, bottomPoint.y));
DrawIconScrollers(false);
DrawWindowScrollers(false);
}
void
TBox::DrawIconScrollers(bool force)
{
rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color backgroundColor;
rgb_color dark;
if (panelColor.IsLight()) {
backgroundColor = tint_color(panelColor, B_DARKEN_1_TINT);
dark = tint_color(backgroundColor, B_DARKEN_3_TINT);
} else {
backgroundColor = tint_color(panelColor, 0.85);
dark = tint_color(panelColor, B_LIGHTEN_1_TINT);
}
bool updateLeft = false;
bool updateRight = false;
BRect rect = fIconView->Bounds();
if (rect.left > (fManager->SlotSize() * fManager->CenterSlot())) {
updateLeft = true;
fLeftScroller = true;
} else {
fLeftScroller = false;
if (force)
updateLeft = true;
}
int32 maxIndex = fManager->GroupList()->CountItems() - 1;
BRect lastFrame = fIconView->FrameOf(maxIndex);
if (lastFrame.right > rect.right) {
updateRight = true;
fRightScroller = true;
} else {
fRightScroller = false;
if (force)
updateRight = true;
}
PushState();
SetDrawingMode(B_OP_COPY);
rect = fIconView->Frame();
if (updateLeft) {
BPoint pt1, pt2, pt3;
pt1.x = rect.left - 5;
pt1.y = floorf((rect.bottom + rect.top) / 2);
pt2.x = pt3.x = pt1.x + 3;
pt2.y = pt1.y - 3;
pt3.y = pt1.y + 3;
if (fLeftScroller) {
SetHighColor(dark);
FillTriangle(pt1, pt2, pt3);
} else if (force) {
SetHighColor(backgroundColor);
FillRect(BRect(pt1.x, pt2.y, pt3.x, pt3.y));
}
}
if (updateRight) {
BPoint pt1, pt2, pt3;
pt1.x = rect.right + 4;
pt1.y = rintf((rect.bottom + rect.top) / 2);
pt2.x = pt3.x = pt1.x - 4;
pt2.y = pt1.y - 4;
pt3.y = pt1.y + 4;
if (fRightScroller) {
SetHighColor(dark);
FillTriangle(pt1, pt2, pt3);
} else if (force) {
SetHighColor(backgroundColor);
FillRect(BRect(pt3.x, pt2.y, pt1.x, pt3.y));
}
}
PopState();
}
void
TBox::DrawWindowScrollers(bool force)
{
rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color backgroundColor;
rgb_color dark;
if (panelColor.IsLight()) {
backgroundColor = tint_color(panelColor, B_DARKEN_1_TINT);
dark = tint_color(backgroundColor, B_DARKEN_2_TINT);
} else {
backgroundColor = panelColor;
dark = tint_color(panelColor, B_LIGHTEN_2_TINT);
}
bool updateUp = false;
bool updateDown = false;
BRect rect = fWindow->WindowView()->Bounds();
if (rect.top != 0) {
updateUp = true;
fUpScroller = true;
} else {
fUpScroller = false;
if (force)
updateUp = true;
}
int32 groupIndex = fManager->CurrentIndex();
int32 maxIndex = fManager->CountWindows(groupIndex) - 1;
BRect lastFrame(0, 0, 0, 0);
if (maxIndex >= 0)
lastFrame = fWindow->WindowView()->FrameOf(maxIndex);
if (maxIndex >= 0 && lastFrame.bottom > rect.bottom) {
updateDown = true;
fDownScroller = true;
} else {
fDownScroller = false;
if (force)
updateDown = true;
}
PushState();
SetDrawingMode(B_OP_COPY);
rect = fWindow->WindowView()->Frame();
rect.InsetBy(-3, 0);
if (updateUp) {
if (fUpScroller) {
SetHighColor(dark);
BPoint pt1, pt2, pt3;
pt1.x = rect.left - 6;
pt1.y = rect.top + 3;
pt2.y = pt3.y = pt1.y + 4;
pt2.x = pt1.x - 4;
pt3.x = pt1.x + 4;
FillTriangle(pt1, pt2, pt3);
pt1.x += rect.Width() + 12;
pt2.x += rect.Width() + 12;
pt3.x += rect.Width() + 12;
FillTriangle(pt1, pt2, pt3);
} else if (force) {
FillRect(BRect(rect.left - 10, rect.top + 3, rect.left - 2,
rect.top + 7), B_SOLID_LOW);
FillRect(BRect(rect.right + 2, rect.top + 3, rect.right + 10,
rect.top + 7), B_SOLID_LOW);
}
}
if (updateDown) {
if (fDownScroller) {
SetHighColor(dark);
BPoint pt1, pt2, pt3;
pt1.x = rect.left - 6;
pt1.y = rect.bottom - 3;
pt2.y = pt3.y = pt1.y - 4;
pt2.x = pt1.x - 4;
pt3.x = pt1.x + 4;
FillTriangle(pt1, pt2, pt3);
pt1.x += rect.Width() + 12;
pt2.x += rect.Width() + 12;
pt3.x += rect.Width() + 12;
FillTriangle(pt1, pt2, pt3);
} else if (force) {
FillRect(BRect(rect.left - 10, rect.bottom - 7, rect.left - 2,
rect.bottom - 3), B_SOLID_LOW);
FillRect(BRect(rect.right + 2, rect.bottom - 7, rect.right + 10,
rect.bottom - 3), B_SOLID_LOW);
}
}
PopState();
Sync();
}
TSwitcherWindow::TSwitcherWindow(BRect frame, TSwitchManager* manager)
:
BWindow(frame, "Twitcher", B_MODAL_WINDOW_LOOK, B_MODAL_ALL_WINDOW_FEEL,
B_NOT_MINIMIZABLE | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_ALL_WORKSPACES),
fManager(manager),
fHairTrigger(true)
{
BRect rect = frame;
rect.OffsetTo(B_ORIGIN);
rect.InsetBy(kHorizontalMargin, 0);
rect.top = kVerticalMargin;
rect.bottom = rect.top + fManager->SlotSize() + 1;
fIconView = new TIconView(rect, manager, this);
rect.top = rect.bottom + (kVerticalMargin * 1 + 4);
rect.InsetBy(9, 0);
fWindowView = new TWindowView(rect, manager, this);
fWindowView->ResizeToPreferred();
fTopView = new TBox(Bounds(), fManager, this, fIconView);
AddChild(fTopView);
fTopView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fTopView->SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
fTopView->SetHighUIColor(B_PANEL_TEXT_COLOR);
SetPulseRate(0);
fTopView->AddChild(fIconView);
fTopView->AddChild(fWindowView);
if (be_plain_font->Size() != 12) {
float sizeDelta = be_plain_font->Size() - 12;
ResizeBy(0, sizeDelta);
}
CenterWindow(BScreen(B_MAIN_SCREEN_ID).Frame(), frame.Size());
}
TSwitcherWindow::~TSwitcherWindow()
{
}
void
TSwitcherWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_UNMAPPED_KEY_DOWN:
case B_KEY_DOWN:
{
int32 repeats = 0;
if (message->FindInt32("be:key_repeat", &repeats) == B_OK
&& (fSkipKeyRepeats || (repeats % 6) != 0))
break;
fSkipKeyRepeats = false;
uint32 rawChar;
uint32 modifiers;
message->FindInt32("raw_char", 0, (int32*)&rawChar);
message->FindInt32("modifiers", 0, (int32*)&modifiers);
DoKey(rawChar, modifiers);
break;
}
default:
BWindow::MessageReceived(message);
}
}
void
TSwitcherWindow::ScreenChanged(BRect screenFrame, color_space)
{
CenterWindow(screenFrame | BScreen(B_MAIN_SCREEN_ID).Frame(),
Frame().Size());
}
void
TSwitcherWindow::WorkspaceActivated(int32, bool active)
{
if (active)
CenterWindow(BScreen(B_MAIN_SCREEN_ID).Frame(), Frame().Size());
}
void
TSwitcherWindow::CenterWindow(BRect screenFrame, BSize windowSize)
{
BPoint centered = BLayoutUtils::AlignInFrame(screenFrame, windowSize,
BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER))
.LeftTop();
centered.y -= roundf(screenFrame.Height() / 16);
MoveTo(centered);
}
void
TSwitcherWindow::Redraw(int32 index)
{
BRect frame = fIconView->FrameOf(index);
frame.right = fIconView->Bounds().right;
fIconView->Invalidate(frame);
}
void
TSwitcherWindow::DoKey(uint32 key, uint32 modifiers)
{
bool forward = ((modifiers & B_SHIFT_KEY) == 0);
switch (key) {
case B_RIGHT_ARROW:
fManager->CycleApp(true, false);
break;
case B_LEFT_ARROW:
case '1':
fManager->CycleApp(false, false);
break;
case B_UP_ARROW:
fManager->CycleWindow(false, false);
break;
case B_DOWN_ARROW:
fManager->CycleWindow(true, false);
break;
case B_TAB:
fManager->CycleApp(forward, false);
break;
case B_ESCAPE:
fManager->Stop(false, 0);
break;
case B_SPACE:
case B_ENTER:
fManager->Stop(true, modifiers);
break;
case 'q':
case 'Q':
fManager->QuitApp();
break;
case 'h':
case 'H':
fManager->HideApp();
break;
#if _ALLOW_STICKY_
case 's':
case 'S':
if (fHairTrigger) {
SetLook(B_TITLED_WINDOW_LOOK);
fHairTrigger = false;
} else {
SetLook(B_MODAL_WINDOW_LOOK);
fHairTrigger = true;
}
break;
#endif
}
}
bool
TSwitcherWindow::QuitRequested()
{
fManager->Stop(false, 0);
return false;
}
void
TSwitcherWindow::WindowActivated(bool state)
{
if (state)
fManager->Unblock();
}
void
TSwitcherWindow::Update(int32 previous, int32 current,
int32 previousSlot, int32 currentSlot, bool forward)
{
if (!IsHidden()) {
fIconView->Update(previous, current, previousSlot, currentSlot,
forward);
} else
fIconView->CenterOn(current);
fWindowView->UpdateGroup(current, 0);
}
void
TSwitcherWindow::Hide()
{
fIconView->Hiding();
SetPulseRate(0);
BWindow::Hide();
}
void
TSwitcherWindow::Show()
{
fHairTrigger = true;
fSkipKeyRepeats = true;
fIconView->Showing();
SetPulseRate(100000);
SetLook(B_MODAL_WINDOW_LOOK);
BWindow::Show();
}
TBox*
TSwitcherWindow::TopView()
{
return fTopView;
}
bool
TSwitcherWindow::HairTrigger()
{
return fHairTrigger;
}
inline int32
TSwitcherWindow::SlotOf(int32 i)
{
return fIconView->SlotOf(i);
}
inline TIconView*
TSwitcherWindow::IconView()
{
return fIconView;
}
inline TWindowView*
TSwitcherWindow::WindowView()
{
return fWindowView;
}
TIconView::TIconView(BRect frame, TSwitchManager* manager,
TSwitcherWindow* switcherWindow)
: BView(frame, "main_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
fAutoScrolling(false),
fSwitcher(switcherWindow),
fManager(manager),
fOffBitmap(NULL),
fOffView(NULL)
{
BRect slot(0, 0, fManager->SlotSize() - 1, fManager->SlotSize() - 1);
fOffView = new BView(slot, "off_view", B_FOLLOW_NONE, B_WILL_DRAW);
fOffBitmap = new BBitmap(slot, B_RGBA32, true);
fOffBitmap->AddChild(fOffView);
}
TIconView::~TIconView()
{
}
void
TIconView::KeyDown(const char* , int32 )
{
}
void
TIconView::AnimateIcon(const BBitmap* start, const BBitmap* end)
{
BRect centerRect(fManager->CenterRect());
BRect bounds = Bounds();
float off = 0;
BRect startRect = start->Bounds();
BRect endRect = end->Bounds();
BRect rect = startRect;
int32 small = fManager->SmallIconSize();
bool out = startRect.Width() <= small;
int32 insetValue = small / 8;
int32 inset = out ? -insetValue : insetValue;
off = roundf((centerRect.Width() - rect.Width()) / 2);
startRect.OffsetTo(off, off);
rect.OffsetTo(off, off);
off = roundf((centerRect.Width() - endRect.Width()) / 2);
endRect.OffsetTo(off, off);
centerRect.OffsetBy(bounds.left, 0);
BRect destRect = fOffBitmap->Bounds();
destRect.OffsetTo(centerRect.left, 0);
off = roundf((centerRect.Width() - destRect.Width()) / 2);
destRect.OffsetBy(off, off);
fOffBitmap->Lock();
rgb_color bg = ui_color(B_PANEL_BACKGROUND_COLOR);
fOffView->SetHighColor(tint_color(bg, bg.IsLight() ? B_DARKEN_1_TINT : 0.85));
for (int i = 0; i < 2; i++) {
rect.InsetBy(inset, inset);
snooze(20000);
fOffView->SetDrawingMode(B_OP_COPY);
fOffView->FillRect(fOffView->Bounds());
fOffView->SetDrawingMode(B_OP_ALPHA);
fOffView->DrawBitmap(start, rect);
fOffView->Sync();
DrawBitmap(fOffBitmap, destRect);
}
fOffView->SetDrawingMode(B_OP_COPY);
fOffView->FillRect(fOffView->Bounds());
fOffView->SetDrawingMode(B_OP_ALPHA);
fOffView->DrawBitmap(start, startRect);
fOffView->Sync();
DrawBitmap(fOffBitmap, destRect);
for (int i = 0; i < 2; i++) {
rect.InsetBy(inset, inset);
snooze(20000);
fOffView->SetDrawingMode(B_OP_COPY);
fOffView->FillRect(fOffView->Bounds());
fOffView->SetDrawingMode(B_OP_ALPHA);
fOffView->DrawBitmap(end, rect);
fOffView->Sync();
DrawBitmap(fOffBitmap, destRect);
}
fOffView->SetDrawingMode(B_OP_COPY);
fOffView->FillRect(fOffView->Bounds());
fOffView->SetDrawingMode(B_OP_ALPHA);
fOffView->DrawBitmap(end, endRect);
fOffView->Sync();
DrawBitmap(fOffBitmap, destRect);
fOffBitmap->Unlock();
}
void
TIconView::Update(int32 previous, int32 current,
int32 previousSlot, int32 currentSlot, bool forward)
{
BList* groupList = fManager->GroupList();
ASSERT(groupList);
TTeamGroup* previousGroup = (TTeamGroup*)groupList->ItemAt(previous);
ASSERT(previousGroup);
AnimateIcon(previousGroup->LargeIcon(), previousGroup->SmallIcon());
int32 nslots = abs(previousSlot - currentSlot);
int32 stepSize = fManager->ScrollStep();
if (forward && (currentSlot < previousSlot)) {
forward = false;
if (previousSlot - currentSlot > 4)
stepSize *= 2;
} else if (!forward && (currentSlot > previousSlot)) {
forward = true;
if (currentSlot - previousSlot > 4)
stepSize *= 2;
}
int32 scrollValue = forward ? stepSize : -stepSize;
int32 total = 0;
fAutoScrolling = true;
while (total < (nslots * fManager->SlotSize())) {
ScrollBy(scrollValue, 0);
snooze(1000);
total += stepSize;
Window()->UpdateIfNeeded();
}
fAutoScrolling = false;
TTeamGroup* currentGroup = (TTeamGroup*)groupList->ItemAt(current);
ASSERT(currentGroup != NULL);
AnimateIcon(currentGroup->SmallIcon(), currentGroup->LargeIcon());
}
void
TIconView::CenterOn(int32 index)
{
BRect rect = FrameOf(index);
ScrollTo(rect.left - (fManager->CenterSlot() * fManager->SlotSize()), 0);
}
int32
TIconView::ItemAtPoint(BPoint point) const
{
return IndexAt((int32)(point.x / fManager->SlotSize()) - fManager->CenterSlot());
}
void
TIconView::ScrollTo(BPoint where)
{
BView::ScrollTo(where);
fSwitcher->TopView()->DrawIconScrollers(true);
}
int32
TIconView::IndexAt(int32 slot) const
{
if (slot < 0 || slot >= fManager->GroupList()->CountItems())
return -1;
return slot;
}
int32
TIconView::SlotOf(int32 index) const
{
BRect rect = FrameOf(index);
return (int32)(rect.left / fManager->SlotSize()) - fManager->CenterSlot();
}
BRect
TIconView::FrameOf(int32 index) const
{
int32 visible = index + fManager->CenterSlot();
return BRect(visible * fManager->SlotSize(), 0,
(visible + 1) * fManager->SlotSize() - 1, fManager->SlotSize() - 1);
}
void
TIconView::DrawTeams(BRect update)
{
float tint = B_NO_TINT;
rgb_color panelColor = ui_color(B_PANEL_BACKGROUND_COLOR);
if (panelColor.IsDark())
tint = 0.85;
else
tint = B_DARKEN_1_TINT;
SetHighUIColor(B_PANEL_BACKGROUND_COLOR, tint);
SetLowUIColor(ViewUIColor(), tint);
FillRect(update);
int32 mainIndex = fManager->CurrentIndex();
BList* groupList = fManager->GroupList();
int32 teamCount = groupList->CountItems();
BRect rect(fManager->CenterSlot() * fManager->SlotSize(), 0,
(fManager->CenterSlot() + 1) * fManager->SlotSize() - 1,
fManager->SlotSize() - 1);
for (int32 i = 0; i < teamCount; i++) {
TTeamGroup* group = (TTeamGroup*)groupList->ItemAt(i);
if (rect.Intersects(update) && group != NULL) {
SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
group->Draw(this, rect, !fAutoScrolling && (i == mainIndex));
SetDrawingMode(B_OP_COPY);
}
rect.OffsetBy(fManager->SlotSize(), 0);
}
}
void
TIconView::Draw(BRect update)
{
DrawTeams(update);
}
void
TIconView::MouseDown(BPoint where)
{
int32 index = ItemAtPoint(where);
if (index >= 0) {
int32 previousIndex = fManager->CurrentIndex();
int32 previousSlot = fManager->CurrentSlot();
int32 currentSlot = SlotOf(index);
fManager->SwitchToApp(previousIndex, index, (currentSlot
> previousSlot));
}
}
void
TIconView::Pulse()
{
uint32 modifiersKeys = modifiers();
if (fSwitcher->HairTrigger() && (modifiersKeys & B_CONTROL_KEY) == 0) {
fManager->Stop(true, modifiersKeys);
return;
}
if (!fSwitcher->HairTrigger()) {
uint32 buttons;
BPoint point;
GetMouse(&point, &buttons);
if (buttons != 0) {
point = ConvertToScreen(point);
if (!Window()->Frame().Contains(point))
fManager->Stop(false, 0);
}
}
}
void
TIconView::Showing()
{
}
void
TIconView::Hiding()
{
ScrollTo(B_ORIGIN);
}
TWindowView::TWindowView(BRect rect, TSwitchManager* manager,
TSwitcherWindow* window)
: BView(rect, "wlist_view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
fCurrentToken(-1),
fItemHeight(-1),
fSwitcher(window),
fManager(manager)
{
SetFont(be_plain_font);
}
void
TWindowView::AttachedToWindow()
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}
void
TWindowView::ScrollTo(BPoint where)
{
BView::ScrollTo(where);
fSwitcher->TopView()->DrawWindowScrollers(true);
}
BRect
TWindowView::FrameOf(int32 index) const
{
return BRect(0, index * fItemHeight, 100, ((index + 1) * fItemHeight) - 1);
}
void
TWindowView::GetPreferredSize(float* _width, float* _height)
{
font_height fh;
be_plain_font->GetHeight(&fh);
fItemHeight = (int32) fh.ascent + fh.descent;
float margin = be_control_look->DefaultLabelSpacing();
fItemHeight = fItemHeight + margin * 2;
fItemHeight = ((((int)fItemHeight) + kWindowScrollSteps)
/ kWindowScrollSteps) * kWindowScrollSteps;
*_height = fItemHeight;
*_width = Bounds().Width();
}
void
TWindowView::ShowIndex(int32 newIndex)
{
BPoint point(0, newIndex * fItemHeight);
BRect bounds = Bounds();
int32 groupIndex = fManager->CurrentIndex();
TTeamGroup* teamGroup
= (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex);
if (teamGroup == NULL)
return;
window_info* windowInfo = fManager->WindowInfo(groupIndex, newIndex);
if (windowInfo == NULL)
return;
fCurrentToken = windowInfo->server_token;
free(windowInfo);
if (bounds.top == point.y)
return;
int32 oldIndex = (int32) (bounds.top / fItemHeight);
int32 stepSize = (int32) (fItemHeight / kWindowScrollSteps);
int32 scrollValue = (newIndex > oldIndex) ? stepSize : -stepSize;
int32 total = 0;
int32 nslots = abs(newIndex - oldIndex);
while (total < (nslots * (int32)fItemHeight)) {
ScrollBy(0, scrollValue);
snooze(10000);
total += stepSize;
Window()->UpdateIfNeeded();
}
}
void
TWindowView::Draw(BRect update)
{
int32 groupIndex = fManager->CurrentIndex();
TTeamGroup* teamGroup
= (TTeamGroup*)fManager->GroupList()->ItemAt(groupIndex);
if (teamGroup == NULL)
return;
BRect bounds = Bounds();
int32 windowIndex = (int32) (bounds.top / fItemHeight);
BRect windowRect = bounds;
windowRect.top = windowIndex * fItemHeight;
windowRect.bottom = (windowIndex + 1) * fItemHeight - 1;
for (int32 i = 0; i < 3; i++) {
if (!update.Intersects(windowRect)) {
windowIndex++;
windowRect.OffsetBy(0, fItemHeight);
continue;
}
bool local = true;
bool minimized = false;
BString title;
client_window_info* windowInfo
= fManager->WindowInfo(groupIndex, windowIndex);
if (windowInfo != NULL) {
if (SmartStrcmp(windowInfo->name, teamGroup->Name()) != 0)
title << teamGroup->Name() << ": " << windowInfo->name;
else
title = teamGroup->Name();
int32 currentWorkspace = current_workspace();
if ((windowInfo->workspaces & (1 << currentWorkspace)) == 0)
local = false;
minimized = windowInfo->is_mini;
free(windowInfo);
} else
title = teamGroup->Name();
if (!title.Length())
return;
float iconWidth = 0;
TBarApp* app = static_cast<TBarApp*>(be_app);
const BBitmap* bitmap = app->FetchWindowIcon(local, minimized);
if (bitmap != NULL)
iconWidth = bitmap->Bounds().Width();
float spacing = be_control_look->DefaultLabelSpacing();
float stringWidth = StringWidth(title.String());
float maxWidth = bounds.Width();
if (bitmap != NULL)
maxWidth -= (iconWidth + spacing);
if (stringWidth > maxWidth) {
TruncateString(&title, B_TRUNCATE_MIDDLE, maxWidth);
stringWidth = maxWidth;
}
BPoint point((bounds.Width() - stringWidth) / 2, windowRect.bottom);
if (bitmap != NULL) {
point.x = (bounds.Width()
- (stringWidth + iconWidth + spacing)) / 2;
}
BPoint p(point.x, (windowRect.top + windowRect.bottom) / 2);
SetDrawingMode(B_OP_OVER);
if (bitmap != NULL) {
p.y -= (bitmap->Bounds().bottom - bitmap->Bounds().top) / 2;
DrawBitmap(bitmap, p);
point.x += bitmap->Bounds().Width() + spacing;
}
font_height fontHeight;
GetFontHeight(&fontHeight);
float textHeight = fontHeight.ascent + fontHeight.descent;
point.y -= (textHeight / 2) + (spacing / 2);
if (bitmap != NULL) {
point.y += ((textHeight - bitmap->Bounds().Height()) / 2);
}
MovePenTo(point);
SetHighUIColor(B_PANEL_TEXT_COLOR);
DrawString(title.String());
SetDrawingMode(B_OP_COPY);
windowIndex++;
windowRect.OffsetBy(0, fItemHeight);
}
}
void
TWindowView::UpdateGroup(int32, int32 windowIndex)
{
ScrollTo(0, windowIndex * fItemHeight);
Invalidate(Bounds());
}
void
TWindowView::Pulse()
{
window_info *windowInfo = get_window_info(fCurrentToken);
if (windowInfo == NULL) {
Invalidate();
ShowIndex(0);
} else
free(windowInfo);
}