windows-nt/Source/XPSP1/NT/sdktools/tweakui/mouse.cpp
2020-09-26 16:20:57 +08:00

1315 lines
34 KiB
C++

/*
* mouse - Dialog box property sheet for "mouse ui tweaks"
*/
#include "tweakui.h"
#pragma BEGIN_CONST_DATA
KL const c_klDelay = { &c_hkCU, c_tszRegPathDesktop, c_tszDelay };
const char CODESEG c_szUser[] = "USER"; /* Must be ANSI */
const static DWORD CODESEG rgdwHelp[] = {
IDC_SPEEDTEXT, IDH_SPEEDHELP,
IDC_SPEEDFAST, IDH_SPEEDHELP,
IDC_SPEEDSLOW, IDH_SPEEDHELP,
IDC_SPEEDTRACK, IDH_SPEEDHELP,
IDC_SPEEDHELP, IDH_SPEEDHELP,
IDC_SENSGROUP, IDH_GROUP,
IDC_DBLCLKTEXT, IDH_DBLCLK,
IDC_DBLCLK, IDH_DBLCLK,
IDC_DBLCLKUD, IDH_DBLCLK,
IDC_DRAGTEXT, IDH_DRAG,
IDC_DRAG, IDH_DRAG,
IDC_DRAGUD, IDH_DRAG,
IDC_SENSHELP, IDH_GROUP,
IDC_EFFECTGROUP, IDH_GROUP,
IDC_BEEP, IDH_BEEP,
/* SOMEDAY - SPI_GETMOUSEHOVERHEIGHT/WIDTH */
IDC_WHEELGROUP, IDH_GROUP,
IDC_WHEELENABLE, IDH_WHEEL,
IDC_WHEELPAGE, IDH_WHEEL,
IDC_WHEELLINE, IDH_WHEEL,
IDC_WHEELLINENO, IDH_WHEEL,
IDC_TESTGROUP, IDH_TEST,
IDC_TEST, IDH_TEST,
IDC_XMOUSE, IDH_XMOUSE,
IDC_XMOUSERAISE, IDH_XMOUSERAISE,
IDC_XMOUSEDELAYTXT, IDH_XMOUSEDELAY,
IDC_XMOUSEDELAY, IDH_XMOUSEDELAY,
IDC_TIPS, IDH_TIPSTIP,
IDC_RESET, IDH_RESET,
0, 0,
};
#pragma END_CONST_DATA
typedef WORD DT, FAR *LPDT; /* typeof(dtMNDropDown) */
#ifdef WIN32
#define fLpdt (lpdt != &dtScratch)
#else
#define fLpdt (SELECTOROF(lpdt) != SELECTOROF((LPDT)&dtScratch))
#endif
/*
* Globals
*/
DT dtScratch; /* Point lpdt here if we are stuck */
DT dtNT; /* Point lpdt here if we are on NT */
LPDT lpdt; /* Where to tweak to adjust the actual dt */
/*
* Instanced. We're a cpl so have only one instance, but I declare
* all the instance stuff in one place so it's easy to convert this
* code to multiple-instance if ever we need to.
*/
typedef struct MDII { /* Mouse_dialog instance info */
BOOL fDrag; /* Potential drag in progress? */
DT dtOrig; /* Original dt when we started */
RECT rcTest; /* Test area */
RECT rcDrag; /* Drag test rectangle */
RECT rcDblClk; /* Double click rectangle */
LONG tmClick; /* Time of previous lbutton down */
HCURSOR hcurDrag; /* What is being dragged? */
BOOL fFactory; /* Factory defaults? */
POINT ptDblClk; /* Double click values pending */
POINT ptDrag; /* Drag values pending */
POINT ptDragStart; /* Where the start click went down */
int cxAspect; /* Screen aspect ratio */
int cyAspect; /* Screen aspect ratio */
int idi; /* Which icon to use? */
} MDII, *PMDII;
MDII mdii;
#define pmdii (&mdii)
#define DestroyCursor(hcur) SafeDestroyIcon((HICON)(hcur))
/*****************************************************************************
*
* Grovelling to find the dropmenu variable.
*
*****************************************************************************/
/*****************************************************************************
*
* dtDefault
*
* Return the default dropmenu time, which is DoubleClickTime * 4 / 5.
*
*****************************************************************************/
DT PASCAL
dtDefault(void)
{
return GetDoubleClickTime() * 4 / 5;
}
/*****************************************************************************
*
* dtCur
*
* Determine what the dropmenu time is, by external means.
*
* It ought to be DoubleClickTime * 4 / 5, or the value in the registry.
*
*****************************************************************************/
INLINE DT
dtCur(void)
{
return (DT)GetIntPkl(dtDefault(), &c_klDelay);
}
/*****************************************************************************
*
* GetProcOrd
*
* Win95 does not allow GetProcAddress to work on Kernel32, so we must
* implement it by hand.
*
*****************************************************************************/
/*
* winnt.h uses these strange structure names.
* Does anybody speak Hungarian over there?
*/
typedef IMAGE_DOS_HEADER IDH, *PIDH;
typedef IMAGE_NT_HEADERS NTH, *PINTH; /* I like how this is "HEADERS" plural */
typedef IMAGE_EXPORT_DIRECTORY EDT, *PEDT;
typedef DWORD EAT, *PEAT;
typedef IMAGE_DATA_DIRECTORY OTE, *POTE;
#define pvAdd(pv, cb) ((LPVOID)((LPSTR)(pv) + (DWORD)(cb)))
#define pvSub(pv1, pv2) (DWORD)((LPSTR)(pv1) - (LPSTR)(pv2))
#define poteExp(pinth) (&(pinth)->OptionalHeader. \
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT])
FARPROC PASCAL
GetProcOrd(LPVOID lpv, UINT ord)
{
PIDH pidh = (PIDH)lpv;
if (!IsBadReadPtr(pidh, sizeof(*pidh)) &&
pidh->e_magic == IMAGE_DOS_SIGNATURE) {
PINTH pinth = (PINTH)pvAdd(pidh, pidh->e_lfanew);
if (!IsBadReadPtr(pinth, sizeof(*pinth)) &&
pinth->Signature == IMAGE_NT_SIGNATURE) {
PEDT pedt = (PEDT)pvAdd(pidh, poteExp(pinth)->VirtualAddress);
if (!IsBadReadPtr(pedt, sizeof(*pedt)) &&
(ord - pedt->Base) < pedt->NumberOfFunctions) {
PEAT peat = (PEAT)pvAdd(pidh, pedt->AddressOfFunctions);
FARPROC fp = (FARPROC)pvAdd(pidh, peat[ord - pedt->Base]);
if (pvSub(fp, peat) >= poteExp(pinth)->Size) {
return fp;
} else { /* Forwarded!? */
return 0;
}
} else {
return 0;
}
} else {
return 0;
}
} else {
return 0;
}
}
/*****************************************************************************
*
* fGrovel
*
* Grovel into USER's DS to find the dropmenu time.
* The problem is that there is no documented way of getting and setting
* the dropmenu time without rebooting. So we find it by (trust me)
* disassembling the SetDoubleClickTime function and knowing that the
* last instructions are
*
* mov [xxxx], ax ; set drop menu time
* pop ds
* leave
* retf 2
*
* Good news! On Windows NT, there is a new SPI to do this.
*
*****************************************************************************/
typedef HINSTANCE (*LL16)(LPCSTR);
typedef FARPROC (*GPA16)(HINSTANCE, LPCSTR);
typedef BOOL (*FL16)(HINSTANCE);
typedef LPVOID (*MSL)(DWORD);
BOOL PASCAL
fGrovel(void)
{
OSVERSIONINFO ovi;
if (SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &dtNT, 0)) {
lpdt = &dtNT;
return 1;
}
#ifdef _X86_
/* Else win95 - must grovel */
ovi.dwOSVersionInfoSize = sizeof(ovi);
if (GetVersionEx(&ovi) &&
ovi.dwMajorVersion == 4 &&
ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
HINSTANCE hinstK32 = GetModuleHandle(c_tszKernel32);
if (hinstK32) {
LL16 LoadLibrary16 = (LL16)GetProcOrd(hinstK32, 35);
FL16 FreeLibrary16 = (FL16)GetProcOrd(hinstK32, 36);
GPA16 GetProcAddress16 = (GPA16)GetProcOrd(hinstK32, 37);
MSL MapSL = (MSL)GetProcAddress(hinstK32, c_tszMapSL);
if ((DWORD)LoadLibrary16 & (DWORD)FreeLibrary16 &
(DWORD)GetProcAddress16 & (DWORD)MapSL) {
HINSTANCE hinst16 = LoadLibrary16(c_szUser);
if ((UINT)hinst16 > 32) {
FARPROC fp = GetProcAddress16(hinst16,
MAKEINTRESOURCE(20));
if (fp) {
LPBYTE lpSDCT;
GetDoubleClickTime(); /* Force segment present */
lpSDCT = (LPBYTE)MapSL((DWORD)fp);
if (!IsBadReadPtr(lpSDCT, 84)) {
int i;
for (i = 0; i < 80; i++, lpSDCT++) {
if (*(LPDWORD)lpSDCT == 0x02CAC91F) {
lpdt = (LPDT)MapSL(MAKELONG(
*(LPWORD)(lpSDCT - 2),
hinst16));
return *lpdt == dtCur();
}
}
}
}
FreeLibrary16(hinst16);
}
}
}
}
#endif
return 0;
}
/*****************************************************************************
*
* msDt
*
* Get the actual drop time if possible. Don't all this unless
* you know it'll work.
*
*****************************************************************************/
DT PASCAL
msDt(void)
{
if (lpdt == &dtNT) {
SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &dtNT, 0);
}
return (DT)*lpdt;
}
/*****************************************************************************
*
* SetDt
*
* Set the drop time, returning TRUE if we need a reboot.
*
*****************************************************************************/
BOOL PASCAL
SetDt(UINT ms, DWORD spif)
{
#ifdef _X86_
if (lpdt == &dtNT) {
#endif
SystemParametersInfo(SPI_SETMENUSHOWDELAY, ms, 0, spif);
return 0;
#ifdef _X86_
} else {
if (spif & SPIF_UPDATEINIFILE) {
SetIntPkl(pmdii->dtOrig, &c_klDelay);
}
if (fLpdt) {
*lpdt = (WORD)ms;
return 0;
} else {
return 1;
}
}
#endif
}
/*****************************************************************************
*
* fXMouse
*
* Determine whether XMouse is enabled.
*
* Returns 0 if disabled, 1 if enabled, or -1 if not supported.
*
* Note that there are *two* ways of getting this information,
* depending on which flavor of NT/Win9x we are running. So
* try all of them until one of them works.
*
*****************************************************************************/
BOOL PASCAL
fXMouse(void)
{
BOOL fX;
if (SystemParametersInfo(SPI_GETUSERPREFERENCE,
SPI_UP_ACTIVEWINDOWTRACKING, &fX, 0)) {
return fX != 0;
} else if (SystemParametersInfo(SPI_GETACTIVEWINDOWTRACKING,
0, &fX, 0)) {
return fX != 0;
} else {
return -1;
}
}
/*****************************************************************************
*
* SetXMouse
*
* Set the XMouse feature.
*
*****************************************************************************/
INLINE void
SetXMouse(BOOL f)
{
if (SystemParametersInfo(SPI_SETUSERPREFERENCE,
SPI_UP_ACTIVEWINDOWTRACKING, IntToPtr(f),
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE)) {
} else {
SystemParametersInfo(SPI_SETACTIVEWINDOWTRACKING,
0, IntToPtr(f), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
/*****************************************************************************
*
* msXMouseDelay
*
* Returns the XMouse hover delay, or -1 if not supported.
*
*****************************************************************************/
int
msXMouseDelay(void)
{
DWORD dw;
if (SystemParametersInfo(SPI_GETACTIVEWNDTRKTIMEOUT, 0, &dw, 0)) {
return (int)dw;
}
return -1;
}
/*****************************************************************************
*
* SetXMouseDelay
*
*****************************************************************************/
INLINE void
SetXMouseDelay(int msDelay)
{
SystemParametersInfo(SPI_SETACTIVEWNDTRKTIMEOUT, 0, IntToPtr(msDelay),
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
/*****************************************************************************
*
* fXMouseRaise
*
* Returns 0 if autoraise disabled, 1 if enabled, or -1 if not supported.
*
*****************************************************************************/
BOOL PASCAL
fXMouseRaise(void)
{
BOOL f;
if (SystemParametersInfo(SPI_GETACTIVEWNDTRKZORDER, 0, &f, 0)) {
return f != 0;
} else {
return -1;
}
}
/*****************************************************************************
*
* SetXMouseRaise
*
* Set the XMouse autoraise feature.
*
*****************************************************************************/
INLINE void
SetXMouseRaise(BOOL f)
{
SystemParametersInfo(SPI_SETACTIVEWNDTRKZORDER, 0, IntToPtr(f),
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
/*****************************************************************************
*
* cxDragCur
*
* Return the current horizontal drag sensitivity.
*
*****************************************************************************/
INLINE int
cxDragCur(void)
{
return GetSystemMetrics(SM_CXDRAG);
}
/*****************************************************************************
*
* SetCxCyDrag
*
* Set the new horizontal and vertical drag tolerances.
*
*****************************************************************************/
INLINE void
SetCxCyDrag(int cxDrag, int cyDrag)
{
SystemParametersInfo(SPI_SETDRAGWIDTH, (UINT)cxDrag, 0L,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
SystemParametersInfo(SPI_SETDRAGHEIGHT, (UINT)cyDrag, 0L,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
/*****************************************************************************
*
* cxDblClkCur
*
* Return the current horizontal double click sensitivity.
* Note that GetSystemMetrics records the total width, so we
* need to divide by two to get the half-wit half-width.
*
*****************************************************************************/
INLINE int
cxDblClkCur(void)
{
return GetSystemMetrics(SM_CXDOUBLECLK) / 2;
}
/*****************************************************************************
*
* SetCxCyDblClk
*
* Set the current horizontal double click sensitivity.
* Note that GetSystemMetrics records the total width, so we
* need to multiply the half-width and half-height by two.
*
*****************************************************************************/
INLINE void
SetCxCyDblClk(int cxDblClk, int cyDblClk)
{
SystemParametersInfo(SPI_SETDOUBLECLKWIDTH, (UINT)cxDblClk * 2, 0L,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
SystemParametersInfo(SPI_SETDOUBLECLKHEIGHT, (UINT)cyDblClk * 2, 0L,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
/*****************************************************************************
*
* Mouse_ReloadDlgInt
*
* Reload values from an edit control.
*
* hdlg is the dialog box itself.
*
* idc is the edit control identifier.
*
* ppt -> a POINT structure which will contain the current value
* in the x, and an aspect-ratio-corrected value in the y.
*
* We allow the value to exceed the range, in case you
* really want it.
*
*
*****************************************************************************/
void PASCAL
Mouse_ReloadDlgInt(HWND hdlg, UINT idc, PPOINT ppt)
{
BOOL f;
LRESULT lr;
HWND hwnd;
int x;
hwnd = GetDlgItem(hdlg, idc+didcUd);
if (hwnd) {
lr = SendMessage(hwnd, UDM_GETRANGE, 0, 0L);
x = (int)GetDlgItemInt(hdlg, idc+didcEdit, &f, 0);
x = max((UINT)x, HIWORD(lr)); /* Force to lower limit of range */
ppt->x = x;
ppt->y = MulDiv(x, pmdii->cyAspect, pmdii->cxAspect);
}
}
/*****************************************************************************
*
* Mouse_InitDlgInt
*
* Initialize a paired edit control / updown control.
*
* hdlg is the dialog box itself.
*
* idc is the edit control identifier. It is assumed that idc+didcUd is
* the identifier for the updown control.
*
* xMin and xMax are the limits of the control.
*
* x = initial value
*
* ppt -> a POINT structure which will contain the current value
* in the x, and an aspect-ratio-corrected value in the y.
*
*
*****************************************************************************/
void PASCAL
Mouse_InitDlgInt(HWND hdlg, UINT idc, int xMin, int xMax, int xVal, PPOINT ppt)
{
SendDlgItemMessage(hdlg, idc+didcEdit, EM_LIMITTEXT, 2, 0L);
SetDlgItemInt(hdlg, idc+didcEdit, xVal, 0);
SendDlgItemMessage(hdlg, idc+didcUd,
UDM_SETRANGE, 0, MAKELPARAM(xMax, xMin));
Mouse_ReloadDlgInt(hdlg, idc, ppt);
}
/*****************************************************************************
*
* The trackbar
*
* The trackbar slider is piecewise linear. It really should be
* exponential, but it's hard to write exp() and log() for integers.
*
* Given two parallel arrays which describe the domain and range,
* with
*
* x[N] <= x <= x[N+1] mapping to y[N] <= y <= y[N+1],
*
* then
*
* x[N] <= x <= x[N+1] maps to
*
* y = y[N] + (x - x[N]) * (y[N+1] - y[N]) / (x[N+1] - x[N]).
*
*****************************************************************************/
/* tbt = trackbar tick */
#define tbtMax 120
#define tbtFreq 15
#define dtMax 65534 /* Don't use 65535; that's uiErr */
const static UINT CODESEG rgtbt[] =
{ 0, tbtMax/2, tbtMax*3/4, tbtMax*7/8, tbtMax };
const static UINT CODESEG rgdt[] =
{ 0, 500, 2000, 5000, dtMax };
/*****************************************************************************
*
* Mouse_Interpolate
*
* Perform piecewise linear interpolation. See the formulas above.
*
*****************************************************************************/
UINT PASCAL
Mouse_Interpolate(UINT x, const UINT CODESEG *px, const UINT CODESEG *py)
{
while (x > px[1]) px++, py++;
return py[0] + MulDiv(x - px[0], py[1] - py[0], px[1] - px[0]);
}
/*****************************************************************************
*
* Mouse_GetDt
*
* Get the setting that the user has selected.
*
* hdlg = dialog handle
*
* dtMax maps to dtInfinite.
*
*****************************************************************************/
DT PASCAL
Mouse_GetDt(HWND hdlg)
{
return (DT)Mouse_Interpolate(
(UINT)SendDlgItemMessage(hdlg, IDC_SPEEDTRACK,
TBM_GETPOS, 0, 0L), rgtbt, rgdt);
}
/*****************************************************************************
*
* Mouse_SetDt
*
* Set the setting into the trackbar.
*
* hdlg = dialog handle
*
*****************************************************************************/
void PASCAL
Mouse_SetDt(HWND hdlg, DT dt)
{
SendDlgItemMessage(hdlg, IDC_SPEEDTRACK, TBM_SETPOS, 1,
Mouse_Interpolate(dt, rgdt, rgtbt));
}
/*****************************************************************************
*
* Mouse_SetDirty
*
* Make a control dirty.
*
*****************************************************************************/
void NEAR PASCAL
Mouse_SetDirty(HWND hdlg)
{
pmdii->fFactory = 0;
PropSheet_Changed(GetParent(hdlg), hdlg);
}
/*****************************************************************************
*
* Mouse_UpdateWheel
*
* Update all the wheel control controls.
*
* If "Use wheel" is unchecked, then disable all the insides.
*
*****************************************************************************/
void PASCAL
Mouse_UpdateWheel(HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_WHEELENABLE);
if (hwnd) {
BOOL f = IsWindowEnabled(hwnd) &&
IsDlgButtonChecked(hdlg, IDC_WHEELENABLE);
AdjustDlgItems(hdlg, IDC_WHEELPAGE, IDC_WHEELLAST, f ? ADI_ENABLE : ADI_DISABLE);
}
}
/*****************************************************************************
*
* Mouse_Reset
*
* Reset all controls to initial values. This also marks
* the control as clean.
*
*****************************************************************************/
BOOL PASCAL
Mouse_Reset(HWND hdlg)
{
HWND hwnd = GetDlgItem(hdlg, IDC_SPEEDTRACK);
BOOL f;
SendMessage(hwnd, TBM_SETRANGE, 0, MAKELPARAM(0, tbtMax));
SendMessage(hwnd, TBM_SETTICFREQ, tbtFreq, 0L);
if (fLpdt) {
pmdii->dtOrig = msDt(); /* Save for revert */
if (pmdii->dtOrig > dtMax) { /* Max out here */
pmdii->dtOrig = dtMax;
}
} else {
/* just use what seems to be the current setting */
pmdii->dtOrig = dtCur();
}
Mouse_SetDt(hdlg, pmdii->dtOrig);
f = fXMouse();
if (f >= 0) {
CheckDlgButton(hdlg, IDC_XMOUSE, f);
f = fXMouseRaise();
if (f >= 0) {
CheckDlgButton(hdlg, IDC_XMOUSERAISE, f);
}
int ms = msXMouseDelay();
if (ms >= 0) {
SetDlgItemInt(hdlg, IDC_XMOUSEDELAY, ms, FALSE);
}
}
Mouse_UpdateWheel(hdlg);
Mouse_InitDlgInt(hdlg, IDC_DBLCLK, 1, 32, cxDblClkCur(), &pmdii->ptDblClk);
Mouse_InitDlgInt(hdlg, IDC_DRAG, 1, 32, cxDragCur(), &pmdii->ptDrag);
return 1;
}
/*****************************************************************************
*
* Mouse_Apply
*
* Write the changes to the registry and into USER's DS.
*
*****************************************************************************/
BOOL NEAR PASCAL
Mouse_Apply(HWND hdlg)
{
BOOL f;
BOOL fNow;
DWORD dwNow;
DT dt;
dt = Mouse_GetDt(hdlg);
if (dt != pmdii->dtOrig) {
pmdii->dtOrig = dt;
if (pmdii->fFactory) {
/* DelPkl(&c_klDelay); */
dt = dtDefault();
}
if (SetDt(pmdii->dtOrig, SPIF_UPDATEINIFILE)) {
Common_NeedLogoff(hdlg);
}
SendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM)(LPCTSTR)c_tszWindows);
}
if (cxDragCur() != pmdii->ptDrag.x) {
/* This will send WM_WININICHANGE as necessary */
SetCxCyDrag(pmdii->ptDrag.x, pmdii->ptDrag.y);
}
if (cxDblClkCur() != pmdii->ptDblClk.x) {
/* This will send WM_WININICHANGE as necessary */
SetCxCyDblClk(pmdii->ptDblClk.x, pmdii->ptDblClk.y);
}
fNow = fXMouse();
if (fNow >= 0) {
f = IsDlgButtonChecked(hdlg, IDC_XMOUSE);
if (fNow != f) {
/* This will send WM_WININICHANGE as necessary */
SetXMouse(f);
}
fNow = fXMouseRaise();
f = IsDlgButtonChecked(hdlg, IDC_XMOUSERAISE);
if (fNow != f) {
SetXMouseRaise(f);
}
int msDelay = (int)GetDlgItemInt(hdlg, IDC_XMOUSEDELAY, &f, FALSE);
int msDelayNow = msXMouseDelay();
if (msDelay != msDelayNow) {
SetXMouseDelay(msDelay);
}
}
if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &dwNow, 0)) {
if (IsWindowEnabled(GetDlgItem(hdlg, IDC_WHEELENABLE))) {
DWORD dw;
if (!IsDlgButtonChecked(hdlg, IDC_WHEELENABLE)) {
dw = 0;
} else if (IsDlgButtonChecked(hdlg, IDC_WHEELPAGE)) {
dw = WHEEL_PAGESCROLL;
} else {
dw = GetDlgItemInt(hdlg, IDC_WHEELLINENO, &f, 0);
}
if (dw != dwNow) {
SystemParametersInfo(SPI_SETWHEELSCROLLLINES, dw, 0,
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
}
return Mouse_Reset(hdlg);
}
/*****************************************************************************
*
* Mouse_ReloadUpdowns
*
* Reload the values from the updown controls and update our
* internals accordingly.
*
*****************************************************************************/
BOOL PASCAL
Mouse_ReloadUpdowns(HWND hdlg)
{
Mouse_ReloadDlgInt(hdlg, IDC_DBLCLK, &pmdii->ptDblClk);
Mouse_ReloadDlgInt(hdlg, IDC_DRAG, &pmdii->ptDrag);
Mouse_SetDirty(hdlg);
return 1;
}
/*****************************************************************************
*
* Mouse_FactoryReset
*
* Restore to original factory settings.
*
* Droptime = DoubleClickTime * 4 / 5.
* Animation = !((GetSystemMetrics(SM_SLOWMACHINE) & 0x0004) &&
* (GetSystemMetrics(SM_SLOWMACHINE) & 0x0001))
* cxDrag = 2
* cxDblClk = 2
*
*****************************************************************************/
BOOL PASCAL
Mouse_FactoryReset(HWND hdlg)
{
Mouse_SetDirty(hdlg);
Mouse_SetDt(hdlg, dtDefault());
if (fXMouse() >= 0) {
CheckDlgButton(hdlg, IDC_XMOUSE, 0);
}
SetDlgItemInt(hdlg, IDC_DRAG, 2, 0);
SetDlgItemInt(hdlg, IDC_DBLCLK, 2, 0);
Mouse_ReloadUpdowns(hdlg);
if (GetDlgItem(hdlg, IDC_WHEELENABLE)) {
CheckDlgButton(hdlg, IDC_WHEELENABLE, TRUE);
CheckRadioButton(hdlg, IDC_WHEELPAGE, IDC_WHEELLINE, IDC_WHEELLINE);
SetDlgItemInt(hdlg, IDC_WHEELLINENO, 3, 0);
}
pmdii->fFactory = 1;
return 1;
}
/*****************************************************************************
*
* Mouse_OnTips
*
*****************************************************************************/
void PASCAL
Mouse_OnTips(HWND hdlg)
{
WinHelp(hdlg, c_tszMyHelp, HELP_FINDER, 0);
}
#ifdef IDC_BUGREPORT
/*****************************************************************************
*
* Mouse_OnBugReport
*
*****************************************************************************/
void PASCAL
Mouse_OnBugReport(HWND hdlg)
{
ShellExecute(hdlg, "open", "http://abject/tweakui/", "", "",
SW_NORMAL);
}
#endif
/*****************************************************************************
*
* Mouse_OnCommand
*
* Ooh, we got a command.
*
*****************************************************************************/
BOOL PASCAL
Mouse_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
switch (id) {
case IDC_RESET: /* Reset to factory default */
if (codeNotify == BN_CLICKED) return Mouse_FactoryReset(hdlg);
break;
case IDC_TIPS: /* Call up help */
if (codeNotify == BN_CLICKED) Mouse_OnTips(hdlg);
break;
#ifdef IDC_BUGREPORT
case IDC_BUGREPORT:
if (codeNotify == BN_CLICKED) Mouse_OnBugReport(hdlg);
break;
#endif
case IDC_XMOUSE:
case IDC_XMOUSERAISE:
case IDC_WHEELPAGE:
case IDC_WHEELLINE:
if (codeNotify == BN_CLICKED) Mouse_SetDirty(hdlg);
break;
case IDC_DRAG:
case IDC_DBLCLK:
case IDC_WHEELLINENO:
case IDC_XMOUSEDELAY:
if (codeNotify == EN_CHANGE) {
Mouse_ReloadUpdowns(hdlg);
Mouse_SetDirty(hdlg);
}
break;
case IDC_WHEELENABLE:
if (codeNotify == BN_CLICKED) {
Mouse_UpdateWheel(hdlg);
Mouse_SetDirty(hdlg);
}
break;
}
return 0;
}
/*****************************************************************************
*
* Mouse_SetTestIcon
*
* Set a new test icon, returning the previous one.
*
*****************************************************************************/
HCURSOR PASCAL
Mouse_SetTestIcon(HWND hdlg, UINT idi)
{
return (HCURSOR)
SendDlgItemMessage(hdlg, IDC_TEST, STM_SETICON,
(WPARAM)LoadIconId(idi), 0L);
}
/*****************************************************************************
*
* Mouse_StopDrag
*
* Stop any drag operation in progress.
*
* We must release the capture unconditionally, or a double-click
* will result in the mouse capture being stuck!
*
*****************************************************************************/
void PASCAL
Mouse_StopDrag(HWND hdlg)
{
ReleaseCapture(); /* Always do this! */
if (pmdii->hcurDrag) {
SetCursor(0); /* We're about to destroy the current cursor */
DestroyCursor(pmdii->hcurDrag);
pmdii->hcurDrag = 0;
DestroyCursor(Mouse_SetTestIcon(hdlg, pmdii->idi));
}
pmdii->fDrag = 0; /* not dragging */
}
/*****************************************************************************
*
* Mouse_OnNotify
*
* Ooh, we got a notification.
*
*****************************************************************************/
BOOL PASCAL
Mouse_OnNotify(HWND hdlg, NMHDR FAR *pnm)
{
switch (pnm->code) {
case PSN_APPLY:
Mouse_Apply(hdlg);
break;
/*
* If we are dragging, then ESC cancels the drag, not the prsht.
* Note that we must set the message result *last*, because
* ReleaseCapture will recursively call the dialog procedure,
* smashing whatever used to be in the message result.
*/
case PSN_QUERYCANCEL:
if (pmdii->fDrag) {
Mouse_StopDrag(hdlg);
SetDlgMsgResult(hdlg, WM_NOTIFY, 1);
}
return 1;
}
return 0;
}
/*****************************************************************************
*
* Mouse_OnInitDialog
*
* Initialize the controls.
*
*****************************************************************************/
BOOL NEAR PASCAL
Mouse_OnInitDialog(HWND hdlg)
{
UINT idc;
DWORD dw;
pmdii->idi = IDI_GEAR1; /* Start with the first gear */
pmdii->fDrag = 0; /* not dragging */
/* Make sure first click isn't counted as a double */
pmdii->tmClick = 0;
pmdii->fFactory = 0;
pmdii->hcurDrag = 0;
GetDlgItemRect(hdlg, IDC_TEST, &pmdii->rcTest);
{
HDC hdc = GetDC(0);
if (hdc) {
pmdii->cxAspect = GetDeviceCaps(hdc, ASPECTX);
pmdii->cyAspect = GetDeviceCaps(hdc, ASPECTY);
ReleaseDC(0, hdc);
if (pmdii->cxAspect == 0) { /* Buggy display driver */
goto Fallback;
}
} else { /* Assume 1:1 aspect ratio */
Fallback:
pmdii->cxAspect = pmdii->cyAspect = 1;
}
}
SendDlgItemMessage(hdlg, IDC_WHEELLINENO, EM_LIMITTEXT, 3, 0L);
SetDlgItemInt(hdlg, IDC_WHEELLINENO, 3, 0);
SendDlgItemMessage(hdlg, IDC_WHEELLINEUD,
UDM_SETRANGE, 0, MAKELPARAM(999, 1));
if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &dw, 0)) {
if (GetSystemMetrics(SM_MOUSEWHEELPRESENT)) {
CheckDlgButton(hdlg, IDC_WHEELENABLE, dw != 0);
if (dw == WHEEL_PAGESCROLL) {
CheckDlgButton(hdlg, IDC_WHEELPAGE, TRUE);
} else {
CheckDlgButton(hdlg, IDC_WHEELLINE, TRUE);
if (dw) {
SetDlgItemInt(hdlg, IDC_WHEELLINENO, dw, 0);
}
}
} else {
EnableDlgItem(hdlg, IDC_WHEELENABLE, 0);
}
} else {
DestroyDlgItems(hdlg, IDC_WHEELFIRST, IDC_WHEELLAST);
}
if (fXMouse() < 0) {
DestroyDlgItems(hdlg, IDC_XMOUSEFIRST, IDC_XMOUSELAST);
} else {
if (fXMouseRaise() < 0) {
EnableDlgItem(hdlg, IDC_XMOUSERAISE, FALSE);
}
if (msXMouseDelay() < 0) {
EnableDlgItem(hdlg, IDC_XMOUSEDELAY, FALSE);
EnableDlgItem(hdlg, IDC_XMOUSEDELAYTXT, FALSE);
}
}
if (fGrovel()) {
Mouse_Reset(hdlg);
return 1; /* Allow focus to travel normally */
} else {
lpdt = &dtScratch;
*lpdt = dtCur(); /* Gotta give it something */
Mouse_Reset(hdlg);
ShowWindow(GetDlgItem(hdlg, IDC_SPEEDHELP), SW_HIDE);
return 0;
}
}
/*****************************************************************************
*
* Mouse_OnLButtonDown
*
* If the left button went down in the test area, begin capturing.
* Also record the time the button went down, so we can do double-click
* fuzz testing.
*
*****************************************************************************/
BOOL PASCAL
Mouse_OnLButtonDown(HWND hdlg, int x, int y)
{
POINT pt = { x, y };
LONG tm = GetMessageTime();
if (PtInRect(&pmdii->rcTest, pt)) {
/*
* Is this a double-click?
*/
if (pmdii->tmClick &&
(DWORD)(tm - pmdii->tmClick) < GetDoubleClickTime() &&
PtInRect(&pmdii->rcDblClk, pt)) {
pmdii->idi ^= IDI_GEAR1 ^ IDI_GEAR2;
DestroyCursor(Mouse_SetTestIcon(hdlg, pmdii->idi));
tm = 0;
}
SetRectPoint(&pmdii->rcDrag, pt);
SetRectPoint(&pmdii->rcDblClk, pt);
InflateRect(&pmdii->rcDrag, pmdii->ptDrag.x, pmdii->ptDrag.y);
InflateRect(&pmdii->rcDblClk, pmdii->ptDblClk.x, pmdii->ptDblClk.y);
pmdii->fDrag = 1; /* Drag in progress */
SetCapture(hdlg);
}
pmdii->tmClick = tm;
return 1;
}
/*****************************************************************************
*
* Mouse_OnMouseMove
*
* If we are captured, see if we've moved far enough to act as
* if a drag is in progress.
*
*****************************************************************************/
BOOL PASCAL
Mouse_OnMouseMove(HWND hdlg, int x, int y)
{
if (pmdii->fDrag && !pmdii->hcurDrag) {
POINT pt = { x, y };
if (!PtInRect(&pmdii->rcDrag, pt)) {
pmdii->hcurDrag = Mouse_SetTestIcon(hdlg, IDI_BLANK);
/*
* Tweak the cursor position so it looks like the icon
* dragged from the original click point.
*/
ICONINFO ii;
if (GetIconInfo(pmdii->hcurDrag, &ii)) {
DeleteObject(ii.hbmMask);
DeleteObject(ii.hbmColor);
/*
* These formulas are heinous.
*
* xClick = client coordinates of original click
* = pmdii->rcDrag.left + pmdii->ptDrag.x
*
* xTest = client coordinates of start of clickable icon
* = pmdii->rcTest.left
*
* xOffset = location of click relative to icon corner
* = xClick - xTest
*
* xAdjust = amount the user's click location differs
* from the actual hotspot
* = ii.xHotspot - xOffset
* = ii.xHotspot - xClick + xTest
*/
pt.x += + ii.xHotspot
- (pmdii->rcDrag.left + pmdii->ptDrag.x)
+ pmdii->rcTest.left;
pt.y += + ii.yHotspot
- (pmdii->rcDrag.top + pmdii->ptDrag.y)
+ pmdii->rcTest.top;
ClientToScreen(hdlg, &pt);
SetCursorPos(pt.x, pt.y);
}
SetCursor(pmdii->hcurDrag);
}
}
return 0;
}
/*****************************************************************************
*
* Mouse_OnRButtonUp
*
* If the button went up in the menu test area, track a menu.
*
*****************************************************************************/
BOOL PASCAL
Mouse_OnRButtonUp(HWND hdlg, int x, int y)
{
POINT pt = { x, y };
if (PtInRect(&pmdii->rcTest, pt) && fLpdt) {
DT dt;
int id;
dt = msDt(); /* Save for revert */
SetDt(Mouse_GetDt(hdlg), 0);
ClientToScreen(hdlg, &pt); /* Make it screen coordinates */
id = TrackPopupMenuEx(GetSubMenu(pcdii->hmenu, 0),
TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_VERTICAL |
TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y,
hdlg, 0);
SetDt(dt, 0);
return 1;
} else {
return 0; /* Do the default thing */
}
}
/*****************************************************************************
*
* Our window procedure.
*
*****************************************************************************/
/*
* The HANDLE_WM_* macros weren't designed to be used from a dialog
* proc, so we need to handle the messages manually. (But carefully.)
*/
INT_PTR EXPORT
Mouse_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
switch (wm) {
case WM_INITDIALOG: return Mouse_OnInitDialog(hdlg);
/* We have only one trackbar, so we don't need to check */
case WM_HSCROLL: Mouse_SetDirty(hdlg); return 1;
/* We have two updowns, but reloading is cheap, so we just reload both */
case WM_VSCROLL:
if (GET_WM_VSCROLL_CODE(wParam, lParam) == SB_THUMBPOSITION) {
return Mouse_ReloadUpdowns(hdlg);
}
break;
case WM_COMMAND:
return Mouse_OnCommand(hdlg,
(int)GET_WM_COMMAND_ID(wParam, lParam),
(UINT)GET_WM_COMMAND_CMD(wParam, lParam));
case WM_NOTIFY:
return Mouse_OnNotify(hdlg, (NMHDR FAR *)lParam);
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
return Mouse_OnLButtonDown(hdlg, LOWORD(lParam), HIWORD(lParam));
case WM_ACTIVATE:
if (GET_WM_ACTIVATE_STATE(wParam, lParam) == WA_INACTIVE) {
Mouse_StopDrag(hdlg);
}
break;
case WM_LBUTTONUP:
Mouse_StopDrag(hdlg);
break;
case WM_RBUTTONUP:
return Mouse_OnRButtonUp(hdlg, LOWORD(lParam), HIWORD(lParam));
case WM_HELP: Common_OnHelp(lParam, &rgdwHelp[0]); break;
case WM_CONTEXTMENU: Common_OnContextMenu(wParam, &rgdwHelp[0]); break;
case WM_MOUSEMOVE:
return Mouse_OnMouseMove(hdlg, LOWORD(lParam), HIWORD(lParam));
default: return 0; /* Unhandled */
}
return 1; /* Handled */
}