/* * Listbox controls * * Copyright Martin Ayotte, 1993 * Constantine Sapuntzakis, 1995 * Alex Korobka, 1995, 1996 * */ /* * FIXME: * - proper scrolling for multicolumn style * - anchor and caret for LBS_EXTENDEDSEL * - proper selection with keyboard * - how to handle (LBS_EXTENDEDSEL | LBS_MULTIPLESEL) style * - support for LBS_NOINTEGRALHEIGHT and LBS_OWNERDRAWVARIABLE styles */ #include #include #include #include #include "windows.h" #include "win.h" #include "gdi.h" #include "msdos.h" #include "listbox.h" #include "dos_fs.h" #include "drive.h" #include "file.h" #include "heap.h" #include "stddebug.h" #include "debug.h" #include "xmalloc.h" #define LIST_HEAP_ALLOC(lphl,f,size) \ LOCAL_Alloc( lphl->HeapSel, LMEM_FIXED, (size) ) #define LIST_HEAP_FREE(lphl,handle) \ LOCAL_Free( lphl->HeapSel, (handle) ) #define LIST_HEAP_ADDR(lphl,handle) \ ((handle) ? PTR_SEG_OFF_TO_LIN(lphl->HeapSel, (handle)) : NULL) #define LIST_HEAP_SIZE 0x10000 #define LBMM_EDGE 4 /* distance inside box which is same as moving mouse outside box, to trigger scrolling of LB */ #define MATCH_SUBSTR 2 #define MATCH_EXACT 1 #define MATCH_NEAREST 0 static void ListBoxInitialize(LPHEADLIST lphl) { lphl->lpFirst = NULL; lphl->ItemsCount = 0; lphl->ItemsVisible = 0; lphl->FirstVisible = 0; lphl->ColumnsVisible = 1; lphl->ItemsPerColumn = 0; lphl->ItemFocused = -1; lphl->PrevFocused = -1; } void CreateListBoxStruct(HWND hwnd, WORD CtlType, LONG styles, HWND parent) { LPHEADLIST lphl; HDC32 hdc; lphl = (LPHEADLIST)xmalloc(sizeof(HEADLIST)); SetWindowLong32A(hwnd, 0, (LONG)lphl); ListBoxInitialize(lphl); lphl->DrawCtlType = CtlType; lphl->CtlID = GetWindowWord(hwnd,GWW_ID); lphl->bRedrawFlag = TRUE; lphl->iNumStops = 0; lphl->TabStops = NULL; lphl->hFont = GetStockObject32(SYSTEM_FONT); lphl->hSelf = hwnd; if (CtlType==ODT_COMBOBOX) /* use the "faked" style for COMBOLBOX */ /* LBS_SORT instead CBS_SORT e.g. */ lphl->dwStyle = MAKELONG(LOWORD(styles),HIWORD(GetWindowLong32A(hwnd,GWL_STYLE))); else lphl->dwStyle = GetWindowLong32A(hwnd,GWL_STYLE); /* use original style dword */ lphl->hParent = parent; lphl->StdItemHeight = 15; /* FIXME: should get the font height */ lphl->OwnerDrawn = styles & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE); lphl->HasStrings = (styles & LBS_HASSTRINGS) || !lphl->OwnerDrawn; /* create dummy hdc to set text height */ if ((hdc = GetDC32(0))) { TEXTMETRIC16 tm; GetTextMetrics16( hdc, &tm ); lphl->StdItemHeight = tm.tmHeight; dprintf_listbox(stddeb,"CreateListBoxStruct: font height %d\n", lphl->StdItemHeight); ReleaseDC32( 0, hdc ); } if (lphl->OwnerDrawn) { LISTSTRUCT dummyls; lphl->needMeasure = TRUE; dummyls.mis.CtlType = lphl->DrawCtlType; dummyls.mis.CtlID = lphl->CtlID; dummyls.mis.itemID = -1; dummyls.mis.itemWidth = 0; /* ignored */ dummyls.mis.itemData = 0; ListBoxAskMeasure(lphl,&dummyls); } lphl->HeapSel = GlobalAlloc16(GMEM_FIXED,LIST_HEAP_SIZE); LocalInit( lphl->HeapSel, 0, LIST_HEAP_SIZE-1); } /* Send notification "code" as part of a WM_COMMAND-message if hwnd has the LBS_NOTIFY style */ void ListBoxSendNotification(LPHEADLIST lphl, WORD code) { if (lphl->dwStyle & LBS_NOTIFY) SendMessage32A( lphl->hParent, WM_COMMAND, MAKEWPARAM( lphl->CtlID, code), (LPARAM)lphl->hSelf ); } /* get the maximum value of lphl->FirstVisible */ int ListMaxFirstVisible(LPHEADLIST lphl) { int m = lphl->ItemsCount-lphl->ItemsVisible; return (m < 0) ? 0 : m; } /* Returns: 0 if nothing needs to be changed */ /* 1 if FirstVisible changed */ int ListBoxScrollToFocus(LPHEADLIST lphl) { short end; if (lphl->ItemsCount == 0) return 0; if (lphl->ItemFocused == -1) return 0; end = lphl->FirstVisible + lphl->ItemsVisible - 1; if (lphl->ItemFocused < lphl->FirstVisible ) { lphl->FirstVisible = lphl->ItemFocused; return 1; } else { if (lphl->ItemFocused > end) { WORD maxFirstVisible = ListMaxFirstVisible(lphl); lphl->FirstVisible = lphl->ItemFocused; if (lphl->FirstVisible > maxFirstVisible) { lphl->FirstVisible = maxFirstVisible; } return 1; } } return 0; } LPLISTSTRUCT ListBoxGetItem(LPHEADLIST lphl, UINT uIndex) { LPLISTSTRUCT lpls; UINT Count = 0; if (uIndex >= lphl->ItemsCount) return NULL; lpls = lphl->lpFirst; while (Count++ < uIndex) lpls = lpls->lpNext; return lpls; } void ListBoxDrawItem(HWND hwnd, LPHEADLIST lphl, HDC16 hdc, LPLISTSTRUCT lpls, RECT16 *rect, WORD itemAction, WORD itemState) { if (lphl->OwnerDrawn) { DRAWITEMSTRUCT32 dis; dis.CtlID = lpls->mis.CtlID; dis.CtlType = lpls->mis.CtlType; dis.itemID = lpls->mis.itemID; dis.hDC = hdc; dis.hwndItem = hwnd; dis.itemData = lpls->mis.itemData; dis.itemAction = itemAction; dis.itemState = itemState; CONV_RECT16TO32( rect, &dis.rcItem ); SendMessage32A( lphl->hParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis ); return; } if (itemAction == ODA_DRAWENTIRE || itemAction == ODA_SELECT) { int OldBkMode; DWORD dwOldTextColor = 0; OldBkMode = SetBkMode(hdc, TRANSPARENT); if (itemState != 0) { dwOldTextColor = SetTextColor(hdc, 0x00FFFFFFL); FillRect16(hdc, rect, GetStockObject32(BLACK_BRUSH)); } if (lphl->dwStyle & LBS_USETABSTOPS) { TabbedTextOut(hdc, rect->left + 5, rect->top + 2, (char *)lpls->itemText, strlen((char *)lpls->itemText), lphl->iNumStops, lphl->TabStops, 0); } else { TextOut16(hdc, rect->left + 5, rect->top + 2, (char *)lpls->itemText, strlen((char *)lpls->itemText)); } if (itemState != 0) { SetTextColor(hdc, dwOldTextColor); } SetBkMode(hdc, OldBkMode); } else DrawFocusRect16(hdc, rect); } int ListBoxFindMouse(LPHEADLIST lphl, int X, int Y) { LPLISTSTRUCT lpls = lphl->lpFirst; int i, j; POINT16 point; point.x = X; point.y = Y; if (lphl->ItemsCount == 0) return LB_ERR; for(i = 0; i < lphl->FirstVisible; i++) { if (lpls == NULL) return LB_ERR; lpls = lpls->lpNext; } for(j = 0; j < lphl->ItemsVisible; i++, j++) { if (lpls == NULL) return LB_ERR; if (PtInRect16(&lpls->itemRect,point)) { return i; } lpls = lpls->lpNext; } dprintf_listbox(stddeb,"ListBoxFindMouse: not found\n"); return LB_ERR; } BOOL32 lbDeleteItemNotify(LPHEADLIST lphl, LPLISTSTRUCT lpls) { /* called only for owner drawn listboxes */ BOOL32 ret; DELETEITEMSTRUCT16 *delItem = SEGPTR_NEW(DELETEITEMSTRUCT16); if (!delItem) return FALSE; delItem->CtlType = lphl->DrawCtlType; delItem->CtlID = lphl->CtlID; delItem->itemID = lpls->mis.itemID; delItem->hwndItem = lphl->hSelf; delItem->itemData = lpls->mis.itemData; ret = SendMessage16( lphl->hParent, WM_DELETEITEM, (WPARAM16)lphl->CtlID, (LPARAM)SEGPTR_GET(delItem) ); SEGPTR_FREE(delItem); return ret; } /* -------------------- strings and item data ---------------------- */ void ListBoxAskMeasure(LPHEADLIST lphl, LPLISTSTRUCT lpls) { MEASUREITEMSTRUCT16 *lpmeasure = SEGPTR_NEW(MEASUREITEMSTRUCT16); if (!lpmeasure) return; *lpmeasure = lpls->mis; lpmeasure->itemHeight = lphl->StdItemHeight; SendMessage16( lphl->hParent, WM_MEASUREITEM, lphl->CtlID, (LPARAM)SEGPTR_GET(lpmeasure) ); if (lphl->dwStyle & LBS_OWNERDRAWFIXED) { if (lpmeasure->itemHeight > lphl->StdItemHeight) lphl->StdItemHeight = lpmeasure->itemHeight; lpls->mis.itemHeight = lpmeasure->itemHeight; } SEGPTR_FREE(lpmeasure); } static LPLISTSTRUCT ListBoxCreateItem(LPHEADLIST lphl, int id) { LPLISTSTRUCT lplsnew = (LPLISTSTRUCT)malloc(sizeof(LISTSTRUCT)); if (lplsnew == NULL) return NULL; lplsnew->itemState = 0; lplsnew->mis.CtlType = lphl->DrawCtlType; lplsnew->mis.CtlID = lphl->CtlID; lplsnew->mis.itemID = id; lplsnew->mis.itemHeight = lphl->StdItemHeight; lplsnew->mis.itemWidth = 0; /* ignored */ lplsnew->mis.itemData = 0; SetRectEmpty16( &lplsnew->itemRect ); return lplsnew; } static int ListBoxAskCompare(LPHEADLIST lphl, int startItem, SEGPTR matchData, BOOL exactMatch ) { /* Do binary search for sorted listboxes. Linked list item storage sort of * defeats the purpose ( forces to traverse item list all the time ) but M$ does it this way... * * MATCH_NEAREST (0) - return position for insertion - for all styles * MATCH_EXACT (1) - search for an item, return index or LB_ERR * MATCH_SUBSTR (2) - same as exact match but with strncmp for string comparision */ COMPAREITEMSTRUCT16 *itemCmp; LPLISTSTRUCT currentItem = NULL; LPCSTR matchStr = (lphl->HasStrings)?(LPCSTR)PTR_SEG_TO_LIN(matchData):NULL; int head, pos = -1, tail, loop = 1; short b = 0, s_length = 0; /* check if empty */ if( !lphl->ItemsCount ) return (exactMatch)? LB_ERR: 0; /* set up variables */ if( exactMatch == MATCH_NEAREST ) startItem = 0; else if( ++startItem ) { loop = 2; if( startItem >= lphl->ItemsCount ) startItem = lphl->ItemsCount - 1; } if( exactMatch == MATCH_SUBSTR && lphl->HasStrings ) { s_length = strlen( matchStr ); if( !s_length ) return 0; /* head of the list - empty string */ } head = startItem; tail = lphl->ItemsCount - 1; dprintf_listbox(stddeb,"AskCompare: head = %i, tail = %i, data = %08x\n", head, tail, (unsigned)matchData ); if (!(itemCmp = SEGPTR_NEW(COMPAREITEMSTRUCT16))) return 0; itemCmp->CtlType = lphl->DrawCtlType; itemCmp->CtlID = lphl->CtlID; itemCmp->hwndItem = lphl->hSelf; /* search from startItem */ while ( loop-- ) { while( head <= tail ) { pos = (tail + head)/2; currentItem = ListBoxGetItem( lphl, pos ); if( lphl->HasStrings ) { b = ( s_length )? lstrncmpi32A( currentItem->itemText, matchStr, s_length) : lstrcmpi32A( currentItem->itemText, matchStr); } else { itemCmp->itemID1 = pos; itemCmp->itemData1 = currentItem->mis.itemData; itemCmp->itemID2 = -1; itemCmp->itemData2 = matchData; b = SendMessage16( lphl->hParent, WM_COMPAREITEM, (WPARAM16)lphl->CtlID, (LPARAM)SEGPTR_GET(itemCmp) ); } if( b == 0 ) { SEGPTR_FREE(itemCmp); return pos; /* found exact match */ } else if( b < 0 ) head = ++pos; else if( b > 0 ) tail = pos - 1; } /* reset to search from the first item */ head = 0; tail = startItem - 1; } dprintf_listbox(stddeb,"\t-> pos = %i\n", pos ); SEGPTR_FREE(itemCmp); /* if we got here match is not exact */ if( pos < 0 ) pos = 0; else if( pos > lphl->ItemsCount ) pos = lphl->ItemsCount; return (exactMatch)? LB_ERR: pos; } int ListBoxInsertString(LPHEADLIST lphl, UINT uIndex, LPCSTR newstr) { LPLISTSTRUCT *lppls, lplsnew, lpls; HANDLE16 hStr; LPSTR str; UINT Count; dprintf_listbox(stddeb,"ListBoxInsertString(%d, %p);\n", uIndex, newstr); if (!newstr) return -1; if (uIndex == (UINT)-1) uIndex = lphl->ItemsCount; lppls = &lphl->lpFirst; for(Count = 0; Count < uIndex; Count++) { if (*lppls == NULL) return LB_ERR; lppls = (LPLISTSTRUCT *) &(*lppls)->lpNext; } lplsnew = ListBoxCreateItem(lphl, Count); if (lplsnew == NULL) { fprintf(stdnimp,"ListBoxInsertString() out of memory !\n"); return LB_ERRSPACE; } lplsnew->lpNext = *lppls; *lppls = lplsnew; lphl->ItemsCount++; hStr = 0; if (lphl->HasStrings) { dprintf_listbox(stddeb," string: %s\n", newstr); hStr = LIST_HEAP_ALLOC(lphl, LMEM_MOVEABLE, strlen(newstr) + 1); str = (LPSTR)LIST_HEAP_ADDR(lphl, hStr); if (str == NULL) return LB_ERRSPACE; strcpy(str, newstr); lplsnew->itemText = str; /* I'm not so sure about the next one */ lplsnew->mis.itemData = 0; } else { lplsnew->itemText = NULL; lplsnew->mis.itemData = (DWORD)newstr; } lplsnew->mis.itemID = uIndex; lplsnew->hData = hStr; /* adjust the itemID field of the following entries */ for(lpls = lplsnew->lpNext; lpls != NULL; lpls = lpls->lpNext) { lpls->mis.itemID++; } if (lphl->needMeasure) { ListBoxAskMeasure(lphl, lplsnew); } dprintf_listbox(stddeb,"ListBoxInsertString // count=%d\n", lphl->ItemsCount); return uIndex; } int ListBoxAddString(LPHEADLIST lphl, SEGPTR itemData) { UINT pos = (UINT) -1; LPCSTR newstr = (lphl->HasStrings)?(LPCSTR)PTR_SEG_TO_LIN(itemData):(LPCSTR)itemData; if ( lphl->dwStyle & LBS_SORT ) pos = ListBoxAskCompare( lphl, -1, itemData, MATCH_NEAREST ); return ListBoxInsertString(lphl, pos, newstr); } int ListBoxGetText(LPHEADLIST lphl, UINT uIndex, LPSTR OutStr) { LPLISTSTRUCT lpls; if (!OutStr) { dprintf_listbox(stddeb, "ListBoxGetText // OutStr==NULL\n"); return 0; } *OutStr = '\0'; lpls = ListBoxGetItem (lphl, uIndex); if (lpls == NULL) return LB_ERR; if (!lphl->HasStrings) { *((long *)OutStr) = lpls->mis.itemData; return 4; } strcpy(OutStr, lpls->itemText); return strlen(OutStr); } DWORD ListBoxGetItemData(LPHEADLIST lphl, UINT uIndex) { LPLISTSTRUCT lpls; lpls = ListBoxGetItem (lphl, uIndex); if (lpls == NULL) return LB_ERR; return lpls->mis.itemData; } int ListBoxSetItemData(LPHEADLIST lphl, UINT uIndex, DWORD ItemData) { LPLISTSTRUCT lpls = ListBoxGetItem(lphl, uIndex); if (lpls == NULL) return LB_ERR; lpls->mis.itemData = ItemData; return 1; } int ListBoxDeleteString(LPHEADLIST lphl, UINT uIndex) { LPLISTSTRUCT lpls, lpls2; UINT Count; if (uIndex >= lphl->ItemsCount) return LB_ERR; lpls = lphl->lpFirst; if (lpls == NULL) return LB_ERR; if (uIndex == 0) { if( lphl->OwnerDrawn ) lbDeleteItemNotify( lphl, lpls); lphl->lpFirst = lpls->lpNext; } else { LPLISTSTRUCT lpls2 = NULL; for(Count = 0; Count < uIndex; Count++) { if (lpls->lpNext == NULL) return LB_ERR; lpls2 = lpls; lpls = (LPLISTSTRUCT)lpls->lpNext; } if( lphl->OwnerDrawn ) lbDeleteItemNotify( lphl, lpls); lpls2->lpNext = lpls->lpNext; } /* adjust the itemID field of the following entries */ for(lpls2 = lpls->lpNext; lpls2 != NULL; lpls2 = lpls2->lpNext) { lpls2->mis.itemID--; } lphl->ItemsCount--; if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData); free(lpls); return lphl->ItemsCount; } static int lbFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr, BOOL match) { /* match is either MATCH_SUBSTR or MATCH_EXACT */ LPLISTSTRUCT lpls; UINT Count; UINT First = nFirst + 1; int s_length = 0; LPSTR lpMatchStr = (LPSTR)MatchStr; if (First > lphl->ItemsCount) return LB_ERR; if (lphl->dwStyle & LBS_SORT ) return ListBoxAskCompare( lphl, nFirst, MatchStr, match ); if (lphl->HasStrings ) { lpMatchStr = PTR_SEG_TO_LIN(MatchStr); if( match == MATCH_SUBSTR ) { s_length = strlen(lpMatchStr); if( !s_length ) return (lphl->ItemsCount)?0:LB_ERR; } } lpls = ListBoxGetItem(lphl, First); Count = 0; while(lpls != NULL) { if (lphl->HasStrings) { if ( ( s_length )? !lstrncmpi32A(lpls->itemText, lpMatchStr, s_length) : !lstrcmpi32A(lpls->itemText, lpMatchStr) ) return Count; } else if ( lpls->mis.itemData == (DWORD)lpMatchStr ) return Count; lpls = lpls->lpNext; Count++; } /* Start over at top */ Count = 0; lpls = lphl->lpFirst; while (Count < First) { if (lphl->HasStrings) { if ( ( s_length )? !lstrncmpi32A(lpls->itemText, lpMatchStr, s_length) : !lstrcmpi32A(lpls->itemText, lpMatchStr) ) return Count; } else if ( lpls->mis.itemData == (DWORD)lpMatchStr ) return Count; lpls = lpls->lpNext; Count++; } return LB_ERR; } int ListBoxFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr) { return lbFindString(lphl, nFirst, MatchStr, MATCH_SUBSTR ); } int ListBoxFindStringExact(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr) { return lbFindString(lphl, nFirst, MatchStr, MATCH_EXACT ); } int ListBoxResetContent(LPHEADLIST lphl) { LPLISTSTRUCT lpls; int i; if (lphl->ItemsCount == 0) return 0; dprintf_listbox(stddeb, "ListBoxResetContent // ItemCount = %d\n", lphl->ItemsCount); for(i = 0; i < lphl->ItemsCount; i++) { lpls = lphl->lpFirst; if (lpls == NULL) return LB_ERR; if (lphl->OwnerDrawn) lbDeleteItemNotify(lphl, lpls); lphl->lpFirst = lpls->lpNext; if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData); free(lpls); } ListBoxInitialize(lphl); return TRUE; } /* --------------------- selection ------------------------- */ int ListBoxSetCurSel(LPHEADLIST lphl, WORD wIndex) { LPLISTSTRUCT lpls; /* use ListBoxSetSel instead */ if (lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ) return 0; /* unselect previous item */ if (lphl->ItemFocused != -1) { lphl->PrevFocused = lphl->ItemFocused; lpls = ListBoxGetItem(lphl, lphl->ItemFocused); if (lpls == 0) return LB_ERR; lpls->itemState = 0; } if ((wIndex != (UINT)-1) && (wIndex < lphl->ItemsCount)) { lphl->ItemFocused = wIndex; lpls = ListBoxGetItem(lphl, wIndex); if (lpls == 0) return LB_ERR; lpls->itemState = ODS_SELECTED | ODS_FOCUS; return 0; } return LB_ERR; } /* ------------------------- dir listing ------------------------ */ LONG ListBoxDirectory(LPHEADLIST lphl, UINT attrib, LPCSTR filespec) { char mask[13]; char* temp = NULL; const char* ptr; int skip, count; LONG ret; DOS_DIRENT entry; char *path, *p; dprintf_listbox(stddeb, "ListBoxDirectory: '%s' %04x\n", filespec, attrib); if (!filespec) return LB_ERR; if (!(ptr = DOSFS_GetUnixFileName( filespec, FALSE ))) return LB_ERR; path = xstrdup(ptr); p = strrchr( path, '/' ); *p++ = '\0'; if (!(ptr = DOSFS_ToDosFCBFormat( p )) || !(temp = SEGPTR_ALLOC( sizeof(char) * 16 )) ) { free( path ); return LB_ERR; } strcpy( mask, ptr ); dprintf_listbox(stddeb, "ListBoxDirectory: path=%s mask=%s\n", path, mask); skip = ret = 0; attrib &= ~FA_LABEL; while ((count = DOSFS_FindNext( path, mask, NULL, 0, attrib, skip, &entry )) > 0) { skip += count; if (entry.attr & FA_DIRECTORY) { if ((attrib & DDL_DIRECTORY) && strcmp(entry.name, ". ")) { sprintf(temp, "[%s]", DOSFS_ToDosDTAFormat( entry.name ) ); AnsiLower( temp ); if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break; } } else /* not a directory */ { if (!(attrib & DDL_EXCLUSIVE) || ((attrib & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE)) == (entry.attr & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE)))) { strcpy( temp, DOSFS_ToDosDTAFormat( entry.name ) ); AnsiLower( temp ); if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break; } } dprintf_listbox(stddeb,"\tn - %i, file '%s'\n", count, temp); } if (attrib & DDL_DRIVES) { int x; DWORD oldstyle = lphl->dwStyle; lphl->dwStyle &= ~LBS_SORT; strcpy( temp, "[-a-]" ); for (x = 0; x < MAX_DOS_DRIVES; x++, temp[2]++) { if (DRIVE_IsValid(x)) if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break; } lphl->dwStyle = oldstyle; } free( path ); SEGPTR_FREE( temp ); return ret; } /* ------------------------- dimensions ------------------------- */ int ListBoxGetItemRect(LPHEADLIST lphl, WORD wIndex, LPRECT16 lprect) { LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wIndex); dprintf_listbox(stddeb,"ListBox LB_GETITEMRECT %i %p", wIndex,lpls); if (lpls == NULL) { if (lphl->dwStyle & LBS_OWNERDRAWVARIABLE) return LB_ERR; else { GetClientRect16(lphl->hSelf,lprect); lprect->bottom=lphl->StdItemHeight; if (lprect->right<0) lprect->right=0; } } else *lprect = lpls->itemRect; dprintf_listbox(stddeb," = %d,%d %d,%d\n", lprect->left,lprect->top, lprect->right,lprect->bottom); return 0; } int ListBoxSetItemHeight(LPHEADLIST lphl, WORD wIndex, long height) { LPLISTSTRUCT lpls; if (!(lphl->dwStyle & LBS_OWNERDRAWVARIABLE)) { lphl->StdItemHeight = (short)height; return 0; } lpls = ListBoxGetItem(lphl, wIndex); if (lpls == NULL) return LB_ERR; lpls->mis.itemHeight = height; return 0; } /* -------------------------- string search ------------------------ */ int ListBoxFindNextMatch(LPHEADLIST lphl, WORD wChar) { LPLISTSTRUCT lpls; UINT count,first; if ((char)wChar < ' ') return LB_ERR; if (!lphl->HasStrings) return LB_ERR; lpls = lphl->lpFirst; for (count = 0; lpls != NULL; lpls = lpls->lpNext, count++) { if (tolower(*lpls->itemText) == tolower((char)wChar)) break; } if (lpls == NULL) return LB_ERR; first = count; for(; lpls != NULL; lpls = lpls->lpNext, count++) { if (*lpls->itemText != (char)wChar) break; if ((short) count > lphl->ItemFocused) return count; } return first; }