Fix (rewrite), and add documentation to LISTVIEW_ProcessLetterKeys.

Lowered the key repetition delay (Aric).
This commit is contained in:
François Gouget 2001-02-12 18:07:43 +00:00 committed by Alexandre Julliard
parent a7faef9516
commit 1afa24aeac

View File

@ -122,10 +122,10 @@ typedef struct tagLISTVIEW_INFO
DWORD dwHoverTime;
INT nColumnCount; /* the number of columns in this control */
DWORD lastKeyPressTimestamp; /* Added */
WPARAM charCode; /* Added */
CHAR szSearchParam[ MAX_PATH ]; /* Added */
DWORD timeSinceLastKeyPress; /* Added */
INT nSearchParamLength; /* Added */
CHAR szSearchParam[ MAX_PATH ]; /* Added */
} LISTVIEW_INFO;
/*
@ -171,7 +171,6 @@ typedef struct tagLISTVIEW_INFO
/*
* macros
*/
#define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
#define ListView_LVNotify(hwnd,lCtrlId,plvnm) \
(BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMLISTVIEW)(plvnm))
#define ListView_Notify(hwnd,lCtrlId,pnmh) \
@ -235,14 +234,8 @@ static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
/******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
#define KEY_DELAY 900
#define LISTVIEW_InitLvItemStruct(item,idx,TEXT) \
ZeroMemory(&(item), sizeof(LVITEMA)); \
(item).mask = LVIF_TEXT; \
(item).iItem = (idx); \
(item).iSubItem = 0; \
(item).pszText = (TEXT); \
(item).cchTextMax = MAX_PATH
#define KEY_DELAY 450
static BOOL
LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
@ -339,231 +332,157 @@ LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
/*************************************************************************
* DESCRIPTION:
* Processes keyboard messages generated by pressing the letter keys on the keyboard.
* Assumes the list is sorted alphabetically, without regard to case.
*
* PARAMETERS:
* [I] HWND: handle to the window
* [I] WPARAM: the character code, the actual character
* [I] LPARAM: key data
*
*
* RETURN:
* Zero.
*
* TODO:
*
*
*/
static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData )
* LISTVIEW_ProcessLetterKeys
*
* Processes keyboard messages generated by pressing the letter keys
* on the keyboard.
* What this does is perform a case insensitive search from the
* current position with the following quirks:
* - If two chars or more are pressed in quick succession we search
* for the corresponding string (e.g. 'abc').
* - If there is a delay we wipe away the current search string and
* restart with just that char.
* - If the user keeps pressing the same character, whether slowly or
* fast, so that the search string is entirely composed of this
* character ('aaaaa' for instance), then we search for first item
* that starting with that character.
* - If the user types the above character in quick succession, then
* we must also search for the corresponding string ('aaaaa'), and
* go to that string if there is a match.
*
* RETURNS
*
* Zero.
*
* BUGS
*
* - The current implementation has a list of characters it will
* accept and it ignores averything else. In particular it will
* ignore accentuated characters which seems to match what
* Windows does. But I'm not sure it makes sense to follow
* Windows there.
* - We don't sound a beep when the search fails.
*
* SEE ALSO
*
* TREEVIEW_ProcessLetterKeys
*/
static INT LISTVIEW_ProcessLetterKeys(
HWND hwnd, /* handle to the window */
WPARAM charCode, /* the character code, the actual character */
LPARAM keyData /* key data */
)
{
LISTVIEW_INFO *infoPtr = NULL;
INT nItem = -1;
BOOL bRedraw;
INT nSize = 0;
INT idx = 0;
BOOL bFoundMatchingFiles = FALSE;
LISTVIEW_INFO *infoPtr;
INT nItem;
INT nSize;
INT endidx,idx;
LVITEMA item;
CHAR TEXT[ MAX_PATH ];
CHAR szCharCode[ 2 ];
DWORD timeSinceLastKeyPress = 0;
szCharCode[0] = charCode;
szCharCode[1] = 0;
/* simple parameter checking */
if ( !hwnd || !charCode || !keyData )
CHAR buffer[MAX_PATH];
DWORD timestamp,elapsed;
/* simple parameter checking */
if (!hwnd || !charCode || !keyData)
return 0;
infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
if ( !infoPtr )
infoPtr=(LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
if (!infoPtr)
return 0;
/* only allow the valid WM_CHARs through */
if ( isalnum( charCode ) || charCode == '.' || charCode == '`' || charCode == '!'
|| charCode == '@' || charCode == '#' || charCode == '$' || charCode == '%'
|| charCode == '^' || charCode == '&' || charCode == '*' || charCode == '('
|| charCode == ')' || charCode == '-' || charCode == '_' || charCode == '+'
|| charCode == '=' || charCode == '\\'|| charCode == ']' || charCode == '}'
|| charCode == '[' || charCode == '{' || charCode == '/' || charCode == '?'
|| charCode == '>' || charCode == '<' || charCode == ',' || charCode == '~')
{
timeSinceLastKeyPress = GetTickCount();
nSize = GETITEMCOUNT( infoPtr );
/* if there are 0 items, there is no where to go */
if ( nSize == 0 )
return 0;
/*
* If the last charCode equals the current charCode then look
* to the next element in list to see if it matches the previous
* charCode.
*/
if ( infoPtr->charCode == charCode )
{
if ( timeSinceLastKeyPress - infoPtr->timeSinceLastKeyPress < KEY_DELAY )
{ /* append new character to search string */
strcat( infoPtr->szSearchParam, szCharCode );
infoPtr->nSearchParamLength++;
/* loop from start of list view */
for( idx = infoPtr->nFocusedItem; idx < nSize; idx++ )
{ /* get item */
LISTVIEW_InitLvItemStruct( item, idx, TEXT );
ListView_GetItemA( hwnd, &item );
/* compare items */
if ( strncasecmp( item.pszText, infoPtr->szSearchParam,
infoPtr->nSearchParamLength ) == 0 )
{
nItem = idx;
break;
}
}
}
else if ( infoPtr->timeSinceLastKeyPress > timeSinceLastKeyPress )
{ /* The DWORD went over it's boundary?? Ergo assuming too slow??. */
for ( idx = 0; idx < nSize; idx++ )
{
LISTVIEW_InitLvItemStruct( item, idx, TEXT );
ListView_GetItemA( hwnd, &item );
if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
{
nItem = idx;
break;
}
}
strcpy( infoPtr->szSearchParam, szCharCode );
infoPtr->nSearchParamLength = 1;
}
else
{ /* Save szCharCode for use in later searches */
strcpy( infoPtr->szSearchParam, szCharCode );
infoPtr->nSearchParamLength = 1;
LISTVIEW_InitLvItemStruct( item, infoPtr->nFocusedItem + 1, TEXT );
ListView_GetItemA( hwnd, &item );
if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
nItem = infoPtr->nFocusedItem + 1;
else
{ /*
* Ok so there are no more folders that match
* now we look for files.
*/
for ( idx = infoPtr->nFocusedItem + 1; idx < nSize; idx ++ )
{
LISTVIEW_InitLvItemStruct( item, idx, TEXT );
ListView_GetItemA( hwnd, &item );
if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
{
nItem = idx;
bFoundMatchingFiles = TRUE;
break;
}
}
if ( !bFoundMatchingFiles )
{ /* go back to first instance */
for ( idx = 0; idx < nSize; idx ++ )
{
LISTVIEW_InitLvItemStruct( item,idx, TEXT );
ListView_GetItemA( hwnd, &item );
if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
{
nItem = idx;
break;
}
}
}
}
}
} /*END: if ( infoPtr->charCode == charCode )*/
else /* different keypressed */
{
/* could be that they are spelling the file/directory for us */
if ( timeSinceLastKeyPress - infoPtr->timeSinceLastKeyPress > KEY_DELAY )
{ /*
* Too slow, move to the first instance of the
* charCode.
*/
for ( idx = 0; idx < nSize; idx++ )
{
LISTVIEW_InitLvItemStruct( item,idx, TEXT );
ListView_GetItemA( hwnd, &item );
if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
{
nItem = idx;
break;
}
}
strcpy( infoPtr->szSearchParam, szCharCode );
infoPtr->nSearchParamLength = 1;
}
else if ( infoPtr->timeSinceLastKeyPress > timeSinceLastKeyPress )
{ /* The DWORD went over it's boundery?? Ergo assuming too slow??. */
for ( idx = 0; idx < nSize; idx++ )
{
LISTVIEW_InitLvItemStruct( item,idx, TEXT );
ListView_GetItemA( hwnd, &item );
if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
{
nItem = idx;
break;
}
}
strcpy( infoPtr->szSearchParam, szCharCode );
infoPtr->nSearchParamLength = 1;
}
else /* Search for the string the user is typing */
{
/* append new character to search string */
strcat( infoPtr->szSearchParam, szCharCode );
infoPtr->nSearchParamLength++;
/* loop from start of list view */
for( idx = 0; idx < nSize; idx++ )
{ /* get item */
LISTVIEW_InitLvItemStruct( item, idx, TEXT );
ListView_GetItemA( hwnd, &item );
/* compare items */
if ( strncasecmp( item.pszText, infoPtr->szSearchParam,
infoPtr->nSearchParamLength ) == 0 )
{
nItem = idx;
break;
}
}
}
}/*END: else */
}
else
if (!isalnum(charCode) &&
charCode != '.' && charCode != '`' && charCode != '!' &&
charCode != '@' && charCode != '#' && charCode != '$' &&
charCode != '%' && charCode != '^' && charCode != '&' &&
charCode != '*' && charCode != '(' && charCode != ')' &&
charCode != '-' && charCode != '_' && charCode != '+' &&
charCode != '=' && charCode != '\\'&& charCode != ']' &&
charCode != '}' && charCode != '[' && charCode != '{' &&
charCode != '/' && charCode != '?' && charCode != '>' &&
charCode != '<' && charCode != ',' && charCode != '~')
return 0;
bRedraw = LISTVIEW_KeySelection(hwnd, nItem );
if (bRedraw != FALSE)
{
/* refresh client area */
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
nSize=GETITEMCOUNT(infoPtr);
/* if there's one item or less, there is no where to go */
if (nSize <= 1)
return 0;
/* compute how much time elapsed since last keypress */
timestamp=GetTickCount();
if (timestamp > infoPtr->lastKeyPressTimestamp) {
elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
} else {
elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
}
/* Store the WM_CHAR for next time */
infoPtr->charCode = charCode;
/* Store time */
infoPtr->timeSinceLastKeyPress = timeSinceLastKeyPress;
return 0;
/* update the search parameters */
infoPtr->lastKeyPressTimestamp=timestamp;
if (elapsed < KEY_DELAY) {
if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam)) {
infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
}
if (infoPtr->charCode != charCode) {
infoPtr->charCode=charCode=0;
}
} else {
infoPtr->charCode=charCode;
infoPtr->szSearchParam[0]=charCode;
infoPtr->nSearchParamLength=1;
/* Redundant with the 1 char string */
charCode=0;
}
/* and search from the current position */
nItem=-1;
if (infoPtr->nFocusedItem >= 0) {
endidx=infoPtr->nFocusedItem;
idx=endidx;
/* if looking for single character match,
* then we must always move forward
*/
if (infoPtr->nSearchParamLength == 1)
idx++;
} else {
endidx=nSize;
idx=0;
}
do {
if (idx == nSize) {
if (endidx == nSize)
break;
idx=0;
}
/* get item */
ZeroMemory(&item, sizeof(item));
item.mask = LVIF_TEXT;
item.iItem = idx;
item.iSubItem = 0;
item.pszText = buffer;
item.cchTextMax = sizeof(buffer);
ListView_GetItemA( hwnd, &item );
/* check for a match */
if (strncasecmp(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
nItem=idx;
break;
} else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
(strncasecmp(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
/* This would work but we must keep looking for a longer match */
nItem=idx;
}
idx++;
} while (idx != endidx);
if (nItem != -1) {
if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
/* refresh client area */
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
}
return 0;
}
/*************************************************************************
@ -8634,7 +8553,9 @@ static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)))
LISTVIEW_SetItemFocus(hwnd,nItem);
if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
!LISTVIEW_IsSelected(hwnd,nItem))
{
LISTVIEW_SetSelection(hwnd, nItem);
}
@ -9285,10 +9206,9 @@ static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
case LVM_UPDATE:
return LISTVIEW_Update(hwnd, (INT)wParam);
/* case WM_CHAR: */
case WM_CHAR:
return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
case WM_COMMAND:
return LISTVIEW_Command(hwnd, wParam, lParam);