📄 lmidoodler.cpp
字号:
#include <windows.h>
#include "LmiDoodler.h"
//
// Sample password input control. This code is not essential to implementing a replacement
// password component.
//
// Size of the grid tic marks
#define DOODLER_TICSIZE 4
// Element colors
#define DOODLER_COLORREF_TIC RGB(0,0,0)
#define DOODLER_COLORREF_SECURE RGB(204,204,204)
#define DOODLER_COLORREF_INPUT RGB(0,0,255)
#define DOODLER_COLORREF_FEEDBACK RGB(255,0,0)
//
// Various internal doodler structures
//
typedef unsigned char DOODLEDATA[25];
typedef struct
{
DOODLEDATA abVectorMap;
int nVectors;
POINT apMousePoints[64+2];
POINT ptLastVertex;
BOOL fSecure;
BOOL fCapture;
HPEN hpen;
HBRUSH hbrush;
int nExtent;
int nHOffset;
int nVOffset;
} DOODLER_WINDOW_INFO;
enum DOODLER_VECTOR
{
up = 0x01,
northeast = 0x02,
right = 0x04,
southeast = 0x08,
down = 0x10,
southwest = 0x20,
left = 0x40,
northwest = 0x80
};
static const POINT l_aVectorBias[8] = { {0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1} };
//
// Draw a blank doodle grid.
//
void LmiDoodler_Clear(
DOODLER_WINDOW_INFO* pdwi,
HDC hdc,
int nWidth,
int nHeight )
{
int nRow, nCol, nExtent;
POINT ptLine[2];
int nStep = (((nWidth>nHeight)?nHeight:nWidth)-1) >> 2;
RECT rc = { 0, 0, nWidth, nHeight };
// Calculate evenly spaced vertices, then calculate the extent of the control.
pdwi->nExtent = nExtent = nStep * 4;
// Center the grid within the client area.
pdwi->nHOffset = ( nWidth - nExtent ) / 2;
pdwi->nVOffset = ( nHeight - nExtent ) / 2;
FillRect( hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH) );
for ( nRow = 0; nRow <= nExtent; nRow += nStep )
for ( nCol = 0; nCol <= nExtent; nCol += nStep )
{
ptLine[0].x = nCol + pdwi->nHOffset; // Drawing horizontal tic
ptLine[0].y = nRow + pdwi->nVOffset;
ptLine[1].x = nCol + 1 + pdwi->nHOffset;
ptLine[1].y = nRow + pdwi->nVOffset;
if ( nCol > 0 )
ptLine[0].x -= DOODLER_TICSIZE;
if ( nCol < nExtent )
ptLine[1].x += DOODLER_TICSIZE;
Polyline( hdc, ptLine, 2 );
ptLine[0].x = nCol + pdwi->nHOffset; // Drawing vertical tic
ptLine[0].y = nRow + pdwi->nVOffset;
ptLine[1].x = nCol + pdwi->nHOffset;
ptLine[1].y = nRow + 1 + pdwi->nVOffset;
if ( nRow > 0 )
ptLine[0].y -= DOODLER_TICSIZE;
if ( nRow < nExtent )
ptLine[1].y += DOODLER_TICSIZE;
Polyline( hdc, ptLine, 2 );
}
}
//
// Handle a doodle input point
//
void LmiDoodler_HandlePoint(
DOODLER_WINDOW_INFO* pdwi,
POINT pt )
{
int x, y, nStep;
BYTE bDirection;
// Map point to nearest vertex
nStep = pdwi->nExtent / 4;
x = ( pt.x - pdwi->nHOffset + ( nStep / 2 ) ) / nStep;
y = ( pt.y - pdwi->nVOffset + ( nStep / 2 ) ) / nStep;
// To assist in handling diagonal lines, create a dead spot in the
// center area betwixt tics
if ( abs( x * nStep - ( pt.x - pdwi->nHOffset ) ) > ( nStep / 4 ) &&
abs( y * nStep - ( pt.y - pdwi->nVOffset ) ) > ( nStep / 4 ) )
return;
// First vector point case
if ( -1 == pdwi->ptLastVertex.x )
{
pdwi->ptLastVertex.x = x;
pdwi->ptLastVertex.y = y;
return;
}
// Record resulting vector
if ( y == pdwi->ptLastVertex.y ) // maybe horizontal
{
if ( x == pdwi->ptLastVertex.x ) // no vector yet
return;
else if ( x < pdwi->ptLastVertex.x )
bDirection = left;
else // ( x > pdwi->ptLastVertex.x )
bDirection = right;
}
else if ( x == pdwi->ptLastVertex.x ) // maybe vertical
{
if ( y == pdwi->ptLastVertex.y ) // no vector yet
return;
else if ( y < pdwi->ptLastVertex.y )
bDirection = up;
else // ( y > pdwi->ptLastVertex.y )
bDirection = down;
}
else if ( x < pdwi->ptLastVertex.x ) // leftward
{
if ( y < pdwi->ptLastVertex.y )
bDirection = northwest;
else // ( y > pdwi->ptLastVertex.y )
bDirection = southwest;
}
else // ( x > pdwi->ptLastVertex.x ) // rightward
{
if ( y < pdwi->ptLastVertex.y )
bDirection = northeast;
else // ( y > pdwi->ptLastVertex.y )
bDirection = southeast;
}
pdwi->abVectorMap[ pdwi->ptLastVertex.y * 5 + pdwi->ptLastVertex.x ] |= bDirection;
pdwi->ptLastVertex.y = y;
pdwi->ptLastVertex.x = x;
pdwi->nVectors++;
}
//
// Render vectors for feedback
//
void LmiDoodler_Render(
DOODLER_WINDOW_INFO* pdwi,
HDC hdc )
{
int x, y, d, nStep;
POINT pt[2];
HPEN hpen = CreatePen( PS_SOLID, 3, DOODLER_COLORREF_FEEDBACK );
hpen = (HPEN)SelectObject( hdc, hpen );
nStep = pdwi->nExtent / 4;
for ( y=0; y<5; y++ )
for ( x=0; x<5; x++ )
for ( d=0; d<8; d++ )
{
if ( !(pdwi->abVectorMap[y*5+x] & (1<<d)) )
continue;
pt[0].x = pt[1].x = x * nStep + pdwi->nHOffset;
pt[0].y = pt[1].y = y * nStep + pdwi->nVOffset;
pt[1].x += nStep * l_aVectorBias[d].x;
pt[1].y += nStep * l_aVectorBias[d].y;
Polyline( hdc, pt, 2 );
}
hpen = (HPEN)SelectObject( hdc, hpen );
DeleteObject( hpen );
}
//
// Adjust the data to the upper left corner. This is so the path
// can be doodled for verification independent of where the user
// cares to put the pen down. Do nothing here and the user will
// have to know exactly which vertex to start on.
//
void LmiDoodler_Normalize( DOODLEDATA* pData )
{
int i;
for ( i=0; (i<25) && (!(*pData)[i]); i++ );
if ( ( i>=25 ) || ( !i ) )
return;
memmove( (*pData), (*pData) + i, 25 - i );
memset( (*pData) + 25 - i, 0, i );
}
//
// Prevent point from going outside useful area.
//
void LmiDoodler_Constrain(
DOODLER_WINDOW_INFO* pdwi,
POINT* pt )
{
if ( pt->x < pdwi->nHOffset )
pt->x = pdwi->nHOffset;
else if ( pt->x > pdwi->nHOffset + pdwi->nExtent )
pt->x = pdwi->nHOffset + pdwi->nExtent;
if ( pt->y < pdwi->nVOffset )
pt->y = pdwi->nVOffset;
else if ( pt->y > pdwi->nVOffset + pdwi->nExtent )
pt->y = pdwi->nVOffset + pdwi->nExtent;
}
//
// Draw the window and handle doodling
//
LRESULT WINAPI LmiDoodler_WndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam )
{
DOODLER_WINDOW_INFO* pdwi = (DOODLER_WINDOW_INFO*)GetWindowLong( hwnd, 0 );
switch( uMsg )
{
case WM_CREATE:
pdwi = (DOODLER_WINDOW_INFO*)LocalAlloc( 0, sizeof(DOODLER_WINDOW_INFO) );
SetWindowLong( hwnd, 0, (LONG)pdwi );
pdwi->fSecure = TRUE;
pdwi->fCapture = FALSE;
pdwi->hpen = (HPEN)0;
pdwi->hbrush = (HBRUSH)0;
pdwi->apMousePoints[0].x = pdwi->apMousePoints[1].x = -1; // Reset input array
pdwi->ptLastVertex.x = -1;
case WM_DESTROY:
if (pdwi->hpen )
DeleteObject( pdwi->hpen );
if (pdwi->hbrush)
DeleteObject( pdwi->hbrush );
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
//if ( GetUpdateRect( hwnd, 0, FALSE ) )
{
RECT rc;
HDC hdcPaint = BeginPaint( hwnd, &ps );
GetClientRect( hwnd, &rc );
LmiDoodler_Clear( pdwi, hdcPaint, rc.right - rc.left, rc.bottom - rc.top );
EndPaint( hwnd, &ps );
}
return 0;
}
case WM_LBUTTONDOWN:
{
SetCapture( hwnd );
pdwi->fCapture = TRUE;
pdwi->nVectors = 0;
COLORREF color = pdwi->fSecure ? DOODLER_COLORREF_SECURE : DOODLER_COLORREF_INPUT;
pdwi->hpen = CreatePen( PS_SOLID, 3, color );
pdwi->hbrush = CreateSolidBrush( color );
HDC hdc = GetDC( hwnd );
HPEN hpenSave = (HPEN)SelectObject( hdc, pdwi->hpen );
HBRUSH hbrushSave = (HBRUSH)SelectObject( hdc, pdwi->hbrush );
POINT point = { LOWORD(lParam), HIWORD(lParam) };
LmiDoodler_Constrain( pdwi, &point );
pdwi->apMousePoints[1] = point;
memset( pdwi->abVectorMap, 0, sizeof(DOODLEDATA) );
Ellipse( hdc, point.x-1, point.y-1, point.x+1, point.y+1 );
LmiDoodler_HandlePoint( pdwi, point );
SelectObject( hdc, hpenSave );
SelectObject( hdc, hbrushSave );
ReleaseDC( hwnd, hdc );
return 0;
}
case WM_MOUSEMOVE:
{
if ( !pdwi->fCapture )
return 0;
HDC hdc = GetDC( hwnd );
UINT n, cp;
HPEN hpenSave = (HPEN)SelectObject( hdc, pdwi->hpen );
cp = 0;
GetMouseMovePoints( &pdwi->apMousePoints[2], 64, &cp );
for ( n=0; n<cp; n++ )
{
pdwi->apMousePoints[2+n].x >>= 2;
pdwi->apMousePoints[2+n].y >>= 2;
ScreenToClient( hwnd, &pdwi->apMousePoints[2+n] );
LmiDoodler_Constrain( pdwi, &pdwi->apMousePoints[2+n] );
LmiDoodler_HandlePoint( pdwi, pdwi->apMousePoints[2+n] );
}
n = 2;
if ( pdwi->apMousePoints[1].x != -1 )
cp++, n--;
if ( pdwi->apMousePoints[0].x != -1 )
cp++, n--;
if ( cp > 1 )
Polyline( hdc, &pdwi->apMousePoints[n], cp );
pdwi->apMousePoints[0] = pdwi->apMousePoints[n+cp-2];
pdwi->apMousePoints[1] = pdwi->apMousePoints[n+cp-1];
SelectObject( hdc, hpenSave );
ReleaseDC( hwnd, hdc );
return 0;
}
case WM_LBUTTONUP:
{
if ( !pdwi->fCapture )
return 0;
DeleteObject( pdwi->hpen ); pdwi->hpen = (HPEN)0;
DeleteObject( pdwi->hbrush ); pdwi->hbrush = (HBRUSH)0;
pdwi->apMousePoints[0].x = pdwi->apMousePoints[1].x = -1; // Reset input array
pdwi->ptLastVertex.x = -1;
SetCapture( (HWND)0 );
pdwi->fCapture = FALSE;
HDC hdc = GetDC( hwnd );
if ( !pdwi->fSecure )
LmiDoodler_Render( pdwi, hdc );
ReleaseDC( hwnd, hdc );
// Let parent know that input is ready.
SendMessage( GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLong(hwnd,GWL_ID),DIN_READY), (LPARAM)hwnd );
return 0;
}
case DIM_CLEAR:
InvalidateRect( hwnd, 0, FALSE );
return 0;
case DIM_GETDATA:
{
if ( !pdwi->nVectors )
return 0;
DOODLEDATA* pData = (DOODLEDATA*)LocalAlloc( 0, sizeof(DOODLEDATA) );
memcpy( pData, pdwi->abVectorMap, sizeof(DOODLEDATA) );
LmiDoodler_Normalize( pData );
return (LRESULT)pData;
}
case DIM_GETDATALEN:
return sizeof(DOODLEDATA);
case DIM_SECURE:
pdwi->fSecure = (BOOL)wParam;
return 0;
case DIM_COMPARE:
if ( !wParam || !lParam )
return ( (DWORD)wParam == (DWORD)lParam );
return memcmp( (void*)wParam, (void*)lParam, sizeof(DOODLEDATA) );
}
return DefWindowProc( hwnd, uMsg, wParam, lParam );
}
//
// Intialize the control class
//
BOOL LmiDoodler_Init( HINSTANCE hInstance )
{
WNDCLASS wc;
if ( GetClassInfo( GetModuleHandle(0), DOODLER_CLASS, &wc ) )
return TRUE;
memset( &wc, 0, sizeof(wc) );
wc.cbWndExtra = sizeof(DOODLER_WINDOW_INFO*);
wc.hInstance = GetModuleHandle(0);
wc.lpfnWndProc = LmiDoodler_WndProc;
wc.lpszClassName = DOODLER_CLASS;
return (ATOM)0 != RegisterClass( &wc );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -