Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
trademarks of Be Incorporated in the United States and other countries. Other
brand product names are registered trademarks or trademarks of their respective
holders.
All rights reserved.
*/
#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
&& !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;
}
\brief quit all teams in this group
*/
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);
}
}
}
\brief hide all teams in this group
*/
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);
}