/* * 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 */ }