You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2685 lines
88 KiB
2685 lines
88 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: tmswitch.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* History:
|
|
* 29-May-1991 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/*
|
|
* COOLSWITCHTRACE is used to trace problems
|
|
* in the CoolSwitch window
|
|
*/
|
|
#undef COOLSWITCHTRACE
|
|
|
|
|
|
#define ALT_F6 2
|
|
#define ALT_ESCAPE 1
|
|
|
|
#define FDIR_FORWARD 0
|
|
#define FDIR_BACKWARD 1
|
|
|
|
|
|
/*
|
|
* Win95 hard codes the size of the icon matrix, the size of
|
|
* the icons, the highlight border and the icon spacing
|
|
*/
|
|
#define CXICONSLOT 43
|
|
#define CYICONSLOT 43
|
|
#define CXICONSIZE 32
|
|
#define CYICONSIZE 32
|
|
|
|
/*
|
|
* Pointer to the start of the SwitchInfo list.
|
|
*/
|
|
PSWINFO gpswiFirst;
|
|
|
|
|
|
VOID xxxPaintIconsInSwitchWindow(PWND, PSWINFO, HDC, INT, INT, INT, BOOL, BOOL, PICON);
|
|
|
|
/***************************************************************************\
|
|
* Getpswi
|
|
*
|
|
* 04-29-96 GerardoB Created
|
|
\***************************************************************************/
|
|
__inline PSWINFO Getpswi(
|
|
PWND pwnd)
|
|
{
|
|
UserAssert(GETFNID(pwnd) == FNID_SWITCH);
|
|
return TestWF(pwnd, WFDESTROYED) ? NULL : ((PSWITCHWND)pwnd)->pswi;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* Setpswi
|
|
*
|
|
* 04-29-96 GerardoB Created
|
|
\***************************************************************************/
|
|
__inline VOID Setpswi(
|
|
PWND pwnd,
|
|
PSWINFO pswi)
|
|
{
|
|
UserAssert(GETFNID(pwnd) == FNID_SWITCH);
|
|
((PSWITCHWND)pwnd)->pswi = pswi;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DSW_GetTopLevelCreatorWindow
|
|
\***************************************************************************/
|
|
PWND DSW_GetTopLevelCreatorWindow(
|
|
PWND pwnd)
|
|
{
|
|
UserAssert(pwnd != NULL);
|
|
|
|
if (pwnd != NULL) {
|
|
while (pwnd->spwndOwner) {
|
|
pwnd = pwnd->spwndOwner;
|
|
}
|
|
}
|
|
|
|
return pwnd;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetNextQueueWindow
|
|
*
|
|
* This routine is used to implement the Alt+Esc feature. This feature lets
|
|
* the user switch between windows for different applications (a.k.a. "Tasks")
|
|
* currently running. We keep track of the most recently active window in
|
|
* each task. This routine starts with the window passed and searches for the
|
|
* next window, in the "top-level" window list, that is from a different task
|
|
* than the one passed. We then return the most recenly active window from
|
|
* that task (or the window we found if the most recently active has been
|
|
* destroyed or is currently disabled or hidden). This routine returns NULL
|
|
* if no other enabled, visible window for another task can be found.
|
|
*
|
|
* History:
|
|
* 30-May-1991 DavidPe Ported from Win 3.1 sources.
|
|
\***************************************************************************/
|
|
PWND _GetNextQueueWindow(
|
|
PWND pwnd,
|
|
BOOL fPrev, /* 1 backward 0 forward */
|
|
BOOL fAltEsc)
|
|
{
|
|
PWND pwndAltTab;
|
|
PWND pwndNext;
|
|
PWND pwndT;
|
|
PWND pwndDesktop;
|
|
BOOL bBeenHereAlready = FALSE;
|
|
|
|
/*
|
|
* If the window we receive is Null then use the last topmost window
|
|
*/
|
|
if (!pwnd) {
|
|
pwnd = GetLastTopMostWindow();
|
|
if (!pwnd) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
pwndAltTab = gspwndAltTab;
|
|
|
|
pwnd = pwndNext = GetTopLevelWindow(pwnd);
|
|
if (!pwndNext) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Get the window's desktop
|
|
*/
|
|
if ((pwndDesktop = pwndNext->spwndParent) == NULL) {
|
|
pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
|
|
pwnd = pwndNext = pwndDesktop->spwndChild;
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
if (pwndNext == NULL)
|
|
return NULL;
|
|
|
|
/*
|
|
* Get the next window
|
|
*/
|
|
pwndNext = _GetWindow(pwndNext, fPrev ? GW_HWNDPREV : GW_HWNDNEXT);
|
|
|
|
if (!pwndNext) {
|
|
|
|
pwndNext = fPrev ? _GetWindow(pwndDesktop->spwndChild, GW_HWNDLAST)
|
|
: pwndDesktop->spwndChild;
|
|
/*
|
|
* To avoid searching the child chain forever, bale out if we get
|
|
* to the end (beginning) of the chain twice.
|
|
* This happens if pwnd is a partially destroyed window that has
|
|
* been unlinked from its siblings but not yet unlinked from the
|
|
* parent. (Happens while sending WM_NCDESTROY in xxxFreeWindow)
|
|
*/
|
|
if (bBeenHereAlready) {
|
|
RIPMSG1(RIP_WARNING, "pwnd %#p is no longer a sibling", pwnd);
|
|
return NULL;
|
|
}
|
|
|
|
bBeenHereAlready = TRUE;
|
|
}
|
|
|
|
/*
|
|
* If we have gone all the way around with no success, return NULL.
|
|
*/
|
|
if (!pwndNext || (pwndNext == pwnd))
|
|
return NULL;
|
|
|
|
/*
|
|
* Ignore the following windows:
|
|
* Switch window
|
|
* Tool Windows
|
|
* NoActivate Windows
|
|
* Hidden windows
|
|
* Disabled windows
|
|
* Topmost windows if via Alt+Esc
|
|
* Bottommost windows if via Alt+Esc
|
|
*
|
|
* If we're doing Alt-Esc processing, we have to skip topmost windows.
|
|
*
|
|
* Because topmost windows don't really go to the back when we
|
|
* send them there, alt-esc would never enumerate non-topmost windows.
|
|
* So, although we're allowed to start enumeration at a topmost window,
|
|
* we only allow enumeration of non-topmost windows, so the user can
|
|
* enumerate his presumably more important applications.
|
|
*/
|
|
if ((pwndNext != pwndAltTab) &&
|
|
// BradG - Win95 is missing the check for Tool Windows
|
|
(!TestWF(pwndNext, WEFTOOLWINDOW)) &&
|
|
(!TestWF(pwndNext, WEFNOACTIVATE)) &&
|
|
(TestWF(pwndNext, WFVISIBLE)) &&
|
|
((pwndNext->spwndLastActive == NULL) || (!TestWF(pwndNext->spwndLastActive, WFDISABLED)) &&
|
|
(!fAltEsc || (!TestWF(pwndNext, WEFTOPMOST) && !TestWF(pwndNext, WFBOTTOMMOST))))) {
|
|
/*
|
|
* If this window is owned, don't return it unless it is the most
|
|
* recently active window in its owner/ownee group.
|
|
*/
|
|
/*
|
|
* Hard loop to find top level owner
|
|
*/
|
|
for (pwndT = pwndNext; pwndT->spwndOwner; pwndT = pwndT->spwndOwner)
|
|
;
|
|
|
|
/*
|
|
* Don't return it unless it is the most recently active
|
|
* window in its owner/ownee group.
|
|
*/
|
|
if (pwndNext == pwndT->spwndLastActive)
|
|
return pwndNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* SwitchToThisWindow()
|
|
*
|
|
* This function was added specifically for Win386. It is called to tell
|
|
* USER that a particular window has been switched to via Alt+Tab or
|
|
* Alt+Esc in the Win386 environment. They call this function to maintain
|
|
* Z-ordering and consistent operation of these two functions. This function
|
|
* must be exported, but need not be documented.
|
|
*
|
|
* The parameter fTab is TRUE if this window is to be switched to via an
|
|
* Alt/Ctl+Tab key sequence otherwise it must be FALSE.
|
|
*
|
|
* History:
|
|
* 04-Feb-1991 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxSwitchToThisWindow(
|
|
PWND pwnd,
|
|
BOOL fAltTab)
|
|
{
|
|
CheckLock(pwnd);
|
|
|
|
/*
|
|
* If we need to, push old window to the bottom.
|
|
*/
|
|
if (gpqForeground && !fAltTab) {
|
|
|
|
BOOL fPush;
|
|
PWND pwndActive;
|
|
TL tlpwndActive;
|
|
|
|
/*
|
|
* if ALT-ESC, and the window brought forward is the next one in the
|
|
* list, we must be rotating the zorder forward, so push the current
|
|
* window to the back
|
|
*/
|
|
pwndActive = gpqForeground->spwndActive;
|
|
fPush = pwndActive && _GetNextQueueWindow(pwndActive, FDIR_FORWARD, !fAltTab);
|
|
if (fPush && !TestWF(pwndActive, WEFTOPMOST) && !TestWF(pwndActive, WFBOTTOMMOST)) {
|
|
ThreadLock(pwndActive, &tlpwndActive);
|
|
xxxSetWindowPos(pwndActive, PWND_BOTTOM, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
|
|
ThreadUnlock(&tlpwndActive);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Switch this new window to the foreground
|
|
* This window can go away during the SetForeground call if it isn't
|
|
* on the thread calling SwitchToThisWindow()!
|
|
*/
|
|
xxxSetForegroundWindow(pwnd, TRUE);
|
|
|
|
/*
|
|
* Restore minimized windows if the Alt+Tab case
|
|
*/
|
|
if (fAltTab && TestWF(pwnd,WFMINIMIZED)) {
|
|
|
|
/*
|
|
* We need to package up a special 'posted' queue message here. This
|
|
* ensures that this message gets processed after the asynchronous
|
|
* activation event occurs (via SetForegroundWindow).
|
|
*/
|
|
PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq,
|
|
QEVENT_POSTMESSAGE, pwnd, WM_SYSCOMMAND,
|
|
SC_RESTORE, 0 );
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* NextPrevTaskIndex
|
|
*
|
|
* History:
|
|
* 01-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
INT NextPrevTaskIndex(
|
|
PSWINFO pswInfo,
|
|
INT iIndex,
|
|
INT iCount,
|
|
BOOL fNext)
|
|
{
|
|
UserAssert(iCount <= pswInfo->iTotalTasks);
|
|
|
|
if (fNext) {
|
|
iIndex += iCount;
|
|
if (iIndex >= pswInfo->iTotalTasks) {
|
|
iIndex -= pswInfo->iTotalTasks;
|
|
}
|
|
} else {
|
|
iIndex -= iCount;
|
|
if (iIndex < 0) {
|
|
iIndex += pswInfo->iTotalTasks;
|
|
}
|
|
}
|
|
|
|
UserAssert((iIndex >= 0) && (iIndex < pswInfo->iTotalTasks));
|
|
return iIndex;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* NextPrevPhwnd
|
|
*
|
|
* Given a pointer to one entry in the window list, this can return
|
|
* the pointer to the next/prev entry in a circular list fashion.
|
|
*
|
|
* History:
|
|
* 01-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
PHWND NextPrevPhwnd(
|
|
PSWINFO pswInfo,
|
|
PHWND phwnd,
|
|
BOOL fNext)
|
|
{
|
|
PBWL pbwl;
|
|
PHWND phwndStart;
|
|
PHWND phwndLast;
|
|
|
|
pbwl = pswInfo->pbwl;
|
|
phwndStart = &(pbwl->rghwnd[0]);
|
|
phwndLast = pswInfo->phwndLast;
|
|
|
|
UserAssert(*phwndLast == (HWND)1); // Last entry must have a 1.
|
|
UserAssert(phwndStart < phwndLast); // There must be atleast one entry.
|
|
UserAssert(phwnd != phwndLast); // Can't be passing in an invalid entry.
|
|
|
|
if (fNext) {
|
|
phwnd++;
|
|
if (phwnd == phwndLast) {
|
|
phwnd = phwndStart;
|
|
}
|
|
} else {
|
|
if (phwnd == phwndStart) {
|
|
phwnd = phwndLast - 1; // we have atleast one valid entry.
|
|
} else {
|
|
phwnd--;
|
|
}
|
|
}
|
|
|
|
return phwnd;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* _IsTaskWindow
|
|
*
|
|
* History:
|
|
* 01-Jun-95 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
BOOL _IsTaskWindow(
|
|
PWND pwnd,
|
|
PWND pwndActive)
|
|
{
|
|
/*
|
|
* Following windows do not qualify to be shown in task list:
|
|
* Switch Window, Hidden windows (unless they are the active
|
|
* window), Disabled windows, Kanji Conv windows.
|
|
*
|
|
* Also, check for a combobox popup list which has the top-most
|
|
* style (it's spwndLastActive will be NULL).
|
|
*/
|
|
UserAssert(pwnd != NULL);
|
|
return( (TestWF(pwnd, WEFAPPWINDOW)
|
|
|| (!TestWF(pwnd, WEFTOOLWINDOW) && !TestWF(pwnd, WEFNOACTIVATE))) &&
|
|
(TestWF(pwnd, WFVISIBLE) || (pwnd == pwndActive)) &&
|
|
(!(pwnd->spwndLastActive && TestWF(pwnd->spwndLastActive, WFDISABLED))));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RemoveNonTaskWindows()
|
|
*
|
|
* Given a list of Windows, this walks down the list and removes the
|
|
* windows that do not qualify to be shown in the Task-Switch screen.
|
|
* This also shrinks the list when it removes some entries.
|
|
* Returns the total number of "tasks" (windows) qualified and remained
|
|
* in the list. The last entry will have a 1 as usual.
|
|
* It also returns a pointer to this last entry via params. It also
|
|
* returns the index of the Currently active task via params.
|
|
*
|
|
* History:
|
|
* 01-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
INT _RemoveNonTaskWindows(
|
|
PBWL pbwl,
|
|
PWND pwndActive,
|
|
LPINT lpiActiveTask,
|
|
PHWND *pphwndLast)
|
|
{
|
|
INT iTaskCount = 0;
|
|
PHWND phwnd;
|
|
PWND pwnd;
|
|
PWND pwndUse;
|
|
PWND pwndOwnee;
|
|
PHWND phwndHole;
|
|
|
|
*lpiActiveTask = -1;
|
|
|
|
/*
|
|
* Walk down the window list and do the following:
|
|
* 1. Remove all entries that do not qualify to be shown in the task list.
|
|
* 2. Count the total number of windows that qualify.
|
|
* 3. Get the pointer to the entry that contains current active window.
|
|
* 4. Get the pointer to the last dummy entry (that has 1 in it)
|
|
*/
|
|
for (phwndHole = phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
|
|
pwnd = RevalidateHwnd(*phwnd);
|
|
if (!pwnd) {
|
|
continue;
|
|
}
|
|
|
|
if (_IsTaskWindow(pwnd, pwndActive)) {
|
|
pwndUse = pwnd;
|
|
|
|
/*
|
|
* First let's find the task-list owner of this window.
|
|
*/
|
|
while (!TestWF(pwndUse, WEFAPPWINDOW) && pwndUse->spwndOwner) {
|
|
pwndOwnee = pwndUse;
|
|
pwndUse = pwndUse->spwndOwner;
|
|
if (TestWF(pwndUse, WEFTOOLWINDOW)) {
|
|
/*
|
|
* If this is the owner of a top level property sheet,
|
|
* show the property sheet.
|
|
*/
|
|
if (TestWF(pwndOwnee, WEFCONTROLPARENT) && (pwndUse->spwndOwner == NULL)) {
|
|
pwndUse = pwnd;
|
|
} else {
|
|
pwndUse = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pwndUse || !pwndUse->spwndLastActive) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* walk up from the last active window 'til we find a valid task
|
|
* list window or until we run out of windows in the ownership
|
|
* chain
|
|
*/
|
|
for (pwndUse = pwndUse->spwndLastActive; pwndUse; pwndUse = pwndUse->spwndOwner)
|
|
if (_IsTaskWindow(pwndUse, pwndActive))
|
|
break;
|
|
|
|
/*
|
|
* if we ran out of windows in the ownership chain then use the
|
|
* owned window itself -- or if we didn't run out of the ownership
|
|
* chain, then only include this window if it's the window in the
|
|
* ownership chain that we just found (VB will love us for it !)
|
|
* -- jeffbog -- 4/20/95 -- Win95C B#2821
|
|
*/
|
|
if (!pwndUse || (pwndUse == pwnd)) {
|
|
|
|
/*
|
|
* Do we have any holes above this? If so, move this handle to
|
|
* that hole.
|
|
*/
|
|
if (phwndHole < phwnd) {
|
|
|
|
/*
|
|
* Yes! There is a hole. Let us move the valid
|
|
* handle there.
|
|
*/
|
|
*phwndHole = *phwnd;
|
|
}
|
|
|
|
if (pwndActive == pwnd)
|
|
*lpiActiveTask = iTaskCount;
|
|
iTaskCount++;
|
|
phwndHole++; // Move to the next entry.
|
|
}
|
|
|
|
/*
|
|
* Else, leave it as a hole for later filling.
|
|
*/
|
|
}
|
|
}
|
|
|
|
*phwndHole = (HWND)1;
|
|
*pphwndLast = phwndHole;
|
|
|
|
return iTaskCount;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrawSwitchWndHilite()
|
|
*
|
|
* This draws or erases the Hilite we draw around the icon to show which
|
|
* task we are going to switch to.
|
|
* This also updates the name on the Task title window.
|
|
*
|
|
* History:
|
|
* 01-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
VOID DrawSwitchWndHilite(
|
|
PSWINFO pswInfo,
|
|
HDC hdcSwitch,
|
|
int iCol,
|
|
int iRow,
|
|
BOOL fShow)
|
|
{
|
|
BOOL fGetAndReleaseIt;
|
|
RECT rcTemp;
|
|
|
|
/*
|
|
* Draw or erase the hilite depending on "fShow".
|
|
*/
|
|
if (fGetAndReleaseIt = (hdcSwitch == NULL))
|
|
hdcSwitch = _GetDCEx(gspwndAltTab, NULL, DCX_USESTYLE);
|
|
|
|
rcTemp.left = pswInfo->ptFirstRowStart.x + iCol * CXICONSLOT;
|
|
rcTemp.top = pswInfo->ptFirstRowStart.y + iRow * CYICONSLOT;
|
|
rcTemp.right = rcTemp.left + CXICONSLOT;
|
|
rcTemp.bottom = rcTemp.top + CYICONSLOT;
|
|
|
|
DrawFrame(hdcSwitch,
|
|
&rcTemp,
|
|
2,
|
|
DF_PATCOPY | ((fShow ? COLOR_HIGHLIGHT : COLOR_3DFACE) << 3));
|
|
|
|
|
|
/*
|
|
* Update the Task title window.
|
|
*/
|
|
if (fShow) {
|
|
WCHAR szText[CCHTITLEMAX];
|
|
INT cch;
|
|
COLORREF clrOldText, clrOldBk;
|
|
PWND pwnd;
|
|
RECT rcRect;
|
|
HFONT hOldFont;
|
|
INT iLeft;
|
|
ULONG_PTR dwResult = 0;
|
|
|
|
clrOldText = GreSetTextColor(hdcSwitch, SYSRGB(BTNTEXT));
|
|
clrOldBk = GreSetBkColor(hdcSwitch, SYSRGB(3DFACE));
|
|
hOldFont = GreSelectFont(hdcSwitch, gpsi->hCaptionFont);
|
|
|
|
|
|
/*
|
|
* Validate this window handle; This could be some app that terminated
|
|
* in the background and the following line will GP fault in that case;
|
|
* BOGUS: We should handle it some other better way.
|
|
*/
|
|
pwnd = RevalidateHwnd( *(pswInfo->phwndCurrent) );
|
|
if (pwnd) {
|
|
/*
|
|
* Get the window's title.
|
|
*/
|
|
if (pwnd->strName.Length) {
|
|
cch = TextCopy(&pwnd->strName, szText, CCHTITLEMAX);
|
|
} else {
|
|
*szText = TEXT('\0');
|
|
cch = 0;
|
|
}
|
|
|
|
/*
|
|
* Draw the text
|
|
*/
|
|
CopyRect(&rcRect, &pswInfo->rcTaskName);
|
|
iLeft = rcRect.left;
|
|
FillRect(hdcSwitch, &rcRect, SYSHBR(3DFACE));
|
|
/*
|
|
* If an lpk is installed let it draw the text.
|
|
*/
|
|
if (CALL_LPK(PtiCurrentShared())) {
|
|
TL tlpwnd;
|
|
LPKDRAWSWITCHWND LpkDrawSwitchWnd;
|
|
|
|
RtlInitLargeUnicodeString(&LpkDrawSwitchWnd.strName, szText, (UINT)-1);
|
|
LpkDrawSwitchWnd.rcRect = rcRect;
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
xxxSendMessageTimeout(pwnd, WM_LPKDRAWSWITCHWND, (WPARAM)hdcSwitch,
|
|
(LPARAM)&LpkDrawSwitchWnd, SMTO_ABORTIFHUNG, 100, &dwResult);
|
|
ThreadUnlock(&tlpwnd);
|
|
} else {
|
|
DRAWTEXTPARAMS dtp;
|
|
|
|
dtp.cbSize = sizeof(dtp);
|
|
dtp.iLeftMargin = 0;
|
|
dtp.iRightMargin = 0;
|
|
DrawTextEx(hdcSwitch, szText, cch, &rcRect, DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE, &dtp );
|
|
}
|
|
}
|
|
|
|
GreSelectFont(hdcSwitch, hOldFont);
|
|
GreSetBkColor(hdcSwitch, clrOldBk);
|
|
GreSetTextColor(hdcSwitch, clrOldText);
|
|
}
|
|
|
|
if (fGetAndReleaseIt)
|
|
_ReleaseDC(hdcSwitch);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DrawIconCallBack
|
|
*
|
|
* This function is called by a Windows app returning his icon.
|
|
*
|
|
* History:
|
|
* 17-Jun-1993 mikesch Created.
|
|
\***************************************************************************/
|
|
|
|
VOID CALLBACK DrawIconCallBack(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
ULONG_PTR dwData,
|
|
LRESULT lResult)
|
|
{
|
|
PWND pwndAltTab;
|
|
|
|
/*
|
|
* dwData is the pointer to the switch window handle.
|
|
* If this Alt+Tab instance is still active, we need to derive this
|
|
* window's index in the bwl array, otherwise, we are receiving an icon
|
|
* for an old Alt+Tab window.
|
|
*/
|
|
pwndAltTab = RevalidateHwnd((HWND)dwData);
|
|
if (pwndAltTab && TestWF(pwndAltTab, WFVISIBLE)) {
|
|
|
|
PSWINFO pswCurrent;
|
|
PICON pIcon;
|
|
PHWND phwnd;
|
|
PWND pwnd;
|
|
PWND pwndT;
|
|
INT iStartTaskIndex;
|
|
TL tlpwndAltTab;
|
|
|
|
/*
|
|
* Derive this window's index in the BWL array
|
|
*/
|
|
if ((pwnd = RevalidateHwnd(hwnd)) == NULL)
|
|
return;
|
|
|
|
/*
|
|
* Get the switch window info
|
|
*/
|
|
pswCurrent = Getpswi(pwndAltTab);
|
|
if (!pswCurrent)
|
|
return;
|
|
|
|
for (iStartTaskIndex = 0, phwnd=&(pswCurrent->pbwl->rghwnd[0]); *phwnd != (HWND)1; phwnd++, iStartTaskIndex++) {
|
|
/*
|
|
* Because we list the active window in the Switch Window, the
|
|
* hwnd here might not be the same, so we also need to walk back
|
|
* to the top-level window to see if this is the right entry
|
|
* in the list.
|
|
*/
|
|
for(pwndT = RevalidateHwnd(*phwnd); pwndT; pwndT = pwndT->spwndOwner) {
|
|
if (pwnd == pwndT)
|
|
goto DrawIcon;
|
|
}
|
|
}
|
|
return;
|
|
|
|
/*
|
|
* Convert the App's HICON into a PICON, or if the App did not return
|
|
* an icon, use the Windows default icon.
|
|
*/
|
|
DrawIcon:
|
|
pIcon = NULL;
|
|
if (lResult)
|
|
pIcon = HMValidateHandleNoRip((HCURSOR)lResult, TYPE_CURSOR);
|
|
|
|
if (!pIcon)
|
|
pIcon = SYSICO(WINLOGO);
|
|
|
|
/*
|
|
* Paint this icon in the Alt+Tab window.
|
|
*/
|
|
ThreadLockAlways(pwndAltTab, &tlpwndAltTab);
|
|
xxxPaintIconsInSwitchWindow(pwndAltTab,
|
|
pswCurrent,
|
|
NULL,
|
|
iStartTaskIndex,
|
|
0,
|
|
1,
|
|
FALSE,
|
|
FALSE,
|
|
pIcon);
|
|
ThreadUnlock(&tlpwndAltTab);
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(uMsg);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* TSW_CalcRowAndCol
|
|
*
|
|
* History:
|
|
* 01-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
BOOL TSW_CalcRowAndCol(
|
|
PSWINFO pswInfo,
|
|
INT iTaskIndex,
|
|
LPINT lpiRow,
|
|
LPINT lpiCol)
|
|
{
|
|
INT iDiff;
|
|
INT iRow;
|
|
|
|
/*
|
|
* Calculate how far is the given task from the first task shown
|
|
* on the switch window.
|
|
*/
|
|
if ((iDiff = (iTaskIndex - pswInfo->iFirstTaskIndex)) < 0)
|
|
iDiff += pswInfo->iTotalTasks;
|
|
|
|
/*
|
|
* Calculate the Row and if this lies outside the switch window, return FALSE
|
|
*/
|
|
if ((iRow = iDiff / pswInfo->iNoOfColumns) >= pswInfo->iNoOfRows)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Return the Row and column where this task lies.
|
|
*/
|
|
*lpiRow = iRow;
|
|
*lpiCol = iDiff - (iRow * pswInfo->iNoOfColumns);
|
|
|
|
return TRUE; // This task lies within the switch window.
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxPaintIconsInSwitchWindow()
|
|
*
|
|
* This can simply paint the icons in the switch window or Scroll the
|
|
* whole window UP/DOWN and then paint the remaining area;
|
|
* * If fScroll is TRUE, then the second, third and fourth params are ignored.
|
|
* * If hIcon is passed in, then we're being called by DrawIconCallBack and
|
|
* iStartRow parameter is ignored in this case.
|
|
*
|
|
* History:
|
|
* 02-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
VOID xxxPaintIconsInSwitchWindow(
|
|
PWND pwndAltTab,
|
|
PSWINFO pswInfo,
|
|
HDC hdc,
|
|
INT iStartTaskIndex,
|
|
INT iStartRow,
|
|
INT iNoOfIcons,
|
|
BOOL fScroll,
|
|
BOOL fUp,
|
|
PICON pIcon)
|
|
{
|
|
INT cx, cy, xStart;
|
|
PHWND phwnd;
|
|
BOOL fGetAndReleaseIt;
|
|
INT iColumnIndex = 0;
|
|
RECT rcScroll;
|
|
PWND pwnd;
|
|
TL tlpwnd;
|
|
HICON hIcon;
|
|
RECT rcIcon;
|
|
|
|
CheckLock(pwndAltTab);
|
|
|
|
/*
|
|
* If we were not supplied a DC, get ghwndSwitch's and set a flag
|
|
* so we remember to release it.
|
|
*/
|
|
if (fGetAndReleaseIt = (hdc == NULL))
|
|
hdc = _GetDCEx(pwndAltTab, NULL, DCX_USESTYLE);
|
|
|
|
cx = pswInfo->ptFirstRowStart.x;
|
|
cy = pswInfo->ptFirstRowStart.y;
|
|
|
|
if (fScroll) {
|
|
|
|
rcScroll.left = cx;
|
|
rcScroll.top = cy;
|
|
rcScroll.right = cx + CXICONSLOT * pswInfo->iNoOfColumns;
|
|
rcScroll.bottom = cy + CYICONSLOT * pswInfo->iNoOfRows;
|
|
|
|
_ScrollDC(hdc,
|
|
0,
|
|
(fUp ? -CYICONSLOT : CYICONSLOT),
|
|
&rcScroll,
|
|
&rcScroll,
|
|
NULL,
|
|
NULL);
|
|
|
|
iStartRow = (fUp ? pswInfo->iNoOfRows - 1 : 0);
|
|
iNoOfIcons = pswInfo->iNoOfColumns;
|
|
iStartTaskIndex = (fUp ? NextPrevTaskIndex(pswInfo, pswInfo->iFirstTaskIndex,
|
|
(pswInfo->iNoOfRows - 1) * pswInfo->iNoOfColumns, TRUE) :
|
|
pswInfo->iFirstTaskIndex);
|
|
}
|
|
|
|
if (pIcon) {
|
|
/*
|
|
* If pIcon is given, this is to paint just one icon during callback.
|
|
*/
|
|
// BradG - Win95 Assert
|
|
UserAssert(iNoOfIcons == 1);
|
|
|
|
/*
|
|
* Due to earlier scrolling, the row number would have changed. So,
|
|
* recalc the row and column from the iStartTaskIndex given.
|
|
*/
|
|
if (!TSW_CalcRowAndCol(pswInfo, iStartTaskIndex, &iStartRow, &iColumnIndex))
|
|
goto Cleanup;
|
|
}
|
|
|
|
xStart = cx += (CXICONSLOT - CXICONSIZE) / 2;
|
|
cx += iColumnIndex * CXICONSLOT;
|
|
cy += ((CYICONSLOT - CYICONSIZE) / 2) + iStartRow * CYICONSLOT;
|
|
phwnd = &(pswInfo->pbwl->rghwnd[iStartTaskIndex]);
|
|
|
|
/*
|
|
* Draw all the icons one by one.
|
|
*/
|
|
while (iNoOfIcons--) {
|
|
/*
|
|
* If the Alt+Key is no longer down, abort painting icons.
|
|
*/
|
|
if ((pswInfo->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
|
|
(!pswInfo->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
|
|
goto Cleanup;
|
|
|
|
/*
|
|
* Check if this window is still alive. (Some task could have
|
|
* terminated in the background)
|
|
*/
|
|
if (pwnd = RevalidateHwnd(*phwnd)) {
|
|
/*
|
|
* Find the window's top-level owner
|
|
*/
|
|
pwnd = DSW_GetTopLevelCreatorWindow(pwnd);
|
|
|
|
/*
|
|
* If we don't have an icon, find one
|
|
*/
|
|
if (!pIcon) {
|
|
/*
|
|
* Try window icon
|
|
*/
|
|
hIcon = (HICON)_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconProp), PROPF_INTERNAL);
|
|
if (hIcon) {
|
|
pIcon = (PICON)HMValidateHandleNoRip(hIcon, TYPE_CURSOR);
|
|
}
|
|
|
|
/*
|
|
* If we don't have an icon yet, try the class icon
|
|
*/
|
|
if (!pIcon) {
|
|
pIcon = pwnd->pcls->spicn;
|
|
}
|
|
|
|
/*
|
|
* If we don't have an icon yet, use WM_QUERYDRAGICON to ask
|
|
* 3,x apps for their icon.
|
|
*/
|
|
if (!pIcon && !TestWF(pwnd, WFWIN40COMPAT)) {
|
|
/*
|
|
* The callback routine will paint the icon for
|
|
* us, so just leave pIcon set to NULL
|
|
*/
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
xxxSendMessageCallback(pwnd, WM_QUERYDRAGICON, 0, 0,
|
|
(SENDASYNCPROC)DrawIconCallBack,
|
|
HandleToUlong(PtoH(pwndAltTab)), FALSE);
|
|
ThreadUnlock(&tlpwnd);
|
|
} else {
|
|
/*
|
|
* If we can't find an icon, so use the Windows icon
|
|
*/
|
|
if (!pIcon) {
|
|
pIcon = SYSICO(WINLOGO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pIcon) {
|
|
_DrawIconEx(hdc, cx, cy, pIcon, SYSMET(CXICON), SYSMET(CYICON),
|
|
0, SYSHBR(3DFACE), DI_NORMAL);
|
|
} else if (fScroll) {
|
|
/*
|
|
* NOT IN WIN95
|
|
*
|
|
* No icon was available, do while we are waiting for the
|
|
* application to paint it's icon, we need to "erase" the
|
|
* background in case we have scrolled the window.
|
|
*/
|
|
rcIcon.left = cx;
|
|
rcIcon.top = cy;
|
|
rcIcon.right = cx + SYSMET(CXICON);
|
|
rcIcon.bottom = cy + SYSMET(CYICON);
|
|
FillRect(hdc, &rcIcon, SYSHBR(3DFACE));
|
|
}
|
|
|
|
/*
|
|
* Check if we are done.
|
|
*/
|
|
if (iNoOfIcons <= 0)
|
|
break;
|
|
|
|
/*
|
|
* Reset hIcon for the next run through the loop
|
|
*/
|
|
pIcon = NULL;
|
|
|
|
/*
|
|
* Move all pointers to the next task/icon.
|
|
*/
|
|
phwnd = NextPrevPhwnd(pswInfo, phwnd, TRUE); // Get next.
|
|
|
|
/*
|
|
* Is it going to be in the same row; then adjust cx and cy.
|
|
*/
|
|
if (++iColumnIndex >= pswInfo->iNoOfColumns) {
|
|
iColumnIndex = 0;
|
|
cx = xStart; // Move to first column
|
|
cy += CYICONSLOT; // Move to next row.
|
|
iStartRow++;
|
|
} else {
|
|
/*
|
|
* else, adjust cx;
|
|
*/
|
|
cx += CXICONSLOT;
|
|
}
|
|
|
|
iStartTaskIndex = NextPrevTaskIndex(pswInfo, iStartTaskIndex, 1, TRUE);
|
|
}
|
|
|
|
Cleanup:
|
|
if (fGetAndReleaseIt)
|
|
_ReleaseDC(hdc);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PaintSwitchWindow
|
|
*
|
|
* History:
|
|
* 02-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
VOID xxxPaintSwitchWindow(
|
|
PWND pwndSwitch)
|
|
{
|
|
LPRECT lprcRect;
|
|
RECT rcRgn;
|
|
HDC hdcSwitch;
|
|
PSWINFO pswCurrent;
|
|
CheckLock(pwndSwitch);
|
|
|
|
/*
|
|
* If our window isn't visible, return
|
|
*/
|
|
if (!TestWF(pwndSwitch, WFVISIBLE))
|
|
return;
|
|
|
|
/*
|
|
* Get the switch window information
|
|
*/
|
|
pswCurrent = Getpswi(pwndSwitch);
|
|
if (!pswCurrent)
|
|
return;
|
|
|
|
/*
|
|
* Get the Switch windows DC so we can paint with it
|
|
*/
|
|
hdcSwitch = _GetDCEx(pwndSwitch, NULL, DCX_USESTYLE );
|
|
|
|
/*
|
|
* Paint the background of the Switch Screen.
|
|
*/
|
|
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
|
|
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
|
|
goto PSWExit;
|
|
|
|
lprcRect = &(pswCurrent->rcTaskName);
|
|
_GetClientRect(pwndSwitch, lprcRect);
|
|
FillRect(hdcSwitch, lprcRect, SYSHBR(3DFACE));
|
|
|
|
/*
|
|
* Store this "caption" area back into the current switch
|
|
* window data structure.
|
|
*/
|
|
InflateRect(lprcRect, -(gcxCaptionFontChar << 1), -(gcyCaptionFontChar));
|
|
lprcRect->top = lprcRect->bottom - gcyCaptionFontChar;
|
|
|
|
/*
|
|
* Draw the sunken edge for showing the task names.
|
|
*/
|
|
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
|
|
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
|
|
goto PSWExit;
|
|
CopyInflateRect(&rcRgn, lprcRect, gcxCaptionFontChar >> 1, gcyCaptionFontChar >> 1);
|
|
DrawEdge(hdcSwitch, &rcRgn, EDGE_SUNKEN, BF_RECT);
|
|
|
|
/*
|
|
* Paint the icons
|
|
*/
|
|
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
|
|
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
|
|
goto PSWExit;
|
|
|
|
xxxPaintIconsInSwitchWindow(pwndSwitch,
|
|
pswCurrent,
|
|
hdcSwitch,
|
|
pswCurrent->iFirstTaskIndex,
|
|
0,
|
|
pswCurrent->iTasksShown,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
|
|
/*
|
|
* So, just draw the hilite.
|
|
*/
|
|
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
|
|
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
|
|
goto PSWExit;
|
|
|
|
DrawSwitchWndHilite(pswCurrent,
|
|
hdcSwitch,
|
|
pswCurrent->iCurCol,
|
|
pswCurrent->iCurRow,
|
|
TRUE);
|
|
|
|
/*
|
|
* Release the switch windows DC
|
|
*/
|
|
PSWExit:
|
|
_ReleaseDC(hdcSwitch);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SwitchWndCleanup()
|
|
*
|
|
* Clean up all the mem allocated etc.,
|
|
*
|
|
* History:
|
|
* 07-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
VOID SwitchWndCleanup(
|
|
PSWINFO *ppswInfo)
|
|
{
|
|
UserAssert(ppswInfo != NULL);
|
|
UserAssert(*ppswInfo != NULL);
|
|
|
|
/*
|
|
* First of all free the Window list.
|
|
*/
|
|
if ((*ppswInfo)->pbwl)
|
|
FreeHwndList((*ppswInfo)->pbwl);
|
|
UserFreePool(*ppswInfo);
|
|
*ppswInfo = NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* AddSwitchWindowInfo
|
|
*
|
|
* 09-12-01 MSadek Created
|
|
\***************************************************************************/
|
|
VOID AddSwitchWindowInfo(
|
|
PSWINFO pswInfo)
|
|
{
|
|
CheckCritIn();
|
|
pswInfo->pswiNext = gpswiFirst;
|
|
gpswiFirst = pswInfo;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RemoveSwitchWindowInfo
|
|
*
|
|
* 09-12-01 MSadek Created
|
|
\***************************************************************************/
|
|
VOID RemoveSwitchWindowInfo(
|
|
PSWINFO *ppswInfo)
|
|
{
|
|
PSWINFO* ppswi;
|
|
PSWINFO pswiT;
|
|
|
|
CheckCritIn();
|
|
|
|
for (ppswi = &gpswiFirst; *ppswi != NULL; ppswi = &(*ppswi)->pswiNext) {
|
|
pswiT = *ppswi;
|
|
if (pswiT == *ppswInfo) {
|
|
*ppswi = pswiT->pswiNext;
|
|
SwitchWndCleanup(ppswInfo);
|
|
}
|
|
|
|
if (*ppswi == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RemoveThreadSwitchWindowInfo
|
|
*
|
|
* 09-12-01 MSadek Created
|
|
\***************************************************************************/
|
|
VOID RemoveThreadSwitchWindowInfo(
|
|
PTHREADINFO pti)
|
|
{
|
|
PSWINFO* ppswi;
|
|
PSWINFO pswiT;
|
|
|
|
CheckCritIn();
|
|
|
|
for (ppswi = &gpswiFirst; *ppswi != NULL; ppswi = &(*ppswi)->pswiNext) {
|
|
pswiT = *ppswi;
|
|
if (pswiT->pti == pti) {
|
|
*ppswi = pswiT->pswiNext;
|
|
SwitchWndCleanup(&pswiT);
|
|
}
|
|
|
|
if (*ppswi == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitSwitchWndInfo
|
|
*
|
|
* This function allocs and Initializes all the data structures
|
|
* required the build and show the tasks in the system.
|
|
* If there is insufficient mem, then this find the next window to switch
|
|
* to and returns it. In this case, we will behave as if the end user hit
|
|
* ALT+ESC. The SWitchScreen will not comeup in this case.
|
|
* If there is only one task in the whole system, then this function
|
|
* fails and returns a NULL. (No ALT+TAB processing is required).
|
|
* Otherwise, it allocs one SwitchWndInfo struc, fills it up and returns
|
|
* the window we gonna switch to.
|
|
*
|
|
* History:
|
|
* 02-Jun-95 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
PWND InitSwitchWndInfo(
|
|
PSWINFO * lppswInfo,
|
|
PWND pwndCurActive,
|
|
BOOL fPrev)
|
|
{
|
|
PBWL pbwl;
|
|
INT iTotalTasks;
|
|
INT iCols, iRows, iIconsInLastRow;
|
|
INT iDiff;
|
|
PHWND phwndLast;
|
|
PSWINFO pswInfo;
|
|
INT iIconIndex;
|
|
INT iCurRow, iCurCol;
|
|
INT cxSwitch, cySwitch;
|
|
INT iFirstRowIcons;
|
|
INT iActiveTask;
|
|
PWND pwnd = NULL;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiCurrent);
|
|
PMONITOR pMonitor = GetPrimaryMonitor();
|
|
|
|
/*
|
|
* Initialize the list
|
|
*/
|
|
*lppswInfo = (PSWINFO)NULL;
|
|
|
|
/*
|
|
* Build the Window list of all the top level windows.
|
|
*/
|
|
#if 0
|
|
if (!(pbwl = BuildHwndList(NULL, BWL_ENUMLIST | BWL_ALLDESKTOPS, NULL)))
|
|
goto ReturnNextWnd;
|
|
#else
|
|
// BradG - HACK, enumerate on current desktop!
|
|
// For the long run, we will need to enumerate all desktops
|
|
// This will be tricky because we need to check the security of
|
|
// each desktop, thus needing the user's security "token".
|
|
if (!(pbwl = BuildHwndList(pdeskinfo->spwnd->spwndChild, BWL_ENUMLIST, NULL))) {
|
|
#ifdef COOLSWITCHTRACE
|
|
DbgPrint("CoolSwitch: BuildHwndList failed (contact bradg).\n");
|
|
UserAssert(pbwl != NULL);
|
|
#endif
|
|
goto ReturnNextWnd;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Walk down the list and remove all non-task windows from the list.
|
|
* Replace those hwnds with 0.
|
|
*/
|
|
if ((iTotalTasks = _RemoveNonTaskWindows(pbwl, pwndCurActive, &iActiveTask, &phwndLast)) < 2) {
|
|
if (iTotalTasks == 1) {
|
|
/*
|
|
* If we have only one window and it's in full screen mode, we will
|
|
* return the shell window so the can switch back to GDI mode.
|
|
*/
|
|
pwnd = RevalidateHwnd(pbwl->rghwnd[0]);
|
|
if (pwnd && GetFullScreen(pwnd) == FULLSCREEN && pwndCurActive == pwnd)
|
|
pwnd = pdeskinfo->spwndShell;
|
|
|
|
} else {
|
|
pwnd = pdeskinfo->spwndShell;
|
|
}
|
|
#ifdef COOLSWITCHTRACE
|
|
DbgPrint("CoolSwitch: Not enough windows to switch.\n");
|
|
#endif
|
|
goto FreeAndReturnNextWnd; // If there isn't even two tasks, no switch wnd processing.
|
|
}
|
|
|
|
/*
|
|
* Allocate the Switch Info structure. If we don't have enough
|
|
* memory, act as if we are doing Alt+Esc.
|
|
*/
|
|
if (!(pswInfo = (PSWINFO)UserAllocPoolWithQuota(sizeof(SWITCHWNDINFO), TAG_ALTTAB))) {
|
|
#ifdef COOLSWITCHTRACE
|
|
DbgPrint("CoolSwitch: UserAllocPool failed on 0x%X bytes (contact bradg).\n", sizeof(SWITCHWNDINFO));
|
|
UserAssert(pswInfo != NULL);
|
|
#endif
|
|
goto FreeAndReturnNextWnd; // Unable to alloc SwitchWndInfo struct.
|
|
}
|
|
|
|
pswInfo->pti = ptiCurrent;
|
|
pswInfo->pbwl = pbwl;
|
|
pswInfo->phwndLast = phwndLast;
|
|
pswInfo->iTasksShown = pswInfo->iTotalTasks = iTotalTasks;
|
|
|
|
/*
|
|
* Get the next/prev window that must become active.
|
|
*/
|
|
iIconIndex = NextPrevTaskIndex(pswInfo, iActiveTask, 1, !fPrev);
|
|
pswInfo->phwndCurrent = &(pbwl->rghwnd[iIconIndex]);
|
|
|
|
iCols = min(gnFastAltTabColumns, iTotalTasks);
|
|
iRows = iTotalTasks / iCols; // Truncation might occur.
|
|
|
|
iIconsInLastRow = iTotalTasks - iRows * iCols;
|
|
iRows += (iIconsInLastRow ? 1 : 0); // Take care of earlier truncation.
|
|
|
|
/*
|
|
* Restrict the number of rows to just MAXROWSALLOWED (3)
|
|
*/
|
|
if (iRows > gnFastAltTabRows) {
|
|
iRows = gnFastAltTabRows;
|
|
pswInfo->fScroll = TRUE; // We need to scroll.
|
|
iIconsInLastRow = iCols;
|
|
pswInfo->iTasksShown = iCols * iRows;
|
|
} else {
|
|
pswInfo->fScroll = FALSE;
|
|
}
|
|
|
|
pswInfo->iNoOfColumns = iCols;
|
|
pswInfo->iNoOfRows = iRows;
|
|
|
|
if (iIconsInLastRow == 0)
|
|
iIconsInLastRow = pswInfo->iNoOfColumns; // Last Row is full.
|
|
pswInfo->iIconsInLastRow = iIconsInLastRow;
|
|
|
|
/*
|
|
* Find out Row and Col where the next/prev icon will lie.
|
|
*/
|
|
if (iIconIndex >= (iRows * iCols)) {
|
|
/*
|
|
* Next Icon lies outside. Bring it to the center.
|
|
*/
|
|
iCurRow = (iRows >> 2) + 1;
|
|
iCurCol = (iCols >> 2) + 1;
|
|
iDiff = (iIconIndex - ((iCurRow * iCols) + iCurCol));
|
|
} else {
|
|
iDiff = 0;
|
|
iCurRow = iIconIndex / iCols;
|
|
iCurCol = iIconIndex - (iCurRow * iCols);
|
|
}
|
|
|
|
pswInfo->iFirstTaskIndex = iDiff;
|
|
pswInfo->iCurRow = iCurRow;
|
|
pswInfo->iCurCol = iCurCol;
|
|
|
|
/*
|
|
* Calculate the Switch Window Dimensions.
|
|
*/
|
|
cxSwitch = min(
|
|
pMonitor->rcMonitor.right - pMonitor->rcMonitor.left,
|
|
gnFastAltTabColumns * CXICONSLOT +
|
|
CXICONSIZE / 2 +
|
|
6 * gpsi->gclBorder * SYSMET(CXBORDER) +
|
|
gcxCaptionFontChar);
|
|
|
|
cySwitch = min(
|
|
pMonitor->rcMonitor.bottom - pMonitor->rcMonitor.top,
|
|
iRows * CYICONSLOT +
|
|
CYICONSIZE +
|
|
gcyCaptionFontChar * 2 +
|
|
gcyCaptionFontChar / 2);
|
|
|
|
/*
|
|
* Find the number of icons in first row
|
|
*/
|
|
if (iRows == 1) {
|
|
iFirstRowIcons = iIconsInLastRow;
|
|
} else {
|
|
iFirstRowIcons = iCols;
|
|
}
|
|
|
|
/*
|
|
* Center the icons based on the number of icons in first row.
|
|
*/
|
|
pswInfo->ptFirstRowStart.x = (cxSwitch - 4*gpsi->gclBorder*SYSMET(CXBORDER) - iFirstRowIcons * CXICONSLOT) >> 1;
|
|
pswInfo->ptFirstRowStart.y = (CYICONSIZE >> 1);
|
|
|
|
pswInfo->cxSwitch = cxSwitch;
|
|
pswInfo->cySwitch = cySwitch;
|
|
|
|
AddSwitchWindowInfo(pswInfo);
|
|
*lppswInfo = pswInfo;
|
|
|
|
return RevalidateHwnd(*(pswInfo->phwndCurrent)); // Success!
|
|
|
|
|
|
/*
|
|
* When there is insufficient mem to create the reqd structures, we simply
|
|
* return the next window. We make the phwndInfo as NULL. So, we won't
|
|
* attempt to draw the switch window.
|
|
*/
|
|
|
|
FreeAndReturnNextWnd:
|
|
FreeHwndList(pbwl);
|
|
ReturnNextWnd:
|
|
if (pwnd)
|
|
return(pwnd);
|
|
|
|
return(_GetNextQueueWindow(pwndCurActive, _GetKeyState(VK_SHIFT) < 0, FALSE));
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxMoveSwitchWndHilite()
|
|
*
|
|
* This moves the Hilite to the next/prev icon.
|
|
* Checks if this move results in a scrolling. If it does, then
|
|
* make sure scroll occurs.
|
|
* Else, erase hilite from the current icon;
|
|
* Then draw hilite on the new Icon.
|
|
* fPrev indicates whether you want the prev task or next.
|
|
*
|
|
* History:
|
|
* 02-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
HWND xxxMoveSwitchWndHilite(
|
|
PWND pwndSwitch,
|
|
PSWINFO pswInfo,
|
|
BOOL fPrev)
|
|
{
|
|
INT iCurCol, iCurRow;
|
|
INT iMaxColumns;
|
|
BOOL fLastRow;
|
|
BOOL fNeedToScroll = FALSE;
|
|
HDC hdc;
|
|
HWND hwnd;
|
|
|
|
CheckLock(pwndSwitch);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
iCurCol = pswInfo->iCurCol;
|
|
iCurRow = pswInfo->iCurRow;
|
|
|
|
/*
|
|
* Find out the new postion (row and column) of hilite.
|
|
*/
|
|
if (fPrev) {
|
|
if (iCurCol > 0) {
|
|
/*
|
|
* Move cursor to prev column on the same row.
|
|
*/
|
|
iCurCol--;
|
|
} else {
|
|
/*
|
|
* Try to move to the previous row.
|
|
*/
|
|
if (iCurRow > 0) {
|
|
/*
|
|
* Move to the last column on the previous row.
|
|
*/
|
|
iCurRow--;
|
|
iCurCol = pswInfo->iNoOfColumns - 1;
|
|
} else {
|
|
/*
|
|
* We are already at (0,0); See if we need to scroll.
|
|
*/
|
|
if (pswInfo->fScroll) {
|
|
/*
|
|
* Time to scroll; Scroll by one Row;
|
|
* Repaint the whole window.
|
|
*/
|
|
fNeedToScroll = TRUE;
|
|
pswInfo->iFirstTaskIndex = NextPrevTaskIndex(pswInfo, pswInfo->iFirstTaskIndex,
|
|
pswInfo->iNoOfColumns, FALSE);
|
|
iCurCol = pswInfo->iNoOfColumns - 1;
|
|
} else {
|
|
/*
|
|
* Move the hilite to the last icon shown.
|
|
*/
|
|
iCurRow = pswInfo->iNoOfRows - 1;
|
|
iCurCol = pswInfo->iIconsInLastRow - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
/*
|
|
* !fPrev
|
|
* Get the number of columns in the current row.
|
|
*/
|
|
if (fLastRow = (iCurRow == (pswInfo->iNoOfRows - 1))) // Are we at the last row?
|
|
iMaxColumns = pswInfo->iIconsInLastRow;
|
|
else
|
|
iMaxColumns = pswInfo->iNoOfColumns;
|
|
|
|
/*
|
|
* Are we at the last column yet?
|
|
*/
|
|
if (iCurCol < (iMaxColumns - 1)) {
|
|
/*
|
|
* No! Move to the right.
|
|
*/
|
|
iCurCol++;
|
|
} else {
|
|
/*
|
|
* We are at the last column.
|
|
* If we are not at last row, then move to next row.
|
|
*/
|
|
if (!fLastRow) {
|
|
iCurCol = 0;
|
|
iCurRow++;
|
|
} else {
|
|
/*
|
|
* We are at the last row, last col;
|
|
* See if we need to scroll.
|
|
*/
|
|
if (pswInfo->fScroll) {
|
|
fNeedToScroll = TRUE;
|
|
pswInfo->iFirstTaskIndex = NextPrevTaskIndex(pswInfo, pswInfo->iFirstTaskIndex,
|
|
pswInfo->iNoOfColumns, TRUE);
|
|
iCurCol = 0;
|
|
} else {
|
|
/*
|
|
* Move to the top left corner (0, 0).
|
|
*/
|
|
iCurRow = iCurCol = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Move the phwnd to the next/prev
|
|
*/
|
|
pswInfo->phwndCurrent = NextPrevPhwnd(pswInfo, pswInfo->phwndCurrent, !fPrev);
|
|
|
|
/*
|
|
* Remove Hilite from the current location.
|
|
*/
|
|
hdc = _GetDCEx(pwndSwitch, NULL, DCX_USESTYLE);
|
|
DrawSwitchWndHilite(pswInfo, hdc, pswInfo->iCurCol, pswInfo->iCurRow, FALSE);
|
|
|
|
pswInfo->iCurRow = iCurRow;
|
|
pswInfo->iCurCol = iCurCol;
|
|
hwnd = (*(pswInfo->phwndCurrent));
|
|
|
|
/*
|
|
* Repaint if needed.
|
|
*/
|
|
if (fNeedToScroll)
|
|
xxxPaintIconsInSwitchWindow(pwndSwitch, pswInfo, hdc, pswInfo->iFirstTaskIndex, 0, 0, TRUE, !fPrev, NULL);
|
|
|
|
/*
|
|
* Draw Hilite at the new location.
|
|
*/
|
|
DrawSwitchWndHilite(pswInfo, hdc, iCurCol, iCurRow, TRUE);
|
|
|
|
_ReleaseDC(hdc);
|
|
|
|
|
|
xxxWindowEvent(EVENT_OBJECT_FOCUS, pwndSwitch, OBJID_CLIENT,
|
|
iCurRow * pswInfo->iNoOfColumns + iCurCol + 1, WEF_USEPWNDTHREAD);
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxShowSwitchWindow()
|
|
*
|
|
* Show the switch Window.
|
|
* Returns: TRUE if succeeded. FALSE, if the window was not shown because
|
|
* of the pre-mature release of ALT key. The selection has been made already.
|
|
*
|
|
* History:
|
|
* 07-Jun-1995 BradG Ported from Win95
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxShowSwitchWindow(
|
|
PWND pwndAltTab)
|
|
{
|
|
PSWINFO pswInfo;
|
|
PMONITOR pMonitorSwitch = GetPrimaryMonitor();
|
|
CheckLock(pwndAltTab);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
/*
|
|
* Get the switch window information
|
|
*/
|
|
pswInfo = Getpswi(pwndAltTab);
|
|
if (pswInfo == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* If the key is not down, don't bother to display Switch Window.
|
|
*/
|
|
if ((pswInfo->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
|
|
(!pswInfo->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0)) {
|
|
#ifdef COOLSWITCHTRACE
|
|
DbgPrint("CoolSwitch: Not displaying window because VM_MENU is up (contact bradg).\n");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Bring and position the window on top.
|
|
*/
|
|
xxxSetWindowPos(pwndAltTab, PWND_TOPMOST, 0,0,0,0,
|
|
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW );
|
|
|
|
if (!TestWF(pwndAltTab, WFVISIBLE)) {
|
|
xxxSetWindowPos(
|
|
pwndAltTab,
|
|
PWND_TOPMOST,
|
|
(pMonitorSwitch->rcWork.left + pMonitorSwitch->rcWork.right - pswInfo->cxSwitch) / 2,
|
|
(pMonitorSwitch->rcWork.top + pMonitorSwitch->rcWork.bottom - pswInfo->cySwitch) / 2,
|
|
pswInfo->cxSwitch,
|
|
pswInfo->cySwitch,
|
|
SWP_SHOWWINDOW | SWP_NOACTIVATE);
|
|
}
|
|
|
|
#ifdef COOLSWITCHTRACE
|
|
UserAssert(TestWF(pwndAltTab, WFVISIBLE));
|
|
#endif
|
|
xxxUpdateWindow(pwndAltTab);
|
|
|
|
xxxWindowEvent(EVENT_SYSTEM_SWITCHSTART, pwndAltTab, OBJID_CLIENT,
|
|
0, WEF_USEPWNDTHREAD);
|
|
|
|
xxxWindowEvent(EVENT_OBJECT_FOCUS, pwndAltTab, OBJID_CLIENT,
|
|
pswInfo->iCurRow * pswInfo->iNoOfColumns + pswInfo->iCurCol + 1,
|
|
WEF_USEPWNDTHREAD);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* xxxSwitchWndProc()
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LRESULT xxxSwitchWndProc(
|
|
PWND pwnd,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
TL tlpwndActivate;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
CheckLock(pwnd);
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
VALIDATECLASSANDSIZE(pwnd, message, wParam, lParam, FNID_SWITCH, WM_CREATE);
|
|
|
|
switch (message) {
|
|
case WM_CREATE:
|
|
/*
|
|
* When the queue was created, the cursor was set to the wait cursor.
|
|
* We want to use the normal one.
|
|
*/
|
|
zzzSetCursor(pwnd->pcls->spcur);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
/*
|
|
* Hide this window without activating anyone else.
|
|
*/
|
|
xxxSetWindowPos(pwnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW |
|
|
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
|
|
|
|
/*
|
|
* Get us out of Alt+Tab mode. Since the alttab information
|
|
* is stored in the gptiRit->pq, we will reference that insteatd
|
|
* of the current-thread.
|
|
*/
|
|
xxxCancelCoolSwitch();
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
case WM_FULLSCREEN:
|
|
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwndActivate);
|
|
xxxPaintSwitchWindow(pwnd);
|
|
ThreadUnlock(&tlpwndActivate);
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
/*
|
|
* Get the switch window info for this window
|
|
*/
|
|
PSWINFO pswCurrent = Getpswi(pwnd);
|
|
|
|
|
|
if (pswCurrent) {
|
|
RemoveSwitchWindowInfo(&pswCurrent);
|
|
Setpswi(pwnd, NULL);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return xxxDefWindowProc(pwnd, message, wParam, lParam);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxCancelCoolSwitch
|
|
*
|
|
* This functions destroys the cool switch window and removed the INALTTAB
|
|
* mode flag from the specified queue.
|
|
*
|
|
* History:
|
|
* 18-Sep-1995 BradG Created
|
|
\***************************************************************************/
|
|
VOID xxxCancelCoolSwitch(
|
|
void)
|
|
{
|
|
CheckCritIn();
|
|
UserAssert(IsWinEventNotifyDeferredOK());
|
|
|
|
/*
|
|
* Destroy the Cool Switch window
|
|
*/
|
|
if (gspwndAltTab != NULL) {
|
|
PWND pwnd = gspwndAltTab;
|
|
/*
|
|
* Make sure that the thread calling this is the same thread which
|
|
* created the alttab window. Otherwise, we could end up with this
|
|
* window floating around until the calling process dies. Remember,
|
|
* we can't destroy windows across different threads.
|
|
*/
|
|
if (gspwndAltTab->head.pti != PtiCurrent()) {
|
|
return;
|
|
}
|
|
|
|
xxxWindowEvent(EVENT_SYSTEM_SWITCHEND, gspwndAltTab, OBJID_CLIENT,
|
|
0, WEF_USEPWNDTHREAD);
|
|
if (Unlock(&gspwndAltTab)) {
|
|
xxxDestroyWindow(pwnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxNextWindow
|
|
*
|
|
* This function does the processing for the alt-tab/esc/F6 UI.
|
|
*
|
|
* History:
|
|
* 30-May-1991 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
VOID xxxNextWindow(
|
|
PQ pq,
|
|
DWORD wParam)
|
|
{
|
|
PWND pwndActivateNext;
|
|
PWND pwndCurrentActivate, pwndCurrentTopFocus;
|
|
int fDir;
|
|
TL tlpwndCurrentTopFocus;
|
|
TL tlpwndActivateNext;
|
|
TL tlpwndCurrentActivate;
|
|
TL tlpwndT;
|
|
PSWINFO pswCurrent;
|
|
ULONG_PTR dwResult;
|
|
BOOL fNonRit = FALSE;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
UserAssert(!IsWinEventNotifyDeferred());
|
|
|
|
if (pq == NULL)
|
|
return;
|
|
|
|
fDir = (_GetAsyncKeyState(VK_SHIFT) < 0) ? FDIR_BACKWARD : FDIR_FORWARD;
|
|
|
|
pwndCurrentTopFocus = GetTopLevelWindow(pq->spwndFocus);
|
|
/*
|
|
* NOTE: As of NT 4.0 the slow Alt+Tab functionality now officially acts
|
|
* like Alt+Esc with the exception that Alt+Tab will activate the window
|
|
* where Alt+Esc will not.
|
|
*/
|
|
switch (wParam) {
|
|
|
|
case VK_TAB:
|
|
|
|
if (gspwndAltTab == NULL) {
|
|
|
|
PWND pwndSwitch;
|
|
TL tlpSwitchInfo;
|
|
|
|
/*
|
|
* We are entering Alt+Tab for the first time, we need to
|
|
* initialize the Switch Window structure and if needed
|
|
* create and display the Alt+Tab window. We have two special
|
|
* cases: (1) The user does not want to use the Switch window,
|
|
* (2) The initialize switch window fails thus we will act
|
|
* just like slow Alt+Tab
|
|
*/
|
|
|
|
/*
|
|
* Since Alt+Shift is the default hotkey for keyboard layout switching,
|
|
* Alt+Shift+Tab may cause a KL switching while AltTab window is up.
|
|
* To prevent it, we'd better reset the global toggle key state here,
|
|
* so that xxxScanSysQueue will not confuse when it handles keyup messages.
|
|
*/
|
|
gLangToggleKeyState = KLT_NONE;
|
|
|
|
/*
|
|
* Mouse buttons sometimes get stuck down due to hardware glitches,
|
|
* usually due to input concentrator switchboxes or faulty serial
|
|
* mouse COM ports, so clear the global button state here just in case,
|
|
* otherwise we may not be able to change focus with the mouse.
|
|
* Also do this in zzzCancelJournalling (Ctr-Esc, Ctrl-Alt-Del, etc.)
|
|
*/
|
|
#if DBG
|
|
if (gwMouseOwnerButton)
|
|
RIPMSG1(RIP_WARNING,
|
|
"gwMouseOwnerButton=%x, being forcibly cleared\n",
|
|
gwMouseOwnerButton);
|
|
#endif
|
|
gwMouseOwnerButton = 0;
|
|
|
|
/*
|
|
* Determine the current active window.
|
|
*/
|
|
Lock(&gspwndActivate, pq->spwndActive);
|
|
if (gspwndActivate == NULL) {
|
|
Lock(&gspwndActivate, grpdeskRitInput->pDeskInfo->spwnd->spwndChild);
|
|
}
|
|
|
|
if (!gspwndActivate) {
|
|
return;
|
|
}
|
|
|
|
ThreadLockWithPti(ptiCurrent, pwndCurrentTopFocus, &tlpwndCurrentTopFocus);
|
|
|
|
/*
|
|
* Make a local copy of gspwndActivate and lock it because xxxFreeWindow will
|
|
* unlock if it is the window being freed.
|
|
*/
|
|
pwndCurrentActivate = gspwndActivate;
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndCurrentActivate, &tlpwndCurrentActivate);
|
|
|
|
/*
|
|
* Cancel the active window's mode
|
|
*/
|
|
xxxSendMessageTimeout(pwndCurrentActivate, WM_CANCELMODE, 0, 0, SMTO_ABORTIFHUNG, 100, &dwResult);
|
|
|
|
/*
|
|
* Initialize the Switch Window data structure, if we
|
|
* succeed create and display the window, otherwise act
|
|
* like slow Alt+Tab.
|
|
*/
|
|
pwndActivateNext = InitSwitchWndInfo(&pswCurrent, pwndCurrentActivate, fDir);
|
|
|
|
ThreadLockWithPti(ptiCurrent, pwndActivateNext, &tlpwndActivateNext);
|
|
|
|
if (pswCurrent == NULL) {
|
|
/*
|
|
* Couldn't initialize our switch window data structure, so we
|
|
* will act like Alt+Esc.
|
|
*/
|
|
goto DoSlowAltTab;
|
|
}
|
|
|
|
if (pwndActivateNext == NULL) {
|
|
RemoveSwitchWindowInfo(&pswCurrent);
|
|
ThreadUnlock(&tlpwndActivateNext);
|
|
ThreadUnlock(&tlpwndCurrentActivate);
|
|
ThreadUnlock(&tlpwndCurrentTopFocus);
|
|
Unlock(&gspwndActivate);
|
|
return;
|
|
}
|
|
|
|
ThreadLockPoolCleanup(ptiCurrent, &pswCurrent, &tlpSwitchInfo, RemoveSwitchWindowInfo);
|
|
|
|
/*
|
|
* Since we are in the RIT, test the physical state of the keyboard
|
|
*/
|
|
pswCurrent->fJournaling = FALSE;
|
|
|
|
/*
|
|
* Create the Alt+Tab window
|
|
*/
|
|
pwndSwitch =
|
|
xxxNVCreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME,
|
|
(PLARGE_STRING)SWITCHWNDCLASS, NULL,
|
|
WS_POPUP | WS_BORDER | WS_DISABLED,
|
|
0, 0, 10, 10, NULL, NULL, NULL, NULL, VER40);
|
|
|
|
if (gspwndAltTab != NULL) {
|
|
RIPMSG0(RIP_WARNING, "xxxNextWindow: Creating a new switch window while one already exists.");
|
|
|
|
_PostMessage(gspwndAltTab, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
Lock(&gspwndAltTab, pwndSwitch);
|
|
|
|
ThreadUnlockPool(ptiCurrent, &tlpSwitchInfo);
|
|
|
|
if (gspwndAltTab == NULL) {
|
|
/*
|
|
* Could not create the cool switch window, do the Alt+Esc thing
|
|
*/
|
|
#ifdef COOLSWITCHTRACE
|
|
DbgPrint("CoolSwitch: Could not create window (contact bradg).\n");
|
|
UserAssert(gspwndAltTab != NULL);
|
|
#endif
|
|
RemoveSwitchWindowInfo(&pswCurrent);
|
|
goto DoSlowAltTab;
|
|
}
|
|
|
|
/*
|
|
* Save the pointer to the switch window info structure
|
|
*/
|
|
Setpswi(gspwndAltTab, pswCurrent);
|
|
/*
|
|
* Set gspwndActivate so the RIT knows what window we would like
|
|
* it to activate.
|
|
*/
|
|
Lock(&gspwndActivate, pwndActivateNext);
|
|
|
|
/*
|
|
* Make sure that our rit queue has the correct pdesk
|
|
*/
|
|
if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) {
|
|
xxxSetThreadDesktop(NULL, grpdeskRitInput); // DeferWinEventNotify() ?? IANJA ??
|
|
}
|
|
|
|
/*
|
|
* If we're currently full screen tell console to switch to
|
|
* the desktop to GDI mode; we can't do this on the RIT because
|
|
* it can be slow.
|
|
*/
|
|
if (gspwndFullScreen != grpdeskRitInput->pDeskInfo->spwnd) {
|
|
ThreadLockWithPti(ptiCurrent, grpdeskRitInput->pDeskInfo->spwnd, &tlpwndT);
|
|
xxxSendNotifyMessage(grpdeskRitInput->pDeskInfo->spwnd, WM_FULLSCREEN, GDIFULLSCREEN, (LPARAM)HW(grpdeskRitInput->pDeskInfo->spwnd));
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
|
|
/*
|
|
* Show the Alt+Tab window. If it returns FALSE this means
|
|
* the ALT key has been released, so there is no need to
|
|
* paint the icons.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, gspwndAltTab, &tlpwndT);
|
|
xxxShowSwitchWindow(gspwndAltTab);
|
|
ThreadUnlock(&tlpwndT);
|
|
|
|
/*
|
|
* Exit now because the Switch window will have been
|
|
* already updated.
|
|
*/
|
|
ThreadUnlock(&tlpwndActivateNext);
|
|
ThreadUnlock(&tlpwndCurrentActivate);
|
|
ThreadUnlock(&tlpwndCurrentTopFocus);
|
|
|
|
} else {
|
|
/*
|
|
* We come here to do the actual switching and/or updating of
|
|
* the switch window when in Alt+Tab mode.
|
|
*/
|
|
PWND pwndSwitch;
|
|
TL tlpwndSwitch;
|
|
HWND hwndActivateNext;
|
|
HWND hwndStop;
|
|
|
|
if (!(pwndSwitch = gspwndAltTab)) {
|
|
|
|
goto DoAltEsc;
|
|
|
|
} else {
|
|
/*
|
|
* Move the hilight rect to the next/prev task. It is possible
|
|
* that some tasks were destoryed, so we need to skip those.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndSwitch, &tlpwndSwitch);
|
|
hwndStop = NULL;
|
|
do {
|
|
pswCurrent = Getpswi(pwndSwitch);
|
|
if (pswCurrent == NULL) {
|
|
ThreadUnlock(&tlpwndSwitch);
|
|
goto DoAltEsc;
|
|
}
|
|
hwndActivateNext = xxxMoveSwitchWndHilite(pwndSwitch, pswCurrent, fDir);
|
|
if (!hwndStop) {
|
|
hwndStop = hwndActivateNext;
|
|
} else {
|
|
if (hwndStop == hwndActivateNext) {
|
|
pwndActivateNext = NULL;
|
|
break;
|
|
}
|
|
}
|
|
pwndActivateNext = RevalidateHwnd(hwndActivateNext);
|
|
} while (!pwndActivateNext);
|
|
ThreadUnlock(&tlpwndSwitch);
|
|
Lock(&gspwndActivate, pwndActivateNext);
|
|
if (!gspwndActivate) {
|
|
/*
|
|
* No Window to activate, bail out of Alt+Tab mode
|
|
*/
|
|
xxxCancelCoolSwitch();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
DoAltEsc:
|
|
case VK_ESCAPE:
|
|
/*
|
|
* NOTE: The RIT doesn't use gspwndActivate to activate the window when
|
|
* processing Alt+Esc, we just use it here as a convenient
|
|
* variable. The actual activation takes place below.
|
|
*/
|
|
pwndCurrentActivate = pq->spwndActive;
|
|
if (pwndCurrentActivate == NULL) {
|
|
pwndCurrentActivate = pq->ptiKeyboard->rpdesk->pDeskInfo->spwnd->spwndChild;
|
|
if (pwndCurrentActivate == NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
ThreadLockWithPti(ptiCurrent, pwndCurrentTopFocus, &tlpwndCurrentTopFocus);
|
|
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndCurrentActivate, &tlpwndCurrentActivate);
|
|
|
|
/*
|
|
* Cancel the active window's mode
|
|
*/
|
|
xxxSendMessageTimeout(pwndCurrentActivate, WM_CANCELMODE, 0, 0, SMTO_ABORTIFHUNG, 100, &dwResult);
|
|
|
|
/*
|
|
* Determine the next window to activate
|
|
*/
|
|
pwndActivateNext = _GetNextQueueWindow(pwndCurrentActivate, fDir, TRUE);
|
|
ThreadLockWithPti(ptiCurrent, pwndActivateNext, &tlpwndActivateNext);
|
|
|
|
/*
|
|
* If we're going forward through the windows, move the currently
|
|
* active window to the bottom so we'll do the right thing when
|
|
* we go backwards.
|
|
*/
|
|
if (pwndActivateNext != pwndCurrentActivate) {
|
|
DoSlowAltTab:
|
|
if (pwndActivateNext) {
|
|
|
|
/*
|
|
* We're about to activate another window while the ALT key is down,
|
|
* so let the current focus window know that it doesn't need the
|
|
* menu underlines anymore
|
|
*/
|
|
if ((pwndCurrentTopFocus != NULL) && (pwndCurrentTopFocus->spmenu != NULL)) {
|
|
ClearMF(pwndCurrentTopFocus->spmenu, MFUNDERLINE);
|
|
}
|
|
|
|
if (fDir == FDIR_FORWARD) {
|
|
/*
|
|
* For Alt+ESC only move the window to the bottom if it's
|
|
* not a top most window
|
|
*/
|
|
if (!TestWF(pwndCurrentActivate, WEFTOPMOST)) {
|
|
xxxSetWindowPos(pwndCurrentActivate, PWND_BOTTOM, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
|
|
SWP_DEFERDRAWING | SWP_NOSENDCHANGING |
|
|
SWP_ASYNCWINDOWPOS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The ALT key is down, so this window needs menu underlines
|
|
*/
|
|
if (pwndActivateNext->spmenu != NULL) {
|
|
SetMF(pwndActivateNext->spmenu, MFUNDERLINE);
|
|
}
|
|
|
|
|
|
/*
|
|
* This little ugly hack will cause xxxSetForegroundWindow2()
|
|
* to send out an activation messages to a queue that is
|
|
* already the active queue allowing us to change the active
|
|
* window on that queue.
|
|
*/
|
|
if (gpqForeground == GETPTI(pwndActivateNext)->pq)
|
|
gpqForeground = NULL;
|
|
|
|
/*
|
|
* Make the selected window thread the owner of the last input;
|
|
* since he's next, he owns the ALT-ESC.
|
|
*/
|
|
glinp.ptiLastWoken = GETPTI(pwndActivateNext);
|
|
|
|
xxxSetForegroundWindow2(pwndActivateNext, NULL,
|
|
(wParam == VK_TAB) ? SFW_SWITCH | SFW_ACTIVATERESTORE : SFW_SWITCH);
|
|
|
|
/*
|
|
* Win3.1 calls SetWindowPos() with activate, which z-orders
|
|
* first regardless, then activates. Our code relies on
|
|
* xxxActivateThisWindow() to z-order, and it'll only do
|
|
* it if the window does not have the child bit set (regardless
|
|
* that the window is a child of the desktop).
|
|
*
|
|
* To be compatible, we'll just force z-order here if the
|
|
* window has the child bit set. This z-order is asynchronous,
|
|
* so this'll z-order after the activate event is processed.
|
|
* That'll allow it to come on top because it'll be foreground
|
|
* then. (Grammatik has a top level window with the child
|
|
* bit set that wants to be come the active window).
|
|
*/
|
|
if (wParam == VK_TAB && TestWF(pwndActivateNext, WFCHILD)) {
|
|
xxxSetWindowPos(pwndActivateNext, (PWND)HWND_TOP, 0, 0, 0, 0,
|
|
SWP_NOSIZE | SWP_NOMOVE | SWP_ASYNCWINDOWPOS);
|
|
}
|
|
}
|
|
}
|
|
ThreadUnlock(&tlpwndActivateNext);
|
|
ThreadUnlock(&tlpwndCurrentActivate);
|
|
ThreadUnlock(&tlpwndCurrentTopFocus);
|
|
break;
|
|
|
|
case VK_F6:
|
|
if ((pwndCurrentActivate = pq->spwndActive) == NULL)
|
|
pwndCurrentActivate = pq->ptiKeyboard->rpdesk->pDeskInfo->spwnd->spwndChild;
|
|
|
|
pwndActivateNext = pwndCurrentActivate;
|
|
|
|
/*
|
|
* HACK! console sessions are all one thread but we want them
|
|
* to act like different threads so if its a console thread (csrss.exe)
|
|
* then ALT-F6 does nothing just like in Win 3.1
|
|
* Note: we never get called with wParam == VK_F6 anyway. Win NT 3.51
|
|
* doesn't seem to either, but Windows '95 does. BUG?? (IanJa)
|
|
*/
|
|
if (!(GETPTI(pwndActivateNext)->TIF_flags & TIF_CSRSSTHREAD)) {
|
|
/*
|
|
* on a alt-f6, we want to keep the switch within the thread.
|
|
* We may want to rethink this because this will look strange
|
|
* when you alt-f6 on a multi-threaded app we will not rotate
|
|
* through the windows on the different threads. This works
|
|
* fine on Win 3.x because it is single threaded.
|
|
*/
|
|
do {
|
|
pwndActivateNext = NextTopWindow(pq->ptiKeyboard, pwndActivateNext, NULL,
|
|
fDir ? NTW_PREVIOUS : 0);
|
|
} while( (pwndActivateNext != NULL) &&
|
|
(GETPTI(pwndActivateNext) != pq->ptiKeyboard));
|
|
|
|
if (pwndActivateNext != NULL) {
|
|
|
|
if (pwndActivateNext != pwndCurrentActivate) {
|
|
/*
|
|
* We're about to activate another window while the ALT key is down,
|
|
* so let the current focus window know that it doesn't need the
|
|
* menu underlines anymore
|
|
*/
|
|
pwndCurrentTopFocus = GetTopLevelWindow(pq->spwndFocus);
|
|
if ((pwndCurrentTopFocus != NULL) && (pwndCurrentTopFocus->spmenu != NULL)) {
|
|
ClearMF(pwndCurrentTopFocus->spmenu, MFUNDERLINE);
|
|
}
|
|
/*
|
|
* The ALT key is down, so this window needs menu underlines
|
|
*/
|
|
if (pwndActivateNext->spmenu != NULL) {
|
|
SetMF(pwndActivateNext->spmenu, MFUNDERLINE);
|
|
}
|
|
}
|
|
|
|
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndActivateNext, &tlpwndActivateNext);
|
|
xxxSetWindowPos(pwndActivateNext, PWND_BOTTOM, 0, 0, 0, 0,
|
|
SWP_DEFERDRAWING | SWP_NOSENDCHANGING | SWP_NOCHANGE |
|
|
SWP_ASYNCWINDOWPOS);
|
|
xxxSetForegroundWindow2(pwndActivateNext, NULL, SFW_SWITCH);
|
|
ThreadUnlock(&tlpwndActivateNext);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxOldNextWindow
|
|
*
|
|
* This function does the processing for the alt-tab/esc/F6 UI.
|
|
*
|
|
* History:
|
|
* 03-17-92 DavidPe Ported from Win 3.1 sources
|
|
\***************************************************************************/
|
|
VOID xxxOldNextWindow(
|
|
UINT flags)
|
|
{
|
|
MSG msg;
|
|
HWND hwndSel;
|
|
PWND pwndNewSel;
|
|
PWND pwndSel;
|
|
BOOL fType = 0;
|
|
BOOL fDrawIcon;
|
|
WORD vk;
|
|
TL tlpwndT;
|
|
TL tlpwndSel;
|
|
TL tlpwndSwitch;
|
|
PSWINFO pswCurrent;
|
|
PWND pwndSwitch;
|
|
HWND hwndStop;
|
|
HWND hwndNewSel;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Don't allow entering this routine when we're already in the AltTab
|
|
* mode. The AltTab window may have been created via xxxNextWindow.
|
|
*/
|
|
if (gspwndAltTab != NULL) {
|
|
return;
|
|
}
|
|
|
|
if ((pwndSel = ptiCurrent->pq->spwndActive) == NULL)
|
|
return;
|
|
|
|
ThreadLockWithPti(ptiCurrent, pwndSel, &tlpwndSel);
|
|
xxxCapture(ptiCurrent, pwndSel, SCREEN_CAPTURE);
|
|
|
|
vk = (WORD)flags;
|
|
msg.wParam = (UINT)flags;
|
|
|
|
pwndNewSel = NULL;
|
|
|
|
if (vk == VK_TAB) {
|
|
|
|
TL tlpSwitchInfo;
|
|
|
|
/*
|
|
* Initialize the Switch window data structures
|
|
*/
|
|
pwndNewSel = InitSwitchWndInfo(&pswCurrent,
|
|
pwndSel,
|
|
_GetKeyState(VK_SHIFT) < 0);
|
|
|
|
if (pswCurrent == NULL) {
|
|
/*
|
|
* We were unable to initialize the data structure used by
|
|
* the Switch window, so we will act like Alt+Esc.
|
|
*/
|
|
} else {
|
|
PWND pwndSwitch;
|
|
|
|
/*
|
|
* We are doing a journal playback do use _GetKeyState to
|
|
* test the keyboard.
|
|
*/
|
|
pswCurrent->fJournaling = TRUE;
|
|
|
|
ThreadLockPoolCleanup(ptiCurrent, &pswCurrent, &tlpSwitchInfo, RemoveSwitchWindowInfo);
|
|
|
|
pwndSwitch =
|
|
xxxNVCreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME,
|
|
(PLARGE_STRING)SWITCHWNDCLASS,
|
|
NULL,
|
|
WS_POPUP | WS_BORDER | WS_DISABLED,
|
|
0,
|
|
0,
|
|
10,
|
|
10,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
VER40);
|
|
if (gspwndAltTab != NULL) {
|
|
RIPMSGF0(RIP_WARNING,
|
|
"Creating a new switch window while one already exists.");
|
|
|
|
_PostMessage(gspwndAltTab, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
ThreadUnlockPool(ptiCurrent, &tlpSwitchInfo);
|
|
|
|
Lock(&gspwndAltTab, pwndSwitch);
|
|
|
|
if (!(pwndSwitch = gspwndAltTab)) {
|
|
RemoveSwitchWindowInfo(&pswCurrent);
|
|
} else {
|
|
/*
|
|
* Lock the switch window.
|
|
*/
|
|
ThreadLockAlwaysWithPti(ptiCurrent, pwndSwitch, &tlpwndSwitch);
|
|
|
|
/*
|
|
* Save the switch window info
|
|
*/
|
|
Setpswi(pwndSwitch, pswCurrent);
|
|
|
|
// Don't we need to switch from full screen mode if needed?
|
|
#if 0
|
|
/*
|
|
* If we're currently full screen tell console to switch to
|
|
* the desktop to GDI mode; we can't do this on the RIT because
|
|
* it can be slow.
|
|
*/
|
|
if (gspwndFullScreen != grpdeskRitInput->pDeskInfo->spwnd) {
|
|
ThreadLockWithPti(pti, grpdeskRitInput->pDeskInfo->spwnd, &tlpwndT);
|
|
xxxSendNotifyMessage(grpdeskRitInput->pDeskInfo->spwnd, WM_FULLSCREEN, GDIFULLSCREEN, (LONG)HW(grpdeskRitInput->pDeskInfo->spwnd));
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Show the switch window, this also will paint the window
|
|
*/
|
|
xxxShowSwitchWindow(gspwndAltTab);
|
|
ThreadUnlock(&tlpwndSwitch);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!pwndNewSel)
|
|
goto StartTab;
|
|
|
|
pwndSel = pwndNewSel;
|
|
|
|
while (TRUE) {
|
|
|
|
hwndSel = PtoH(pwndSel);
|
|
/*
|
|
* Wait for a message without getting it out of the queue.
|
|
*/
|
|
while (!xxxPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD))
|
|
xxxWaitMessage();
|
|
|
|
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
|
|
if (_CallMsgFilter(&msg, MSGF_NEXTWINDOW)) {
|
|
/*
|
|
* Swallow the message if the hook processed it
|
|
*/
|
|
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If we are doing Alt+Tab and some other key comes in (other than
|
|
* tab, escape or shift), then bomb out of this loop and leave that
|
|
* key in the queue.
|
|
*/
|
|
if ((msg.message == WM_SYSKEYDOWN) && gspwndAltTab != NULL) {
|
|
|
|
vk = (WORD)msg.wParam;
|
|
|
|
if ((vk != VK_TAB) && (vk != VK_ESCAPE) && (vk != VK_SHIFT)) {
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
fType = 0;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
switch (msg.message) {
|
|
|
|
case WM_CANCELJOURNAL:
|
|
/*
|
|
* If journalling was canceled we need to exit our loop and
|
|
* remove the Alt+Tab window. We don't want to remove this
|
|
* meesage because we want the app to know that journalling
|
|
* was canceled.
|
|
*/
|
|
|
|
/* > > > F A L L T H R O U G H < < < */
|
|
case WM_HOTKEY:
|
|
/*
|
|
* When pressing ALT-CTL-ESC-DEL on the logon desktop
|
|
* We eat WM_KEYUP and the queue for the wiinlogon thread will be empty so will be stuck
|
|
* in xxxWaitMessage() forever till the user do a mouse click where we will exit the loop in the below case statement.
|
|
* consider WM_HOTKEY as a valid exit case.
|
|
* [msadek -- 03/17/2001, bug# 337206]
|
|
*/
|
|
|
|
/* > > > F A L L T H R O U G H < < < */
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
case WM_XBUTTONDOWN:
|
|
case WM_XBUTTONUP:
|
|
/*
|
|
* If mouse message, cancel and get out of loop.
|
|
*/
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
fType = 0;
|
|
goto Exit;
|
|
|
|
case WM_KEYUP:
|
|
case WM_KEYDOWN:
|
|
case WM_SYSCHAR:
|
|
case WM_SYSKEYUP:
|
|
case WM_MOUSEMOVE:
|
|
/*
|
|
* Swallow the message
|
|
*/
|
|
hwndSel = PtoH(pwndSel);
|
|
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
|
|
|
|
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
|
|
if (msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP) {
|
|
|
|
vk = (WORD)msg.wParam;
|
|
|
|
/*
|
|
* If alt-tab up, then exit.
|
|
*/
|
|
if (vk == VK_MENU) {
|
|
/*
|
|
* If doing Alt+Esc, wait for up of ESC to get out.
|
|
*/
|
|
if (gspwndAltTab == NULL)
|
|
break;
|
|
|
|
fType = 0;
|
|
goto Exit;
|
|
|
|
} else if (vk == VK_ESCAPE || vk == VK_F6) {
|
|
/*
|
|
* Get out on up transition of ESC or F6 keys.
|
|
*/
|
|
if (gspwndAltTab != NULL) {
|
|
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
fType = 0;
|
|
|
|
} else {
|
|
|
|
fType = ((vk == VK_ESCAPE) ? ALT_ESCAPE : ALT_F6);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
} else if (msg.message == WM_KEYDOWN) {
|
|
/*
|
|
* Exit out loop is a stray key stroke comes through. In
|
|
* particular look for VK_CONTROL.
|
|
*/
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
fType = 0;
|
|
goto Exit;
|
|
}
|
|
break;
|
|
|
|
case WM_SYSKEYDOWN:
|
|
vk = (WORD)msg.wParam;
|
|
|
|
switch (vk) {
|
|
|
|
case VK_SHIFT:
|
|
case VK_TAB:
|
|
case VK_ESCAPE:
|
|
case VK_F6:
|
|
|
|
hwndSel = PtoH(pwndSel);
|
|
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
|
|
|
|
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
|
|
if (!(vk == VK_TAB))
|
|
break;
|
|
StartTab:
|
|
if (vk == VK_ESCAPE) {
|
|
pwndNewSel = _GetNextQueueWindow(
|
|
pwndSel,
|
|
_GetKeyState(VK_SHIFT) < 0,
|
|
TRUE);
|
|
|
|
if (pwndNewSel == NULL)
|
|
break;
|
|
|
|
fType = ALT_ESCAPE;
|
|
pwndSel = pwndNewSel;
|
|
|
|
/*
|
|
* Wait until ESC goes up to activate new window.
|
|
*/
|
|
break;
|
|
}
|
|
if (vk == VK_F6) {
|
|
|
|
PWND pwndFirst;
|
|
PWND pwndSaveSel = pwndSel;
|
|
|
|
/*
|
|
* Save the first returned window to act as a limit
|
|
* to the search because NextTopWindow will return NULL
|
|
* only if pwndSel is the only window that meets its
|
|
* selection criteria.
|
|
*
|
|
* This prevents a hang that can occur in winword or
|
|
* excel when then Alt-F4-F6 key combination is hit
|
|
* and unsaved changes exist.
|
|
*/
|
|
pwndFirst = pwndNewSel = (PWND)NextTopWindow(ptiCurrent, pwndSel, NULL,
|
|
_GetKeyState(VK_SHIFT) < 0 ? NTW_PREVIOUS : 0);
|
|
|
|
while (TRUE) {
|
|
|
|
/*
|
|
* If pwndNewSel is NULL, pwndSel is the only candidate.
|
|
*/
|
|
if (pwndNewSel == NULL)
|
|
break;
|
|
|
|
pwndSel = pwndNewSel;
|
|
|
|
/*
|
|
* If the window is on the same thread, wait until
|
|
* F6 goes up to activate new window.
|
|
*/
|
|
if (GETPTI(pwndSel) == ptiCurrent)
|
|
break;
|
|
|
|
pwndNewSel = (PWND)NextTopWindow(ptiCurrent, pwndSel, NULL,
|
|
_GetKeyState(VK_SHIFT) < 0 ? NTW_PREVIOUS : 0);
|
|
|
|
/*
|
|
* If we've looped around, use the original window.
|
|
* Wait until F6 goes up to activate new window.
|
|
*/
|
|
if (pwndNewSel == pwndFirst) {
|
|
pwndSel = pwndSaveSel;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Here for the Alt+Tab case
|
|
*/
|
|
if ((pwndSwitch = gspwndAltTab) != NULL) {
|
|
ThreadLockWithPti(ptiCurrent, pwndSwitch, &tlpwndSwitch);
|
|
hwndStop = NULL;
|
|
do {
|
|
|
|
pswCurrent = Getpswi(pwndSwitch);
|
|
if (pswCurrent == NULL) {
|
|
break;
|
|
}
|
|
hwndNewSel = xxxMoveSwitchWndHilite(
|
|
pwndSwitch,
|
|
pswCurrent,
|
|
_GetKeyState(VK_SHIFT) < 0);
|
|
|
|
if (!hwndStop) {
|
|
hwndStop = hwndNewSel;
|
|
} else {
|
|
if (hwndStop == hwndNewSel) {
|
|
pwndNewSel = NULL;
|
|
break;
|
|
}
|
|
}
|
|
pwndNewSel = RevalidateHwnd(hwndNewSel);
|
|
} while (!pwndNewSel);
|
|
ThreadUnlock(&tlpwndSwitch);
|
|
pwndSel = pwndNewSel;
|
|
|
|
} else {
|
|
|
|
pwndNewSel = _GetNextQueueWindow(
|
|
pwndSel,
|
|
_GetKeyState(VK_SHIFT) < 0,
|
|
FALSE);
|
|
|
|
if (pwndNewSel && pwndNewSel != pwndSel) {
|
|
|
|
if (!TestWF(pwndSel, WEFTOPMOST)) {
|
|
/*
|
|
* Force the old window to the bottom
|
|
*/
|
|
ThreadLockWithPti(ptiCurrent, pwndSel, &tlpwndT);
|
|
xxxSetWindowPos(pwndSel,
|
|
PWND_BOTTOM,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
SWP_NOMOVE |
|
|
SWP_NOSIZE |
|
|
SWP_NOACTIVATE |
|
|
SWP_DEFERDRAWING |
|
|
SWP_NOSENDCHANGING |
|
|
SWP_ASYNCWINDOWPOS);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
|
|
pwndSel = pwndNewSel; // Will be revalidated at top of loop
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto Exit;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hwndSel = PtoH(pwndSel);
|
|
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
|
|
xxxTranslateMessage(&msg, 0);
|
|
xxxDispatchMessage(&msg);
|
|
|
|
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
|
|
pwndSel = ptiCurrent->pq->spwndActive;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
xxxReleaseCapture();
|
|
|
|
fDrawIcon = (gspwndAltTab != NULL);
|
|
|
|
/*
|
|
* If this is an Alt-Escape we also have to send the current window
|
|
* to the bottom.
|
|
*/
|
|
if (fType == ALT_ESCAPE) {
|
|
|
|
PWND pwndActive;
|
|
|
|
if (gpqForeground) {
|
|
|
|
pwndActive = gpqForeground->spwndActive;
|
|
|
|
if (pwndActive && (pwndActive != pwndSel)) {
|
|
ThreadLockWithPti(ptiCurrent, pwndActive, &tlpwndT);
|
|
xxxSetWindowPos(pwndActive,
|
|
PWND_BOTTOM,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
SWP_NOMOVE |
|
|
SWP_NOSIZE |
|
|
SWP_NOACTIVATE |
|
|
SWP_DEFERDRAWING |
|
|
SWP_NOSENDCHANGING |
|
|
SWP_ASYNCWINDOWPOS);
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pwndSel) {
|
|
ThreadLockWithPti(ptiCurrent, pwndSel, &tlpwndT);
|
|
xxxSetForegroundWindow(pwndSel, FALSE);
|
|
|
|
if (TestWF(pwndSel, WFMINIMIZED)) {
|
|
|
|
if ((fType == 0) && fDrawIcon)
|
|
_PostMessage(pwndSel, WM_SYSCOMMAND, (UINT)SC_RESTORE, 0);
|
|
|
|
}
|
|
ThreadUnlock(&tlpwndT);
|
|
}
|
|
|
|
/*
|
|
* destroy the alt-tab window
|
|
*/
|
|
xxxCancelCoolSwitch();
|
|
|
|
ThreadUnlock(&tlpwndSel);
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
*
|
|
* GetAltTabInfo() - Active Accessibility API for OLEACC
|
|
*
|
|
* This succeeds if we are currently in alt-tab mode.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL WINAPI
|
|
_GetAltTabInfo(
|
|
int iItem,
|
|
PALTTABINFO pati,
|
|
LPWSTR ccxpwszItemText,
|
|
UINT cchItemText OPTIONAL,
|
|
BOOL bAnsi)
|
|
{
|
|
PSWINFO pswCurrent;
|
|
|
|
if (!gspwndAltTab || ((pswCurrent = Getpswi(gspwndAltTab)) == NULL)) {
|
|
RIPERR0(ERROR_NOT_FOUND, RIP_WARNING, "no Alt-Tab window");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Fill in general information
|
|
*/
|
|
pati->cItems = pswCurrent->iTotalTasks;
|
|
pati->cColumns = pswCurrent->iNoOfColumns;
|
|
pati->cRows = pswCurrent->iNoOfRows;
|
|
|
|
pati->iColFocus = pswCurrent->iCurCol;
|
|
pati->iRowFocus = pswCurrent->iCurRow;
|
|
|
|
pati->cxItem = CXICONSLOT;
|
|
pati->cyItem = CYICONSLOT;
|
|
pati->ptStart = pswCurrent->ptFirstRowStart;
|
|
|
|
/*
|
|
* Fill in specific information if asked.
|
|
*/
|
|
if (cchItemText && (iItem >= 0)) {
|
|
PWND pwndCur;
|
|
|
|
pwndCur = NULL;
|
|
|
|
try {
|
|
if ((iItem < pswCurrent->iTotalTasks) &&
|
|
(pwndCur = RevalidateHwnd(pswCurrent->pbwl->rghwnd[iItem]))) {
|
|
if (bAnsi) {
|
|
LPSTR ccxpszItemText = (LPSTR)ccxpwszItemText;
|
|
ULONG cch;
|
|
RtlUnicodeToMultiByteN(ccxpszItemText, cchItemText - 1,
|
|
&cch, pwndCur->strName.Buffer, pwndCur->strName.Length);
|
|
ccxpszItemText[cch] = '\0';
|
|
} else {
|
|
TextCopy(&pwndCur->strName, ccxpwszItemText, cchItemText);
|
|
}
|
|
} else {
|
|
// no such item
|
|
NullTerminateString(ccxpwszItemText, bAnsi);
|
|
}
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|