Windows NT 4.0 source code leak
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

#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()