mirror of https://github.com/lianthony/NT4.0
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.
1184 lines
31 KiB
1184 lines
31 KiB
#include "ctlspriv.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// updown.c : A micro-scrollbar control; useful for increment/decrement.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define NUM_UDACCELS 3
|
|
|
|
#define DONTCARE 0
|
|
#define SIGNED 1
|
|
#define UNSIGNED 2
|
|
|
|
typedef struct {
|
|
HWND hwnd;
|
|
LONG style;
|
|
HWND hwndBuddy;
|
|
unsigned fUp : 1;
|
|
unsigned fDown : 1;
|
|
unsigned fUnsigned : 1; // BUGBUG: no way to turn this on
|
|
unsigned fSharedBorder : 1;
|
|
unsigned fSunkenBorder : 1;
|
|
unsigned fUpDownDestroyed : 1; // This tells the buddy that updown destoryed.
|
|
UINT nBase;
|
|
int nUpper;
|
|
int nLower;
|
|
int nPos;
|
|
HWND hwndParent;
|
|
UINT uClass;
|
|
BOOL bDown;
|
|
DWORD dwStart;
|
|
WNDPROC lpfnDefProc;
|
|
UINT nAccel;
|
|
UDACCEL udAccel[NUM_UDACCELS];
|
|
} UDSTATE, NEAR *PUDSTATE;
|
|
|
|
|
|
// Constants:
|
|
//
|
|
#define CLASS_UNKNOWN 0
|
|
#define CLASS_EDIT 1
|
|
#define CLASS_LISTBOX 2
|
|
|
|
#define MAX_INTLENGTH 18 // big enough for all intl stuff, too
|
|
|
|
// this is the space to the left and right of the arrow (in pixels)
|
|
#define XBORDER 0
|
|
|
|
#define BASE_DECIMAL 10
|
|
#define BASE_HEX 16
|
|
|
|
// Declarations:
|
|
//
|
|
LRESULT CALLBACK ArrowKeyProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// ***** Internal workhorses *****
|
|
//
|
|
|
|
|
|
// Validates the buddy.
|
|
//
|
|
void NEAR PASCAL isgoodbuddy(PUDSTATE np)
|
|
{
|
|
if (!np->hwndBuddy)
|
|
return;
|
|
if (!IsWindow(np->hwndBuddy))
|
|
{
|
|
#if defined(DEBUG) && !defined(WIN32)
|
|
DebugOutput(DBF_ERROR | DBF_USER,
|
|
TEXT("UpDown: invalid buddy handle 0x04X; ")
|
|
TEXT("resetting to NULL"), np->hwndBuddy);
|
|
#endif
|
|
np->hwndBuddy = NULL;
|
|
np->uClass = CLASS_UNKNOWN;
|
|
}
|
|
if (GetParent(np->hwndBuddy) != np->hwndParent)
|
|
{
|
|
#if defined(DEBUG) && !defined(WIN32)
|
|
DebugOutput(DBF_ERROR | DBF_USER,
|
|
TEXT("UpDown: buddy has different parent; ")
|
|
TEXT("resetting to NULL"));
|
|
#endif
|
|
np->hwndBuddy = NULL;
|
|
np->uClass = CLASS_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
// Picks a good buddy.
|
|
//
|
|
void NEAR PASCAL pickbuddy(PUDSTATE np)
|
|
{
|
|
if (np->style & UDS_AUTOBUDDY)
|
|
np->hwndBuddy = GetWindow(np->hwnd, GW_HWNDPREV);
|
|
}
|
|
|
|
void NEAR PASCAL unachor(PUDSTATE np)
|
|
{
|
|
RECT rc;
|
|
RECT rcBuddy;
|
|
RECT rcUD;
|
|
|
|
if ( np->hwndBuddy && (np->style & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT))) {
|
|
GetWindowRect(np->hwndBuddy, &rcBuddy);
|
|
GetWindowRect(np->hwnd, &rcUD);
|
|
UnionRect(&rc, &rcUD, &rcBuddy);
|
|
MapWindowRect(NULL, np->hwndParent, &rc);
|
|
MoveWindow(np->hwndBuddy, rc.left, rc.top,
|
|
rc.right - rc.left, rc.bottom - rc.top, FALSE);
|
|
|
|
}
|
|
}
|
|
|
|
// Anchor this control to the buddy's edge, if appropriate.
|
|
//
|
|
void NEAR PASCAL anchor(PUDSTATE np)
|
|
{
|
|
BOOL bAlignToBuddy;
|
|
int nOver = 0, nHasBorder;
|
|
RECT rc, rcBuddy;
|
|
int nHeight, nWidth;
|
|
|
|
np->fSharedBorder = FALSE;
|
|
|
|
isgoodbuddy(np);
|
|
nHasBorder = (np->style & WS_BORDER) == WS_BORDER;
|
|
|
|
bAlignToBuddy = np->hwndBuddy && (np->style & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT));
|
|
|
|
if (bAlignToBuddy)
|
|
{
|
|
if ((np->uClass == CLASS_EDIT) ||
|
|
(GetWindowLong(np->hwndBuddy, GWL_EXSTYLE) & WS_EX_CLIENTEDGE))
|
|
{
|
|
np->fSunkenBorder = TRUE;
|
|
}
|
|
|
|
GetWindowRect(np->hwndBuddy, &rc);
|
|
|
|
if ((np->uClass == CLASS_EDIT) || (GetWindowLong(np->hwndBuddy, GWL_STYLE) & WS_BORDER))
|
|
{
|
|
// BUGBUG for full generalization, should handle border AND clientedge
|
|
|
|
nOver = g_cxBorder * (np->fSunkenBorder ? 2 : 1);
|
|
np->fSharedBorder = TRUE;
|
|
|
|
// turn off border styles...
|
|
np->style &= ~WS_BORDER;
|
|
|
|
SetWindowLong(np->hwnd, GWL_STYLE, np->style);
|
|
SetWindowLong(np->hwnd, GWL_EXSTYLE, GetWindowLong(np->hwnd, GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetWindowRect(np->hwnd, &rc);
|
|
}
|
|
|
|
nHeight = rc.bottom - rc.top;
|
|
nWidth = rc.right - rc.left;
|
|
|
|
ScreenToClient(np->hwndParent, (LPPOINT)&rc.left);
|
|
rc.right = rc.left + nWidth;
|
|
|
|
if (bAlignToBuddy)
|
|
{
|
|
nWidth = g_cxVScroll - g_cxBorder + nOver;
|
|
rcBuddy = rc;
|
|
|
|
if (np->style & UDS_ALIGNLEFT)
|
|
{
|
|
// size buddy to right
|
|
rcBuddy.left += nWidth - nOver;
|
|
rc.right = rc.left + nWidth;
|
|
}
|
|
else
|
|
{
|
|
// size buddy to left
|
|
rcBuddy.right -= nWidth - nOver;
|
|
rc.left = rc.right - nWidth;
|
|
}
|
|
// size the buddy to fit the updown on the appropriate side
|
|
MoveWindow(np->hwndBuddy, rcBuddy.left, rcBuddy.top,
|
|
rcBuddy.right - rcBuddy.left, nHeight, TRUE);
|
|
}
|
|
else if (!(np->style & UDS_HORZ))
|
|
{
|
|
nWidth = g_cxVScroll + 2 * nHasBorder;
|
|
}
|
|
|
|
SetWindowPos(np->hwnd, NULL, rc.left, rc.top, nWidth, nHeight,
|
|
SWP_DRAWFRAME | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
|
|
|
|
// Use this to make any and all comparisons involving the nPos,
|
|
// nUpper or nLower fields of the PUDSTATE. It determines
|
|
// whether to do a signed or unsigned comparison and returns
|
|
// > 0 for (x > y)
|
|
// < 0 for (x < y)
|
|
// == 0 for (x == y).
|
|
|
|
int NEAR PASCAL compare(PUDSTATE np, int x, int y, UINT fCompareType)
|
|
{
|
|
if ((fCompareType == UNSIGNED) || ((np->fUnsigned) && !(fCompareType == SIGNED)) )
|
|
{
|
|
// Do unsigned comparisons
|
|
if ((UINT)x > (UINT)y)
|
|
return 1;
|
|
else if ((UINT)x < (UINT)y)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
// Do signed comparisons
|
|
if (x > y)
|
|
return 1;
|
|
else if (x < y)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Use this after any pos change to make sure pos stays in range.
|
|
// Wraps as necessary.
|
|
//
|
|
BOOL NEAR PASCAL nudge(PUDSTATE np)
|
|
{
|
|
BOOL bOutOfRange = TRUE;
|
|
int min = np->nUpper;
|
|
int max = np->nLower;
|
|
|
|
if (compare(np,max,min, DONTCARE) < 0)
|
|
{
|
|
int t;
|
|
t = min;
|
|
min = max;
|
|
max = t;
|
|
}
|
|
|
|
|
|
if (np->style & UDS_WRAP)
|
|
{
|
|
if ((compare(np, np->nPos, min, np->fUnsigned ? UNSIGNED : SIGNED) < 0))
|
|
np->nPos = max;
|
|
else if ((compare(np, np->nPos, max, np->fUnsigned ? UNSIGNED : SIGNED) > 0))
|
|
np->nPos = min;
|
|
else bOutOfRange = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (compare(np,np->nPos,min, DONTCARE) < 0)
|
|
np->nPos = min;
|
|
else if (compare(np,np->nPos,max, DONTCARE) > 0)
|
|
np->nPos = max;
|
|
else
|
|
bOutOfRange = FALSE;
|
|
}
|
|
|
|
return(bOutOfRange);
|
|
}
|
|
|
|
// Sets the state of the buttons (pushed, released).
|
|
//
|
|
void NEAR PASCAL squish(PUDSTATE np, UINT bTop, UINT bBottom)
|
|
{
|
|
BOOL bInvalidate = FALSE;
|
|
|
|
if (np->nUpper == np->nLower || !IsWindowEnabled(np->hwnd))
|
|
{
|
|
bTop = FALSE;
|
|
bBottom = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bTop = !!bTop;
|
|
bBottom = !!bBottom;
|
|
}
|
|
|
|
if (np->fUp != bTop)
|
|
{
|
|
np->fUp = bTop;
|
|
bInvalidate = TRUE;
|
|
}
|
|
if (np->fDown != bBottom)
|
|
{
|
|
np->fDown = bBottom;
|
|
bInvalidate = TRUE;
|
|
}
|
|
|
|
if (bInvalidate)
|
|
{
|
|
np->dwStart = GetTickCount();
|
|
InvalidateRect(np->hwnd, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
// Gets the intl 1000 separator
|
|
//
|
|
void NEAR PASCAL getthousands(LPTSTR pszThousand)
|
|
{
|
|
#ifdef WIN32
|
|
if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, 2))
|
|
{
|
|
pszThousand[0] = TEXT(',');
|
|
pszThousand[1] = TEXT('\0');
|
|
}
|
|
#else
|
|
static DWORD uLast = 0;
|
|
static TCHAR cThou;
|
|
DWORD uNow;
|
|
|
|
/* Only check the intl setting every 5 seconds.
|
|
*/
|
|
uNow = GetTickCount();
|
|
if (uNow - uLast > 5000)
|
|
{
|
|
if (!GetProfileString(TEXT("intl"), TEXT("sThousand"), pszThousand, pszThousand, 2))
|
|
{
|
|
pszThousand[0] = TEXT(',');
|
|
pszThousand[1] = TEXT('\0');
|
|
}
|
|
cThou = pszThousand[0];
|
|
uLast = uNow;
|
|
}
|
|
else
|
|
{
|
|
pszThousand[0] = cThou;
|
|
pszThousand[1] = 0;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
// Gets the caption of the buddy
|
|
//
|
|
LRESULT NEAR PASCAL getint(PUDSTATE np)
|
|
{
|
|
TCHAR szInt[MAX_INTLENGTH]; // big enough for all intl stuff, too
|
|
TCHAR szThousand[2];
|
|
TCHAR cTemp;
|
|
int nPos;
|
|
int sign = 1;
|
|
LPTSTR p = szInt;
|
|
BOOL bInValid = TRUE;
|
|
|
|
isgoodbuddy(np);
|
|
if (np->hwndBuddy && np->style & UDS_SETBUDDYINT)
|
|
{
|
|
if (np->uClass == CLASS_LISTBOX)
|
|
{
|
|
np->nPos = (int)SendMessage(np->hwndBuddy, LB_GETCURSEL, 0, 0L);
|
|
bInValid = nudge(np);
|
|
}
|
|
else
|
|
{
|
|
GetWindowText(np->hwndBuddy, szInt, ARRAYSIZE(szInt));
|
|
|
|
switch (np->nBase)
|
|
{
|
|
case BASE_HEX:
|
|
if ((*p == TEXT('x')) || (*p == TEXT('X')))
|
|
// ignore first character
|
|
p++;
|
|
else if ((*p == TEXT('0')) && ((*(p + 1) == TEXT('x')) || (*(p + 1) == TEXT('X'))))
|
|
// ignore first two characters (TEXT("0x") or "0X")
|
|
p += 2;
|
|
|
|
for (nPos = 0; *p; p++)
|
|
{
|
|
if ((*p >= TEXT('A')) && (*p <= TEXT('F')))
|
|
cTemp = (TCHAR)(*p - TEXT('A') + 10);
|
|
else if ((*p >= TEXT('a')) && (*p <= TEXT('f')))
|
|
cTemp = (TCHAR)(*p - TEXT('a') + 10);
|
|
else if ((*p >= TEXT('0')) && (*p <= TEXT('9')))
|
|
cTemp = (TCHAR)(*p - TEXT('0'));
|
|
else
|
|
goto BadValue;
|
|
|
|
nPos = (nPos * 16) + cTemp;
|
|
}
|
|
np->nPos = nPos;
|
|
break;
|
|
|
|
case BASE_DECIMAL:
|
|
default:
|
|
getthousands(szThousand);
|
|
if (*p == TEXT('-'))
|
|
{
|
|
sign = -1;
|
|
++p;
|
|
}
|
|
|
|
for (nPos=0; *p; p++)
|
|
{
|
|
cTemp = *p;
|
|
|
|
// If there is a thousand separator, make sure it is in the
|
|
// right place
|
|
if (cTemp == szThousand[0] && lstrlen(p)==4)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cTemp -= TEXT('0');
|
|
if ((UINT)cTemp > 9)
|
|
{
|
|
goto BadValue;
|
|
}
|
|
nPos = (nPos*10) + cTemp;
|
|
}
|
|
|
|
np->nPos = nPos*sign;
|
|
break;
|
|
}
|
|
bInValid = nudge(np);
|
|
}
|
|
}
|
|
|
|
BadValue:
|
|
return(MAKELRESULT(np->nPos, bInValid));
|
|
}
|
|
|
|
// Sets the caption of the buddy if appropriate.
|
|
//
|
|
void NEAR PASCAL setint(PUDSTATE np)
|
|
{
|
|
static int cReenter = 0;
|
|
|
|
TCHAR szInt[MAX_INTLENGTH];
|
|
TCHAR szThousand[2];
|
|
int pos = np->nPos;
|
|
LPTSTR p = szInt;
|
|
|
|
isgoodbuddy(np);
|
|
if (np->hwndBuddy && np->style & UDS_SETBUDDYINT)
|
|
{
|
|
/* If we have reentered, then maybe the app has set up a loop.
|
|
* Check to see if the value really needs to be set.
|
|
*/
|
|
if (cReenter && (LRESULT)pos==getint(np))
|
|
{
|
|
return;
|
|
}
|
|
np->nPos = pos;
|
|
|
|
++cReenter;
|
|
|
|
if (np->uClass == CLASS_LISTBOX)
|
|
{
|
|
SendMessage(np->hwndBuddy, LB_SETCURSEL, pos, 0L);
|
|
FORWARD_WM_COMMAND(GetParent(np->hwndBuddy),
|
|
GetDlgCtrlID(np->hwndBuddy),
|
|
np->hwndBuddy, LBN_SELCHANGE, SendMessage);
|
|
}
|
|
else
|
|
{
|
|
switch (np->nBase)
|
|
{
|
|
case BASE_HEX:
|
|
wsprintf(p, TEXT("0x%04X"), pos);
|
|
break;
|
|
|
|
case BASE_DECIMAL:
|
|
default:
|
|
if (pos < 0)
|
|
{
|
|
*p++ = TEXT('-');
|
|
pos = -pos;
|
|
}
|
|
|
|
if (pos >= 1000 && !(np->style & UDS_NOTHOUSANDS))
|
|
{
|
|
getthousands(szThousand);
|
|
p += wsprintf(p, TEXT("%d"), pos / 1000);
|
|
if (szThousand[0])
|
|
*p++ = szThousand[0];
|
|
wsprintf(p, TEXT("%03d"), pos % 1000);
|
|
}
|
|
else
|
|
{
|
|
wsprintf(p, TEXT("%d"), pos);
|
|
}
|
|
break;
|
|
}
|
|
|
|
SetWindowText(np->hwndBuddy, szInt);
|
|
}
|
|
|
|
--cReenter;
|
|
}
|
|
}
|
|
|
|
// Use this to click the pos up or down by one.
|
|
//
|
|
void NEAR PASCAL bump(PUDSTATE np)
|
|
{
|
|
BOOL bChanged = FALSE;
|
|
UINT uElapsed, increment;
|
|
int direction, i;
|
|
|
|
/* So I'm not really getting seconds here; it's close enough, and
|
|
* dividing by 1024 keeps __aFuldiv from being needed.
|
|
*/
|
|
uElapsed = (UINT)((GetTickCount() - np->dwStart) / 1024);
|
|
|
|
increment = np->udAccel[0].nInc;
|
|
for (i=np->nAccel-1; i>=0; --i)
|
|
{
|
|
if (np->udAccel[i].nSec <= uElapsed)
|
|
{
|
|
increment = np->udAccel[i].nInc;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (increment == 0)
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("bad accelerator value"));
|
|
return;
|
|
}
|
|
|
|
direction = compare(np,np->nUpper,np->nLower, DONTCARE) < 0 ? -1 : 1;
|
|
if (np->fUp)
|
|
{
|
|
bChanged = TRUE;
|
|
}
|
|
if (np->fDown)
|
|
{
|
|
direction = -direction;
|
|
bChanged = TRUE;
|
|
}
|
|
|
|
if (bChanged)
|
|
{
|
|
/* Make sure we have a multiple of the increment
|
|
* Note that we should loop only when the increment changes
|
|
*/
|
|
NM_UPDOWN nm;
|
|
|
|
nm.iPos = np->nPos;
|
|
nm.iDelta = increment*direction;
|
|
if (SendNotifyEx(np->hwndParent, np->hwnd, UDN_DELTAPOS, &nm.hdr, FALSE))
|
|
return;
|
|
|
|
np->nPos += nm.iDelta;
|
|
for ( ; ; )
|
|
{
|
|
if (!((int)np->nPos % (int)increment))
|
|
{
|
|
break;
|
|
}
|
|
np->nPos += direction;
|
|
}
|
|
|
|
nudge(np);
|
|
setint(np);
|
|
if (np->style & UDS_HORZ)
|
|
FORWARD_WM_HSCROLL(np->hwndParent, np->hwnd, SB_THUMBPOSITION, np->nPos, SendMessage);
|
|
else
|
|
FORWARD_WM_VSCROLL(np->hwndParent, np->hwnd, SB_THUMBPOSITION, np->nPos, SendMessage);
|
|
}
|
|
}
|
|
|
|
//#pragma data_seg(DATASEG_READONLY)
|
|
const TCHAR c_szEdit[] = TEXT("edit");
|
|
const TCHAR c_szListbox[] = TEXT("listbox");
|
|
//#pragma data_seg()
|
|
|
|
// Sets the new buddy
|
|
//
|
|
LRESULT NEAR PASCAL setbuddy(PUDSTATE np, HWND hwndBuddy)
|
|
{
|
|
HWND hOldBuddy;
|
|
TCHAR szClName[10];
|
|
|
|
hOldBuddy = np->hwndBuddy;
|
|
if (hOldBuddy && GetProp(hOldBuddy, s_szUpdownClass))
|
|
{
|
|
SetWindowLong(hOldBuddy, GWL_WNDPROC, (LONG)np->lpfnDefProc);
|
|
RemoveProp(hOldBuddy, s_szUpdownClass);
|
|
np->lpfnDefProc = NULL;
|
|
}
|
|
|
|
np->hwndBuddy = hwndBuddy;
|
|
if (!hwndBuddy)
|
|
{
|
|
pickbuddy(np);
|
|
hwndBuddy = np->hwndBuddy;
|
|
}
|
|
|
|
np->uClass = CLASS_UNKNOWN;
|
|
if (hwndBuddy)
|
|
{
|
|
if (np->style & UDS_ARROWKEYS)
|
|
{
|
|
if (GetProp(hwndBuddy, s_szUpdownClass) == NULL)
|
|
{
|
|
np->lpfnDefProc = (WNDPROC)SetWindowLong(hwndBuddy, GWL_WNDPROC, (LONG)ArrowKeyProc);
|
|
SetProp(hwndBuddy, s_szUpdownClass, (HANDLE)np);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("SetBuddy called on already subclassed buddy"));
|
|
}
|
|
}
|
|
|
|
GetClassName(hwndBuddy, szClName, ARRAYSIZE(szClName));
|
|
if (!lstrcmpi(szClName, c_szEdit))
|
|
{
|
|
np->uClass = CLASS_EDIT;
|
|
}
|
|
else if (!lstrcmpi(szClName, c_szListbox))
|
|
{
|
|
np->uClass = CLASS_LISTBOX;
|
|
}
|
|
}
|
|
|
|
anchor(np);
|
|
return MAKELRESULT(hOldBuddy, 0);
|
|
}
|
|
|
|
|
|
// Paint the whole control
|
|
//
|
|
void NEAR PASCAL PaintUpDownControl(PUDSTATE np, HDC hdc)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
RECT rcBtn;
|
|
RECT rc;
|
|
|
|
BOOL bEnabled = (np->nUpper != np->nLower) && IsWindowEnabled(np->hwnd);
|
|
|
|
if (np->hwndBuddy)
|
|
bEnabled = bEnabled && IsWindowEnabled(np->hwndBuddy);
|
|
|
|
if (hdc)
|
|
ps.hdc = hdc;
|
|
else
|
|
BeginPaint(np->hwnd, &ps);
|
|
|
|
GetClientRect(np->hwnd, &rcBtn);
|
|
|
|
// if we are autobuddy'd and anchored to a sunken-edge control, we draw the
|
|
// "nonclient" area of ourselves to blend in with our buddy.
|
|
if (np->fSharedBorder && np->fSunkenBorder)
|
|
{
|
|
UINT bf = BF_TOP | BF_BOTTOM | BF_ADJUST |
|
|
(np->style & UDS_ALIGNLEFT ? BF_LEFT : 0) |
|
|
(np->style & UDS_ALIGNRIGHT ? BF_RIGHT : 0);
|
|
DrawEdge(ps.hdc, &rcBtn, EDGE_SUNKEN, bf);
|
|
}
|
|
|
|
// with remaining space, draw appropriate scrollbar arrow controls in
|
|
// upper and lower halves
|
|
|
|
rc = rcBtn;
|
|
if (np->style & UDS_HORZ)
|
|
{
|
|
// Horizontal ones
|
|
rc.right = (rcBtn.right + rcBtn.left) / 2;
|
|
DrawFrameControl(ps.hdc, &rc, DFC_SCROLL,
|
|
(DFCS_SCROLLLEFT |
|
|
(np->fDown ? DFCS_PUSHED : 0) |
|
|
(bEnabled ? 0 : DFCS_INACTIVE)));
|
|
rc.left = rcBtn.right - (rc.right - rc.left); // handles odd-x case, too
|
|
rc.right = rcBtn.right;
|
|
DrawFrameControl(ps.hdc, &rc, DFC_SCROLL,
|
|
(DFCS_SCROLLRIGHT |
|
|
(np->fUp ? DFCS_PUSHED : 0) |
|
|
(bEnabled ? 0 : DFCS_INACTIVE)));
|
|
}
|
|
else
|
|
{
|
|
rc.bottom = (rcBtn.bottom + rcBtn.top) / 2;
|
|
DrawFrameControl(ps.hdc, &rc, DFC_SCROLL,
|
|
(DFCS_SCROLLUP |
|
|
(np->fUp ? DFCS_PUSHED : 0) |
|
|
(bEnabled ? 0 : DFCS_INACTIVE)));
|
|
rc.top = rcBtn.bottom - (rc.bottom - rc.top); // handles odd-y case, too
|
|
rc.bottom = rcBtn.bottom;
|
|
DrawFrameControl(ps.hdc, &rc, DFC_SCROLL,
|
|
(DFCS_SCROLLDOWN |
|
|
(np->fDown ? DFCS_PUSHED : 0) |
|
|
(bEnabled ? 0 : DFCS_INACTIVE)));
|
|
}
|
|
|
|
if (hdc == NULL)
|
|
EndPaint(np->hwnd, &ps);
|
|
}
|
|
|
|
|
|
LRESULT CALLBACK ArrowKeyProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PUDSTATE np = (PUDSTATE)GetProp(hWnd, s_szUpdownClass);
|
|
WNDPROC lpfnDefProc;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCDESTROY:
|
|
/* Restore the window proc.
|
|
*/
|
|
lpfnDefProc = np->lpfnDefProc;
|
|
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)lpfnDefProc);
|
|
RemoveProp(hWnd, s_szUpdownClass);
|
|
np->hwndBuddy = NULL;
|
|
np->lpfnDefProc = NULL;
|
|
if (np->fUpDownDestroyed)
|
|
{
|
|
// The buddy was destroyed after updown so free the memory now
|
|
// And pass off to the message to who we subclassed...
|
|
LocalFree((HLOCAL)np);
|
|
}
|
|
return CallWindowProc(lpfnDefProc, hWnd, uMsg, wParam, lParam);
|
|
|
|
case WM_GETDLGCODE:
|
|
return CallWindowProc(np->lpfnDefProc, hWnd, uMsg, wParam, lParam) | DLGC_WANTARROWS;
|
|
|
|
case WM_KEYDOWN:
|
|
switch (wParam)
|
|
{
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
if (GetCapture() != np->hwnd)
|
|
{
|
|
/* Get the value from the buddy if this is the first key down
|
|
*/
|
|
if (!(lParam&(1L<<30)))
|
|
{
|
|
getint(np);
|
|
}
|
|
|
|
/* Update the visuals and bump the value
|
|
*/
|
|
np->bDown = (wParam == VK_DOWN);
|
|
squish(np, !np->bDown, np->bDown);
|
|
bump(np);
|
|
}
|
|
return(0L);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
switch (wParam)
|
|
{
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
if (GetCapture() != np->hwnd)
|
|
{
|
|
squish(np, FALSE, FALSE);
|
|
}
|
|
return(0L);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
switch (wParam)
|
|
{
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
return(0L);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(np->lpfnDefProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
UINT NEAR PASCAL setbase(PUDSTATE np, UINT wNewBase)
|
|
{
|
|
UINT wOldBase;
|
|
|
|
switch (wNewBase)
|
|
{
|
|
case BASE_DECIMAL:
|
|
case BASE_HEX:
|
|
np->fUnsigned = (wNewBase != BASE_DECIMAL);
|
|
wOldBase = np->nBase;
|
|
np->nBase = wNewBase;
|
|
setint(np);
|
|
return wOldBase;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HWND WINAPI CreateUpDownControl(DWORD dwStyle, int x, int y, int cx, int cy,
|
|
HWND hParent, int nID, HINSTANCE hInst,
|
|
HWND hwndBuddy, int nUpper, int nLower, int nPos)
|
|
{
|
|
HWND hWnd = CreateWindow(s_szUpdownClass, NULL, dwStyle, x, y, cx, cy,
|
|
hParent, (HMENU)nID, hInst, 0L);
|
|
if (hWnd)
|
|
{
|
|
SendMessage(hWnd, UDM_SETBUDDY, (WPARAM)hwndBuddy, 0L);
|
|
SendMessage(hWnd, UDM_SETRANGE, 0, MAKELONG(nUpper, nLower));
|
|
SendMessage(hWnd, UDM_SETPOS, 0, MAKELONG(nPos, 0));
|
|
}
|
|
return hWnd;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// UpDownWndProc:
|
|
//
|
|
LRESULT CALLBACK UpDownWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
RECT rc;
|
|
int i;
|
|
PUDSTATE np = (PUDSTATE)GetWindowInt(hWnd, 0);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
// Don't set a timer if on the middle border
|
|
BOOL bTimeIt = TRUE;
|
|
|
|
if (np->hwndBuddy && !IsWindowEnabled(np->hwndBuddy))
|
|
break;
|
|
|
|
SetCapture(hWnd);
|
|
getint(np);
|
|
|
|
switch (np->uClass)
|
|
{
|
|
case CLASS_EDIT:
|
|
case CLASS_LISTBOX:
|
|
SetFocus(np->hwndBuddy);
|
|
break;
|
|
}
|
|
|
|
GetClientRect(hWnd, &rc);
|
|
if (np->style & UDS_HORZ)
|
|
{
|
|
// Horizontal placement
|
|
if ((int)LOWORD(lParam) < (rc.right / 2))
|
|
{
|
|
np->bDown = TRUE;
|
|
squish(np, FALSE, TRUE);
|
|
}
|
|
else if ((int)LOWORD(lParam) > (rc.right / 2))
|
|
{
|
|
np->bDown = FALSE;
|
|
squish(np, TRUE, FALSE);
|
|
}
|
|
else
|
|
bTimeIt = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if ((int)HIWORD(lParam) > (rc.bottom / 2))
|
|
{
|
|
np->bDown = TRUE;
|
|
squish(np, FALSE, TRUE);
|
|
}
|
|
else if ((int)HIWORD(lParam) < (rc.bottom / 2))
|
|
{
|
|
np->bDown = FALSE;
|
|
squish(np, TRUE, FALSE);
|
|
}
|
|
else
|
|
bTimeIt = FALSE;
|
|
}
|
|
|
|
if (bTimeIt)
|
|
{
|
|
SetTimer(hWnd, 1, GetProfileInt(TEXT("windows"), TEXT("CursorBlinkRate"), 530), NULL);
|
|
bump(np);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
{
|
|
POINT pt;
|
|
|
|
if (GetCapture() != hWnd)
|
|
{
|
|
goto EndScroll;
|
|
}
|
|
|
|
SetTimer(hWnd, 1, 100, NULL);
|
|
|
|
GetWindowRect(hWnd, &rc);
|
|
if (np->style & UDS_HORZ) {
|
|
i = (rc.left + rc.right) / 2;
|
|
if (np->bDown)
|
|
{
|
|
rc.right = i;
|
|
}
|
|
else
|
|
{
|
|
rc.left = i;
|
|
}
|
|
} else {
|
|
i = (rc.top + rc.bottom) / 2;
|
|
if (np->bDown)
|
|
{
|
|
rc.top = i;
|
|
}
|
|
else
|
|
{
|
|
rc.bottom = i;
|
|
}
|
|
}
|
|
InflateRect(&rc, (g_cxFrame+1)/2, (g_cyFrame+1)/2);
|
|
GetCursorPos(&pt);
|
|
if (PtInRect(&rc, pt))
|
|
{
|
|
squish(np, !np->bDown, np->bDown);
|
|
bump(np);
|
|
}
|
|
else
|
|
{
|
|
squish(np, FALSE, FALSE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
if (np->hwndBuddy && !IsWindowEnabled(np->hwndBuddy))
|
|
break;
|
|
|
|
if (GetCapture() == hWnd)
|
|
{
|
|
EndScroll:
|
|
squish(np, FALSE, FALSE);
|
|
ReleaseCapture();
|
|
KillTimer(hWnd, 1);
|
|
|
|
if (np->uClass == CLASS_EDIT)
|
|
Edit_SetSel(np->hwndBuddy, 0, -1);
|
|
|
|
if (np->style & UDS_HORZ)
|
|
FORWARD_WM_HSCROLL(np->hwndParent, np->hwnd,
|
|
SB_ENDSCROLL, np->nPos, SendMessage);
|
|
else
|
|
FORWARD_WM_VSCROLL(np->hwndParent, np->hwnd,
|
|
SB_ENDSCROLL, np->nPos, SendMessage);
|
|
}
|
|
break;
|
|
|
|
case WM_ENABLE:
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
break;
|
|
|
|
case WM_WININICHANGE:
|
|
if (np && (!wParam ||
|
|
(wParam == SPI_SETNONCLIENTMETRICS) ||
|
|
(wParam == SPI_SETICONTITLELOGFONT))) {
|
|
InitGlobalMetrics(wParam);
|
|
unachor(np);
|
|
anchor(np);
|
|
}
|
|
break;
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
PaintUpDownControl(np, (HDC)wParam);
|
|
break;
|
|
|
|
case UDM_SETRANGE:
|
|
np->nUpper = (int)(SHORT)LOWORD(lParam);
|
|
np->nLower = (int)(SHORT)HIWORD(lParam);
|
|
nudge(np);
|
|
break;
|
|
|
|
case UDM_GETRANGE:
|
|
return MAKELONG(np->nUpper, np->nLower);
|
|
|
|
case UDM_SETBASE:
|
|
// wParam: new base
|
|
// lParam: not used
|
|
// return: 0 if invalid base is specified,
|
|
// previous base otherwise
|
|
return (LRESULT)setbase(np, (UINT)wParam);
|
|
|
|
case UDM_GETBASE:
|
|
return np->nBase;
|
|
|
|
case UDM_SETPOS:
|
|
{
|
|
int iNewPos = (int)(SHORT)LOWORD(lParam);
|
|
if (compare(np, np->nLower, np->nUpper, DONTCARE) < 0) {
|
|
|
|
if (compare(np, iNewPos, np->nUpper, DONTCARE) > 0) {
|
|
iNewPos = np->nUpper;
|
|
}
|
|
|
|
if (compare(np, iNewPos, np->nLower, DONTCARE) < 0) {
|
|
iNewPos = np->nLower;
|
|
}
|
|
} else {
|
|
if (compare(np, iNewPos, np->nUpper, DONTCARE) < 0) {
|
|
iNewPos = np->nUpper;
|
|
}
|
|
|
|
if (compare(np, iNewPos, np->nLower, DONTCARE) > 0) {
|
|
iNewPos = np->nLower;
|
|
}
|
|
}
|
|
|
|
i = np->nPos;
|
|
np->nPos = iNewPos;
|
|
setint(np);
|
|
return (LRESULT)i;
|
|
}
|
|
|
|
case UDM_GETPOS:
|
|
return getint(np);
|
|
|
|
case UDM_SETBUDDY:
|
|
return setbuddy(np, (HWND)wParam);
|
|
|
|
case UDM_GETBUDDY:
|
|
return (LRESULT)(int)np->hwndBuddy;
|
|
|
|
case UDM_SETACCEL:
|
|
if (wParam == 0)
|
|
return(FALSE);
|
|
if (wParam >= NUM_UDACCELS)
|
|
{
|
|
HANDLE npPrev = (HANDLE)np;
|
|
np = (PUDSTATE)LocalReAlloc((HLOCAL)np, sizeof(UDSTATE)+(wParam-NUM_UDACCELS)*sizeof(UDACCEL),
|
|
LMEM_MOVEABLE);
|
|
if (!np)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
else
|
|
{
|
|
SetWindowInt(hWnd, 0, (int)np);
|
|
|
|
if ((np->style & UDS_ARROWKEYS) && np->hwndBuddy)
|
|
{
|
|
// Update the property of our buddy.
|
|
if ((HANDLE)GetProp(np->hwndBuddy, s_szUpdownClass) == npPrev)
|
|
{
|
|
RemoveProp(np->hwndBuddy, s_szUpdownClass);
|
|
SetProp(np->hwndBuddy, s_szUpdownClass, (HANDLE)np);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
np->nAccel = wParam;
|
|
for (i=0; i<(int)wParam; ++i)
|
|
{
|
|
np->udAccel[i] = ((LPUDACCEL)lParam)[i];
|
|
}
|
|
return(TRUE);
|
|
|
|
case UDM_GETACCEL:
|
|
if (wParam > np->nAccel)
|
|
{
|
|
wParam = np->nAccel;
|
|
}
|
|
for (i=0; i<(int)wParam; ++i)
|
|
{
|
|
((LPUDACCEL)lParam)[i] = np->udAccel[i];
|
|
}
|
|
return(np->nAccel);
|
|
|
|
|
|
case WM_CREATE:
|
|
// Allocate the instance data space.
|
|
np = (PUDSTATE)LocalAlloc(LPTR, sizeof(UDSTATE));
|
|
if (!np)
|
|
return -1;
|
|
|
|
SetWindowInt(hWnd, 0, (int)np);
|
|
|
|
#define lpCreate ((CREATESTRUCT FAR *)lParam)
|
|
|
|
np->hwnd = hWnd;
|
|
np->hwndParent = lpCreate->hwndParent;
|
|
np->style = lpCreate->style;
|
|
|
|
// np->fUp =
|
|
// np->fDown =
|
|
// np->fUnsigned =
|
|
// np->fSharedBorder =
|
|
// np->fSunkenBorder =
|
|
// FALSE;
|
|
|
|
if (lpCreate->dwExStyle & WS_EX_CLIENTEDGE)
|
|
np->fSunkenBorder = TRUE;
|
|
|
|
np->nBase = BASE_DECIMAL;
|
|
np->nUpper = 0;
|
|
np->nLower = 100;
|
|
np->nPos = 0;
|
|
np->hwndBuddy = NULL;
|
|
np->uClass = CLASS_UNKNOWN;
|
|
|
|
np->nAccel = NUM_UDACCELS;
|
|
np->udAccel[0].nSec = 0;
|
|
np->udAccel[0].nInc = 1;
|
|
np->udAccel[1].nSec = 2;
|
|
np->udAccel[1].nInc = 5;
|
|
np->udAccel[2].nSec = 5;
|
|
np->udAccel[2].nInc = 20;
|
|
|
|
/* This does the pickbuddy and anchor
|
|
*/
|
|
setbuddy(np, NULL);
|
|
setint(np);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (np) {
|
|
if (np->hwndBuddy || np->lpfnDefProc)
|
|
{
|
|
// Make sure that our buddy is unsubclassed.
|
|
DebugMsg(DM_ERROR, TEXT("UpDown Destroyed while buddy subclassed"));
|
|
np->fUpDownDestroyed = TRUE;
|
|
}
|
|
else
|
|
LocalFree((HLOCAL)np);
|
|
SetWindowInt(hWnd, 0, 0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// InitUpDownClass:
|
|
// Adds our WNDCLASS to the system.
|
|
//
|
|
#pragma code_seg(CODESEG_INIT)
|
|
|
|
BOOL FAR PASCAL InitUpDownClass(HINSTANCE hInst)
|
|
{
|
|
WNDCLASS wndclass;
|
|
|
|
if (!GetClassInfo(hInst, s_szUpdownClass, &wndclass))
|
|
{
|
|
#ifndef WIN32
|
|
extern LRESULT CALLBACK _UpDownWndProc(HWND, UINT, WPARAM, LPARAM);
|
|
wndclass.lpfnWndProc = _UpDownWndProc;
|
|
#else
|
|
wndclass.lpfnWndProc = (WNDPROC)UpDownWndProc;
|
|
#endif
|
|
wndclass.lpszClassName = s_szUpdownClass;
|
|
wndclass.hInstance = hInst;
|
|
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndclass.hIcon = NULL;
|
|
wndclass.lpszMenuName = NULL;
|
|
wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = sizeof(PUDSTATE);
|
|
|
|
return RegisterClass(&wndclass);
|
|
}
|
|
return TRUE;
|
|
}
|
|
#pragma code_seg()
|