Listview typing completion
François Gouget
fgouget at codeweavers.com
Sun Feb 11 13:32:20 CST 2001
When one types something in a listview, the cursor normally goes to
the first item matching what is being typed.
The previous code did not work if you quickly typed the same
character twice: if you type "nnn" the cursor should go to the first
item matching "nnn" (e.g. "nnno") or to the third item starting with an
"n". I couldn't resist rewriting TREEVIEW_ProcessLetterKeys. I believe
the new code is simpler and AFAIK it handles everything just fine.
Changelog:
François Gouget <fgouget at codeweavers.com>
* dlls/comctl32/listview.c
Fix (rewrite), and add documentation to LISTVIEW_ProcessLetterKeys
Lowered the key repetition delay (Aric)
--
François Gouget
fgouget at codeweavers.com
-------------- next part --------------
@@ -122,10 +124,10 @@
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 +173,6 @@
/*
* 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 +236,8 @@
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 +334,157 @@
/*************************************************************************
-* 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 (!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;
+
+ 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;
+ }
+
+ /* 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->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 */
+ if (infoPtr->nSearchParamLength == 1)
+ idx++;
+ } else {
+ endidx=nSize;
+ idx=0;
}
- else
- return 0;
-
- bRedraw = LISTVIEW_KeySelection(hwnd, nItem );
- if (bRedraw != FALSE)
- {
- /* refresh client area */
- InvalidateRect(hwnd, NULL, TRUE);
- UpdateWindow(hwnd);
+ 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);
+ }
}
- /* Store the WM_CHAR for next time */
- infoPtr->charCode = charCode;
-
- /* Store time */
- infoPtr->timeSinceLastKeyPress = timeSinceLastKeyPress;
-
return 0;
-
}
/*************************************************************************
@@ -9276,10 +9197,9 @@
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);
More information about the wine-patches
mailing list