mirror of
https://github.com/reactos/wine.git
synced 2024-11-29 22:50:43 +00:00
Fix (rewrite), and add documentation to LISTVIEW_ProcessLetterKeys.
Lowered the key repetition delay (Aric).
This commit is contained in:
parent
a7faef9516
commit
1afa24aeac
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user