4 * Copyright Martin Ayotte, 1993
5 * Constantine Sapuntzakis, 1995
6 * Alex Korobka, 1995, 1996
12 * - proper scrolling for multicolumn style
13 * - anchor and caret for LBS_EXTENDEDSEL
14 * - proper selection with keyboard
15 * - how to handle (LBS_EXTENDEDSEL | LBS_MULTIPLESEL) style
16 * - support for LBS_NOINTEGRALHEIGHT and LBS_OWNERDRAWVARIABLE styles
36 #define LIST_HEAP_ALLOC(lphl,f,size) \
37 LOCAL_Alloc( lphl->HeapSel, LMEM_FIXED, (size) )
38 #define LIST_HEAP_FREE(lphl,handle) \
39 LOCAL_Free( lphl->HeapSel, (handle) )
40 #define LIST_HEAP_ADDR(lphl,handle) \
41 ((handle) ? PTR_SEG_OFF_TO_LIN(lphl->HeapSel, (handle)) : NULL)
43 #define LIST_HEAP_SIZE 0x10000
45 #define LBMM_EDGE 4 /* distance inside box which is same as moving mouse
46 outside box, to trigger scrolling of LB */
48 #define MATCH_SUBSTR 2
50 #define MATCH_NEAREST 0
52 static void ListBoxInitialize(LPHEADLIST lphl)
56 lphl->ItemsVisible = 0;
57 lphl->FirstVisible = 0;
58 lphl->ColumnsVisible = 1;
59 lphl->ItemsPerColumn = 0;
60 lphl->ItemFocused = -1;
61 lphl->PrevFocused = -1;
64 void CreateListBoxStruct(HWND hwnd, WORD CtlType, LONG styles, HWND parent)
69 lphl = (LPHEADLIST)xmalloc(sizeof(HEADLIST));
70 SetWindowLong32A(hwnd, 0, (LONG)lphl);
71 ListBoxInitialize(lphl);
72 lphl->DrawCtlType = CtlType;
73 lphl->CtlID = GetWindowWord(hwnd,GWW_ID);
74 lphl->bRedrawFlag = TRUE;
76 lphl->TabStops = NULL;
77 lphl->hFont = GetStockObject32(SYSTEM_FONT);
79 if (CtlType==ODT_COMBOBOX) /* use the "faked" style for COMBOLBOX */
80 /* LBS_SORT instead CBS_SORT e.g. */
81 lphl->dwStyle = MAKELONG(LOWORD(styles),HIWORD(GetWindowLong32A(hwnd,GWL_STYLE)));
83 lphl->dwStyle = GetWindowLong32A(hwnd,GWL_STYLE); /* use original style dword */
84 lphl->hParent = parent;
85 lphl->StdItemHeight = 15; /* FIXME: should get the font height */
86 lphl->OwnerDrawn = styles & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE);
87 lphl->HasStrings = (styles & LBS_HASSTRINGS) || !lphl->OwnerDrawn;
89 /* create dummy hdc to set text height */
90 if ((hdc = GetDC32(0)))
93 GetTextMetrics16( hdc, &tm );
94 lphl->StdItemHeight = tm.tmHeight;
95 dprintf_listbox(stddeb,"CreateListBoxStruct: font height %d\n",
97 ReleaseDC32( 0, hdc );
100 if (lphl->OwnerDrawn)
104 lphl->needMeasure = TRUE;
105 dummyls.mis.CtlType = lphl->DrawCtlType;
106 dummyls.mis.CtlID = lphl->CtlID;
107 dummyls.mis.itemID = -1;
108 dummyls.mis.itemWidth = 0; /* ignored */
109 dummyls.mis.itemData = 0;
111 ListBoxAskMeasure(lphl,&dummyls);
114 lphl->HeapSel = GlobalAlloc16(GMEM_FIXED,LIST_HEAP_SIZE);
115 LocalInit( lphl->HeapSel, 0, LIST_HEAP_SIZE-1);
118 void DestroyListBoxStruct(LPHEADLIST lphl)
120 /* XXX need to free lphl->Heap */
121 GlobalFree16(lphl->HeapSel);
125 static LPHEADLIST ListBoxGetStorageHeader(HWND hwnd)
127 return (LPHEADLIST)GetWindowLong32A(hwnd,0);
130 /* Send notification "code" as part of a WM_COMMAND-message if hwnd
131 has the LBS_NOTIFY style */
132 void ListBoxSendNotification(LPHEADLIST lphl, WORD code)
134 if (lphl->dwStyle & LBS_NOTIFY)
135 SendMessage32A( lphl->hParent, WM_COMMAND,
136 MAKEWPARAM( lphl->CtlID, code), (LPARAM)lphl->hSelf );
140 /* get the maximum value of lphl->FirstVisible */
141 int ListMaxFirstVisible(LPHEADLIST lphl)
143 int m = lphl->ItemsCount-lphl->ItemsVisible;
144 return (m < 0) ? 0 : m;
148 void ListBoxUpdateWindow(HWND hwnd, LPHEADLIST lphl, BOOL repaint)
150 if (lphl->dwStyle & WS_VSCROLL)
151 SetScrollRange32(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE);
152 if ((lphl->dwStyle & WS_HSCROLL) && (lphl->ItemsPerColumn != 0))
153 SetScrollRange32(hwnd, SB_HORZ, 1, lphl->ItemsVisible /
154 lphl->ItemsPerColumn + 1, TRUE);
156 if (repaint && lphl->bRedrawFlag) InvalidateRect32( hwnd, NULL, TRUE );
159 /* Returns: 0 if nothing needs to be changed */
160 /* 1 if FirstVisible changed */
162 int ListBoxScrollToFocus(LPHEADLIST lphl)
166 if (lphl->ItemsCount == 0) return 0;
167 if (lphl->ItemFocused == -1) return 0;
169 end = lphl->FirstVisible + lphl->ItemsVisible - 1;
171 if (lphl->ItemFocused < lphl->FirstVisible ) {
172 lphl->FirstVisible = lphl->ItemFocused;
175 if (lphl->ItemFocused > end) {
176 WORD maxFirstVisible = ListMaxFirstVisible(lphl);
178 lphl->FirstVisible = lphl->ItemFocused;
180 if (lphl->FirstVisible > maxFirstVisible) {
181 lphl->FirstVisible = maxFirstVisible;
190 LPLISTSTRUCT ListBoxGetItem(LPHEADLIST lphl, UINT uIndex)
195 if (uIndex >= lphl->ItemsCount) return NULL;
197 lpls = lphl->lpFirst;
198 while (Count++ < uIndex) lpls = lpls->lpNext;
203 void ListBoxDrawItem(HWND hwnd, LPHEADLIST lphl, HDC16 hdc, LPLISTSTRUCT lpls,
204 RECT16 *rect, WORD itemAction, WORD itemState)
206 if (lphl->OwnerDrawn)
208 DRAWITEMSTRUCT32 dis;
210 dis.CtlID = lpls->mis.CtlID;
211 dis.CtlType = lpls->mis.CtlType;
212 dis.itemID = lpls->mis.itemID;
215 dis.itemData = lpls->mis.itemData;
216 dis.itemAction = itemAction;
217 dis.itemState = itemState;
218 CONV_RECT16TO32( rect, &dis.rcItem );
219 SendMessage32A( lphl->hParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis );
222 if (itemAction == ODA_DRAWENTIRE || itemAction == ODA_SELECT) {
224 DWORD dwOldTextColor = 0;
226 OldBkMode = SetBkMode(hdc, TRANSPARENT);
228 if (itemState != 0) {
229 dwOldTextColor = SetTextColor(hdc, 0x00FFFFFFL);
230 FillRect16(hdc, rect, GetStockObject32(BLACK_BRUSH));
233 if (lphl->dwStyle & LBS_USETABSTOPS) {
234 TabbedTextOut(hdc, rect->left + 5, rect->top + 2,
235 (char *)lpls->itemText, strlen((char *)lpls->itemText),
236 lphl->iNumStops, lphl->TabStops, 0);
238 TextOut16(hdc, rect->left + 5, rect->top + 2,
239 (char *)lpls->itemText, strlen((char *)lpls->itemText));
242 if (itemState != 0) {
243 SetTextColor(hdc, dwOldTextColor);
246 SetBkMode(hdc, OldBkMode);
248 else DrawFocusRect16(hdc, rect);
252 int ListBoxFindMouse(LPHEADLIST lphl, int X, int Y)
254 LPLISTSTRUCT lpls = lphl->lpFirst;
258 point.x = X; point.y = Y;
259 if (lphl->ItemsCount == 0) return LB_ERR;
261 for(i = 0; i < lphl->FirstVisible; i++) {
262 if (lpls == NULL) return LB_ERR;
265 for(j = 0; j < lphl->ItemsVisible; i++, j++) {
266 if (lpls == NULL) return LB_ERR;
267 if (PtInRect16(&lpls->itemRect,point)) {
272 dprintf_listbox(stddeb,"ListBoxFindMouse: not found\n");
276 BOOL32 lbDeleteItemNotify(LPHEADLIST lphl, LPLISTSTRUCT lpls)
278 /* called only for owner drawn listboxes */
280 DELETEITEMSTRUCT16 *delItem = SEGPTR_NEW(DELETEITEMSTRUCT16);
281 if (!delItem) return FALSE;
283 delItem->CtlType = lphl->DrawCtlType;
284 delItem->CtlID = lphl->CtlID;
285 delItem->itemID = lpls->mis.itemID;
286 delItem->hwndItem = lphl->hSelf;
287 delItem->itemData = lpls->mis.itemData;
289 ret = SendMessage16( lphl->hParent, WM_DELETEITEM, (WPARAM16)lphl->CtlID,
290 (LPARAM)SEGPTR_GET(delItem) );
291 SEGPTR_FREE(delItem);
295 void ListBoxAskMeasure(LPHEADLIST lphl, LPLISTSTRUCT lpls)
297 MEASUREITEMSTRUCT16 *lpmeasure = SEGPTR_NEW(MEASUREITEMSTRUCT16);
298 if (!lpmeasure) return;
299 *lpmeasure = lpls->mis;
300 lpmeasure->itemHeight = lphl->StdItemHeight;
301 SendMessage16( lphl->hParent, WM_MEASUREITEM, lphl->CtlID,
302 (LPARAM)SEGPTR_GET(lpmeasure) );
304 if (lphl->dwStyle & LBS_OWNERDRAWFIXED)
306 if (lpmeasure->itemHeight > lphl->StdItemHeight)
307 lphl->StdItemHeight = lpmeasure->itemHeight;
308 lpls->mis.itemHeight = lpmeasure->itemHeight;
310 SEGPTR_FREE(lpmeasure);
313 /* -------------------- strings and item data ---------------------- */
315 LPLISTSTRUCT ListBoxCreateItem(LPHEADLIST lphl, int id)
317 LPLISTSTRUCT lplsnew = (LPLISTSTRUCT)malloc(sizeof(LISTSTRUCT));
319 if (lplsnew == NULL) return NULL;
321 lplsnew->itemState = 0;
322 lplsnew->mis.CtlType = lphl->DrawCtlType;
323 lplsnew->mis.CtlID = lphl->CtlID;
324 lplsnew->mis.itemID = id;
325 lplsnew->mis.itemHeight = lphl->StdItemHeight;
326 lplsnew->mis.itemWidth = 0; /* ignored */
327 lplsnew->mis.itemData = 0;
328 SetRectEmpty16( &lplsnew->itemRect );
333 int ListBoxAskCompare(LPHEADLIST lphl, int startItem, SEGPTR matchData, BOOL exactMatch )
335 /* Do binary search for sorted listboxes. Linked list item storage sort of
336 * defeats the purpose ( forces to traverse item list all the time ) but M$ does it this way...
338 * MATCH_NEAREST (0) - return position for insertion - for all styles
339 * MATCH_EXACT (1) - search for an item, return index or LB_ERR
340 * MATCH_SUBSTR (2) - same as exact match but with strncmp for string comparision
343 COMPAREITEMSTRUCT16 *itemCmp;
344 LPLISTSTRUCT currentItem = NULL;
345 LPCSTR matchStr = (lphl->HasStrings)?(LPCSTR)PTR_SEG_TO_LIN(matchData):NULL;
346 int head, pos = -1, tail, loop = 1;
347 short b = 0, s_length = 0;
351 if( !lphl->ItemsCount )
352 return (exactMatch)? LB_ERR: 0;
354 /* set up variables */
356 if( exactMatch == MATCH_NEAREST )
358 else if( ++startItem )
361 if( startItem >= lphl->ItemsCount ) startItem = lphl->ItemsCount - 1;
364 if( exactMatch == MATCH_SUBSTR && lphl->HasStrings )
366 s_length = strlen( matchStr );
367 if( !s_length ) return 0; /* head of the list - empty string */
370 head = startItem; tail = lphl->ItemsCount - 1;
372 dprintf_listbox(stddeb,"AskCompare: head = %i, tail = %i, data = %08x\n", head, tail, (unsigned)matchData );
374 if (!(itemCmp = SEGPTR_NEW(COMPAREITEMSTRUCT16))) return 0;
375 itemCmp->CtlType = lphl->DrawCtlType;
376 itemCmp->CtlID = lphl->CtlID;
377 itemCmp->hwndItem = lphl->hSelf;
379 /* search from startItem */
383 while( head <= tail )
385 pos = (tail + head)/2;
386 currentItem = ListBoxGetItem( lphl, pos );
388 if( lphl->HasStrings )
390 b = ( s_length )? lstrncmpi32A( currentItem->itemText, matchStr, s_length)
391 : lstrcmpi32A( currentItem->itemText, matchStr);
395 itemCmp->itemID1 = pos;
396 itemCmp->itemData1 = currentItem->mis.itemData;
397 itemCmp->itemID2 = -1;
398 itemCmp->itemData2 = matchData;
400 b = SendMessage16( lphl->hParent, WM_COMPAREITEM,
401 (WPARAM16)lphl->CtlID,
402 (LPARAM)SEGPTR_GET(itemCmp) );
407 SEGPTR_FREE(itemCmp);
408 return pos; /* found exact match */
411 if( b < 0 ) head = ++pos;
413 if( b > 0 ) tail = pos - 1;
416 /* reset to search from the first item */
417 head = 0; tail = startItem - 1;
420 dprintf_listbox(stddeb,"\t-> pos = %i\n", pos );
421 SEGPTR_FREE(itemCmp);
423 /* if we got here match is not exact */
425 if( pos < 0 ) pos = 0;
426 else if( pos > lphl->ItemsCount ) pos = lphl->ItemsCount;
428 return (exactMatch)? LB_ERR: pos;
431 int ListBoxInsertString(LPHEADLIST lphl, UINT uIndex, LPCSTR newstr)
433 LPLISTSTRUCT *lppls, lplsnew, lpls;
438 dprintf_listbox(stddeb,"ListBoxInsertString(%d, %p);\n", uIndex, newstr);
440 if (!newstr) return -1;
442 if (uIndex == (UINT)-1)
443 uIndex = lphl->ItemsCount;
445 lppls = &lphl->lpFirst;
446 for(Count = 0; Count < uIndex; Count++) {
447 if (*lppls == NULL) return LB_ERR;
448 lppls = (LPLISTSTRUCT *) &(*lppls)->lpNext;
451 lplsnew = ListBoxCreateItem(lphl, Count);
453 if (lplsnew == NULL) {
454 fprintf(stdnimp,"ListBoxInsertString() out of memory !\n");
458 lplsnew->lpNext = *lppls;
463 if (lphl->HasStrings) {
464 dprintf_listbox(stddeb," string: %s\n", newstr);
465 hStr = LIST_HEAP_ALLOC(lphl, LMEM_MOVEABLE, strlen(newstr) + 1);
466 str = (LPSTR)LIST_HEAP_ADDR(lphl, hStr);
467 if (str == NULL) return LB_ERRSPACE;
469 lplsnew->itemText = str;
470 /* I'm not so sure about the next one */
471 lplsnew->mis.itemData = 0;
473 lplsnew->itemText = NULL;
474 lplsnew->mis.itemData = (DWORD)newstr;
477 lplsnew->mis.itemID = uIndex;
478 lplsnew->hData = hStr;
480 /* adjust the itemID field of the following entries */
481 for(lpls = lplsnew->lpNext; lpls != NULL; lpls = lpls->lpNext) {
485 if (lphl->needMeasure) {
486 ListBoxAskMeasure(lphl, lplsnew);
489 dprintf_listbox(stddeb,"ListBoxInsertString // count=%d\n", lphl->ItemsCount);
494 int ListBoxAddString(LPHEADLIST lphl, SEGPTR itemData)
496 UINT pos = (UINT) -1;
497 LPCSTR newstr = (lphl->HasStrings)?(LPCSTR)PTR_SEG_TO_LIN(itemData):(LPCSTR)itemData;
499 if ( lphl->dwStyle & LBS_SORT )
500 pos = ListBoxAskCompare( lphl, -1, itemData, MATCH_NEAREST );
502 return ListBoxInsertString(lphl, pos, newstr);
506 int ListBoxGetText(LPHEADLIST lphl, UINT uIndex, LPSTR OutStr)
511 dprintf_listbox(stddeb, "ListBoxGetText // OutStr==NULL\n");
515 lpls = ListBoxGetItem (lphl, uIndex);
516 if (lpls == NULL) return LB_ERR;
518 if (!lphl->HasStrings) {
519 *((long *)OutStr) = lpls->mis.itemData;
523 strcpy(OutStr, lpls->itemText);
524 return strlen(OutStr);
528 DWORD ListBoxGetItemData(LPHEADLIST lphl, UINT uIndex)
532 lpls = ListBoxGetItem (lphl, uIndex);
533 if (lpls == NULL) return LB_ERR;
534 return lpls->mis.itemData;
538 int ListBoxSetItemData(LPHEADLIST lphl, UINT uIndex, DWORD ItemData)
540 LPLISTSTRUCT lpls = ListBoxGetItem(lphl, uIndex);
542 if (lpls == NULL) return LB_ERR;
543 lpls->mis.itemData = ItemData;
548 int ListBoxDeleteString(LPHEADLIST lphl, UINT uIndex)
550 LPLISTSTRUCT lpls, lpls2;
553 if (uIndex >= lphl->ItemsCount) return LB_ERR;
555 lpls = lphl->lpFirst;
556 if (lpls == NULL) return LB_ERR;
560 if( lphl->OwnerDrawn )
561 lbDeleteItemNotify( lphl, lpls);
562 lphl->lpFirst = lpls->lpNext;
566 LPLISTSTRUCT lpls2 = NULL;
567 for(Count = 0; Count < uIndex; Count++) {
568 if (lpls->lpNext == NULL) return LB_ERR;
571 lpls = (LPLISTSTRUCT)lpls->lpNext;
573 if( lphl->OwnerDrawn )
574 lbDeleteItemNotify( lphl, lpls);
575 lpls2->lpNext = lpls->lpNext;
578 /* adjust the itemID field of the following entries */
579 for(lpls2 = lpls->lpNext; lpls2 != NULL; lpls2 = lpls2->lpNext) {
585 if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData);
588 return lphl->ItemsCount;
591 int lbFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr, BOOL match)
593 /* match is either MATCH_SUBSTR or MATCH_EXACT */
597 UINT First = nFirst + 1;
599 LPSTR lpMatchStr = (LPSTR)MatchStr;
601 if (First > lphl->ItemsCount) return LB_ERR;
603 if (lphl->dwStyle & LBS_SORT )
604 return ListBoxAskCompare( lphl, nFirst, MatchStr, match );
606 if (lphl->HasStrings )
608 lpMatchStr = PTR_SEG_TO_LIN(MatchStr);
610 if( match == MATCH_SUBSTR )
612 s_length = strlen(lpMatchStr);
613 if( !s_length ) return (lphl->ItemsCount)?0:LB_ERR;
617 lpls = ListBoxGetItem(lphl, First);
621 if (lphl->HasStrings)
623 if ( ( s_length )? !lstrncmpi32A(lpls->itemText, lpMatchStr, s_length)
624 : !lstrcmpi32A(lpls->itemText, lpMatchStr) ) return Count;
627 if ( lpls->mis.itemData == (DWORD)lpMatchStr ) return Count;
633 /* Start over at top */
635 lpls = lphl->lpFirst;
637 while (Count < First)
639 if (lphl->HasStrings)
641 if ( ( s_length )? !lstrncmpi32A(lpls->itemText, lpMatchStr, s_length)
642 : !lstrcmpi32A(lpls->itemText, lpMatchStr) ) return Count;
645 if ( lpls->mis.itemData == (DWORD)lpMatchStr ) return Count;
654 int ListBoxFindString(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr)
656 return lbFindString(lphl, nFirst, MatchStr, MATCH_SUBSTR );
659 int ListBoxFindStringExact(LPHEADLIST lphl, UINT nFirst, SEGPTR MatchStr)
661 return lbFindString(lphl, nFirst, MatchStr, MATCH_EXACT );
664 int ListBoxResetContent(LPHEADLIST lphl)
669 if (lphl->ItemsCount == 0) return 0;
671 dprintf_listbox(stddeb, "ListBoxResetContent // ItemCount = %d\n",
674 for(i = 0; i < lphl->ItemsCount; i++) {
675 lpls = lphl->lpFirst;
676 if (lpls == NULL) return LB_ERR;
678 if (lphl->OwnerDrawn) lbDeleteItemNotify(lphl, lpls);
680 lphl->lpFirst = lpls->lpNext;
681 if (lpls->hData != 0) LIST_HEAP_FREE(lphl, lpls->hData);
684 ListBoxInitialize(lphl);
689 /* --------------------- selection ------------------------- */
691 int ListBoxSetCurSel(LPHEADLIST lphl, WORD wIndex)
695 /* use ListBoxSetSel instead */
696 if (lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ) return 0;
698 /* unselect previous item */
699 if (lphl->ItemFocused != -1) {
700 lphl->PrevFocused = lphl->ItemFocused;
701 lpls = ListBoxGetItem(lphl, lphl->ItemFocused);
702 if (lpls == 0) return LB_ERR;
706 if ((wIndex != (UINT)-1) && (wIndex < lphl->ItemsCount))
708 lphl->ItemFocused = wIndex;
709 lpls = ListBoxGetItem(lphl, wIndex);
710 if (lpls == 0) return LB_ERR;
711 lpls->itemState = ODS_SELECTED | ODS_FOCUS;
720 int ListBoxSetSel(LPHEADLIST lphl, WORD wIndex, WORD state)
725 if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ))
728 if (wIndex == (UINT)-1) {
729 for (lpls = lphl->lpFirst; lpls != NULL; lpls = lpls->lpNext) {
730 if( lpls->itemState & ODS_SELECTED) n++;
731 lpls->itemState = state? lpls->itemState | ODS_SELECTED
732 : lpls->itemState & ~ODS_SELECTED;
737 if (wIndex >= lphl->ItemsCount) return LB_ERR;
739 lpls = ListBoxGetItem(lphl, wIndex);
740 lpls->itemState = state? lpls->itemState | ODS_SELECTED
741 : lpls->itemState & ~ODS_SELECTED;
747 int ListBoxGetSel(LPHEADLIST lphl, WORD wIndex)
749 LPLISTSTRUCT lpls = ListBoxGetItem(lphl, wIndex);
751 if (lpls == NULL) return LB_ERR;
752 return lpls->itemState & ODS_SELECTED;
755 /* ------------------------- dir listing ------------------------ */
757 LONG ListBoxDirectory(LPHEADLIST lphl, UINT attrib, LPCSTR filespec)
767 dprintf_listbox(stddeb, "ListBoxDirectory: '%s' %04x\n", filespec, attrib);
768 if (!filespec) return LB_ERR;
769 if (!(ptr = DOSFS_GetUnixFileName( filespec, FALSE ))) return LB_ERR;
771 p = strrchr( path, '/' );
773 if (!(ptr = DOSFS_ToDosFCBFormat( p )) ||
774 !(temp = SEGPTR_ALLOC( sizeof(char) * 16 )) )
782 dprintf_listbox(stddeb, "ListBoxDirectory: path=%s mask=%s\n", path, mask);
786 while ((count = DOSFS_FindNext( path, mask, NULL, 0,
787 attrib, skip, &entry )) > 0)
790 if (entry.attr & FA_DIRECTORY)
792 if ((attrib & DDL_DIRECTORY) && strcmp(entry.name, ". "))
794 sprintf(temp, "[%s]", DOSFS_ToDosDTAFormat( entry.name ) );
796 if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break;
799 else /* not a directory */
801 if (!(attrib & DDL_EXCLUSIVE) ||
802 ((attrib & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE)) ==
803 (entry.attr & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE))))
805 strcpy( temp, DOSFS_ToDosDTAFormat( entry.name ) );
807 if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break;
811 dprintf_listbox(stddeb,"\tn - %i, file '%s'\n", count, temp);
813 if (attrib & DDL_DRIVES)
816 DWORD oldstyle = lphl->dwStyle;
818 lphl->dwStyle &= ~LBS_SORT;
819 strcpy( temp, "[-a-]" );
820 for (x = 0; x < MAX_DOS_DRIVES; x++, temp[2]++)
822 if (DRIVE_IsValid(x))
823 if ((ret = ListBoxAddString(lphl, SEGPTR_GET(temp))) == LB_ERR) break;
825 lphl->dwStyle = oldstyle;
834 /* ------------------------- dimensions ------------------------- */
836 int ListBoxGetItemRect(LPHEADLIST lphl, WORD wIndex, LPRECT16 lprect)
838 LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wIndex);
840 dprintf_listbox(stddeb,"ListBox LB_GETITEMRECT %i %p", wIndex,lpls);
843 if (lphl->dwStyle & LBS_OWNERDRAWVARIABLE)
847 GetClientRect16(lphl->hSelf,lprect);
848 lprect->bottom=lphl->StdItemHeight;
849 if (lprect->right<0) lprect->right=0;
853 *lprect = lpls->itemRect;
854 dprintf_listbox(stddeb," = %d,%d %d,%d\n", lprect->left,lprect->top,
855 lprect->right,lprect->bottom);
860 int ListBoxSetItemHeight(LPHEADLIST lphl, WORD wIndex, long height)
864 if (!(lphl->dwStyle & LBS_OWNERDRAWVARIABLE)) {
865 lphl->StdItemHeight = (short)height;
869 lpls = ListBoxGetItem(lphl, wIndex);
870 if (lpls == NULL) return LB_ERR;
872 lpls->mis.itemHeight = height;
876 /* -------------------------- string search ------------------------ */
878 int ListBoxFindNextMatch(LPHEADLIST lphl, WORD wChar)
883 if ((char)wChar < ' ') return LB_ERR;
884 if (!lphl->HasStrings) return LB_ERR;
886 lpls = lphl->lpFirst;
888 for (count = 0; lpls != NULL; lpls = lpls->lpNext, count++) {
889 if (tolower(*lpls->itemText) == tolower((char)wChar)) break;
891 if (lpls == NULL) return LB_ERR;
893 for(; lpls != NULL; lpls = lpls->lpNext, count++) {
894 if (*lpls->itemText != (char)wChar)
896 if ((short) count > lphl->ItemFocused)
902 /***********************************************************************
905 static LONG LBCreate(HWND hwnd, WORD wParam, LONG lParam)
908 LONG dwStyle = GetWindowLong32A(hwnd,GWL_STYLE);
911 CreateListBoxStruct(hwnd, ODT_LISTBOX, dwStyle, GetParent16(hwnd));
912 lphl = ListBoxGetStorageHeader(hwnd);
913 dprintf_listbox(stddeb,"ListBox created: lphl = %p dwStyle = %04x:%04x\n",
914 lphl, HIWORD(dwStyle), LOWORD(dwStyle));
916 GetClientRect16(hwnd,&rect);
917 lphl->ColumnsWidth = rect.right - rect.left;
919 if (dwStyle & WS_VSCROLL)
920 SetScrollRange32(hwnd, SB_VERT, 0, ListMaxFirstVisible(lphl), TRUE);
921 if (dwStyle & WS_HSCROLL)
922 SetScrollRange32(hwnd, SB_HORZ, 1, 1, TRUE);
928 /***********************************************************************
931 static LONG LBDestroy(HWND hwnd, WORD wParam, LONG lParam)
933 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
935 ListBoxResetContent(lphl);
937 DestroyListBoxStruct(lphl);
938 dprintf_listbox(stddeb,"ListBox destroyed: lphl = %p\n",lphl);
943 /***********************************************************************
946 static LONG LBNCCalcSize(HWND hwnd, WORD wParam, LONG lParam)
948 LONG ret = DefWindowProc16(hwnd, WM_NCCALCSIZE, wParam, lParam);
950 return (GetWindowLong32A(hwnd,GWL_STYLE) & LBS_MULTICOLUMN)? WVR_VREDRAW : ret;
954 /***********************************************************************
957 static LONG LBVScroll(HWND hwnd, WORD wParam, LONG lParam)
959 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
962 dprintf_listbox(stddeb,"ListBox WM_VSCROLL w=%04X l=%08lX !\n",
964 y = lphl->FirstVisible;
968 if (lphl->FirstVisible > 0)
969 lphl->FirstVisible--;
973 lphl->FirstVisible++;
977 if (lphl->FirstVisible > lphl->ItemsVisible) {
978 lphl->FirstVisible -= lphl->ItemsVisible;
980 lphl->FirstVisible = 0;
985 lphl->FirstVisible += lphl->ItemsVisible;
989 lphl->FirstVisible = LOWORD(lParam);
993 if (lphl->FirstVisible > ListMaxFirstVisible(lphl))
994 lphl->FirstVisible = ListMaxFirstVisible(lphl);
996 if (y != lphl->FirstVisible) {
997 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
998 InvalidateRect32( hwnd, NULL, TRUE );
1003 /***********************************************************************
1006 static LONG LBHScroll(HWND hwnd, WORD wParam, LONG lParam)
1011 dprintf_listbox(stddeb,"ListBox WM_HSCROLL w=%04X l=%08lX !\n",
1013 lphl = ListBoxGetStorageHeader(hwnd);
1014 y = lphl->FirstVisible;
1017 if (lphl->FirstVisible > lphl->ItemsPerColumn) {
1018 lphl->FirstVisible -= lphl->ItemsPerColumn;
1020 lphl->FirstVisible = 0;
1024 lphl->FirstVisible += lphl->ItemsPerColumn;
1027 if (lphl->ItemsPerColumn != 0) {
1028 int lbsub = lphl->ItemsVisible / lphl->ItemsPerColumn * lphl->ItemsPerColumn;
1029 if (lphl->FirstVisible > lbsub) {
1030 lphl->FirstVisible -= lbsub;
1032 lphl->FirstVisible = 0;
1037 if (lphl->ItemsPerColumn != 0)
1038 lphl->FirstVisible += lphl->ItemsVisible /
1039 lphl->ItemsPerColumn * lphl->ItemsPerColumn;
1042 lphl->FirstVisible = lphl->ItemsPerColumn * LOWORD(lParam);
1045 if (lphl->FirstVisible > ListMaxFirstVisible(lphl))
1046 lphl->FirstVisible = ListMaxFirstVisible(lphl);
1048 if (lphl->ItemsPerColumn != 0) {
1049 lphl->FirstVisible = lphl->FirstVisible /
1050 lphl->ItemsPerColumn * lphl->ItemsPerColumn + 1;
1051 if (y != lphl->FirstVisible) {
1052 SetScrollPos32(hwnd, SB_HORZ, lphl->FirstVisible /
1053 lphl->ItemsPerColumn + 1, TRUE);
1054 InvalidateRect32( hwnd, NULL, TRUE );
1060 /***********************************************************************
1063 static LONG LBLButtonDown(HWND hwnd, WORD wParam, LONG lParam)
1065 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1073 lphl->PrevFocused = lphl->ItemFocused;
1075 y = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam));
1077 if (y == -1) return 0;
1079 if (lphl->dwStyle & LBS_NOTIFY && y!= LB_ERR )
1080 if( SendMessage16(lphl->hParent, WM_LBTRACKPOINT, y, lParam) )
1084 switch( lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) )
1086 case LBS_MULTIPLESEL:
1087 lphl->ItemFocused = y;
1088 wRet = ListBoxGetSel(lphl, y);
1089 ListBoxSetSel(lphl, y, !wRet);
1091 case LBS_EXTENDEDSEL:
1092 /* should handle extended mode here and in kbd handler
1095 if ( lphl->PrevFocused != y && y!= LB_ERR)
1097 LPLISTSTRUCT lpls = ListBoxGetItem( lphl, lphl->ItemFocused = y );
1098 n = ListBoxSetSel(lphl,-1,FALSE);
1100 lpls->itemState = ODS_FOCUS | ODS_SELECTED;
1102 if( n > 1 && n != LB_ERR )
1103 InvalidateRect32( hwnd,NULL,TRUE );
1110 if( y!=lphl->ItemFocused )
1111 ListBoxSetCurSel(lphl, y);
1116 fprintf(stdnimp,"Listbox: LBS_MULTIPLESEL and LBS_EXTENDEDSEL are on!\n");
1120 /* invalidate changed items */
1121 if( lphl->dwStyle & LBS_MULTIPLESEL || y!=lphl->PrevFocused )
1123 ListBoxGetItemRect(lphl, y, &rectsel);
1124 InvalidateRect16( hwnd, &rectsel, TRUE );
1126 if( lphl->PrevFocused!=-1 && y!=lphl->PrevFocused )
1128 ListBoxGetItemRect(lphl, lphl->PrevFocused, &rectsel);
1129 InvalidateRect16( hwnd, &rectsel, TRUE );
1132 if (GetWindowLong32A(lphl->hSelf,GWL_EXSTYLE) & WS_EX_DRAGDETECT)
1133 if( DragDetect(lphl->hSelf,MAKEPOINT16(lParam)) )
1134 SendMessage16(lphl->hParent, WM_BEGINDRAG,0,0L);
1138 /***********************************************************************
1141 static LONG LBLButtonUp(HWND hwnd, WORD wParam, LONG lParam)
1143 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1145 if (GetCapture32() == hwnd) ReleaseCapture();
1147 if (lphl->PrevFocused != lphl->ItemFocused)
1148 ListBoxSendNotification(lphl, LBN_SELCHANGE);
1153 /***********************************************************************
1156 static LONG LBRButtonUp(HWND hwnd, WORD wParam, LONG lParam)
1158 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1160 SendMessage16(lphl->hParent, WM_COMMAND, GetWindowWord(hwnd,GWW_ID),
1161 MAKELONG(hwnd, LBN_DBLCLK));
1165 /***********************************************************************
1168 static LONG LBMouseMove(HWND hwnd, WORD wParam, LONG lParam)
1170 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1171 int y,redraw_prev = 0;
1173 RECT16 rect, rectsel; /* XXX Broken */
1175 dprintf_listbox(stddeb,"LBMouseMove %d %d\n",SLOWORD(lParam),SHIWORD(lParam));
1176 if ((wParam & MK_LBUTTON) != 0) {
1177 y = SHIWORD(lParam);
1178 if (y < LBMM_EDGE) {
1179 if (lphl->FirstVisible > 0) {
1180 lphl->FirstVisible--;
1181 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1182 InvalidateRect32( hwnd, NULL, TRUE );
1186 GetClientRect16(hwnd, &rect);
1187 if (y >= (rect.bottom-LBMM_EDGE)) {
1188 if (lphl->FirstVisible < ListMaxFirstVisible(lphl)) {
1189 lphl->FirstVisible++;
1190 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1191 InvalidateRect32( hwnd, NULL, TRUE );
1195 if ((y > 0) && (y < (rect.bottom - LBMM_EDGE))) {
1196 if ((y < rectsel.top) || (y > rectsel.bottom)) {
1197 iRet = ListBoxFindMouse(lphl, LOWORD(lParam), HIWORD(lParam));
1198 if (iRet == lphl->ItemFocused || iRet == -1) {
1201 if (lphl->dwStyle & LBS_MULTIPLESEL) {
1202 lphl->ItemFocused = iRet;
1203 ListBoxSendNotification(lphl, LBN_SELCHANGE);
1204 } else if ( lphl->dwStyle & LBS_EXTENDEDSEL )
1206 /* Fixme: extended selection mode */
1207 ListBoxSetSel( lphl, lphl->ItemFocused, 0);
1208 lphl->PrevFocused = lphl->ItemFocused;
1209 lphl->ItemFocused = iRet;
1210 ListBoxSetSel( lphl, iRet, TRUE);
1215 ListBoxSetCurSel(lphl, (WORD)iRet);
1218 if( lphl->PrevFocused!=-1 && redraw_prev )
1220 ListBoxGetItemRect(lphl, lphl->PrevFocused, &rectsel);
1221 InvalidateRect16( hwnd, &rectsel, TRUE );
1223 ListBoxGetItemRect(lphl, iRet, &rectsel);
1224 InvalidateRect16( hwnd, &rectsel, TRUE );
1232 /***********************************************************************
1235 * Doesn't yet handle properly VK_SHIFT with LB_EXTENDEDSEL
1237 static LONG LBKeyDown(HWND hwnd, WORD wParam, LONG lParam)
1239 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1240 WORD newFocused = 0xFFFF;
1243 ListBoxGetItemRect(lphl,lphl->ItemFocused,&rect);
1254 if ( lphl->dwStyle & LBS_WANTKEYBOARDINPUT )
1256 newFocused = (WORD)(INT)SendMessage16(lphl->hParent,WM_VKEYTOITEM,
1257 wParam,MAKELPARAM(lphl->ItemFocused,hwnd));
1258 if ( newFocused == 0xFFFE ) return 0L;
1260 if ( newFocused == 0xFFFF )
1262 newFocused = lphl->ItemFocused;
1271 newFocused = lphl->ItemsCount - 1;
1274 if (lphl->dwStyle & LBS_MULTICOLUMN) {
1275 if (newFocused >= lphl->ItemsPerColumn) {
1276 newFocused -= lphl->ItemsPerColumn;
1283 if (newFocused > 0) newFocused--;
1286 if (lphl->dwStyle & LBS_MULTICOLUMN)
1287 newFocused += lphl->ItemsPerColumn;
1293 if (newFocused > lphl->ItemsVisible)
1294 newFocused -= lphl->ItemsVisible;
1295 else newFocused = 0;
1298 newFocused += lphl->ItemsVisible;
1303 /* end of nested switch */
1307 if (lphl->dwStyle & LBS_MULTIPLESEL)
1309 WORD wRet = ListBoxGetSel(lphl, lphl->ItemFocused);
1310 ListBoxSetSel(lphl, lphl->ItemFocused, !wRet);
1314 /* chars are handled in LBChar */
1319 /* at this point newFocused is set up */
1321 if (newFocused >= lphl->ItemsCount)
1322 newFocused = lphl->ItemsCount - 1;
1324 if (!(lphl->dwStyle & LBS_MULTIPLESEL))
1326 ListBoxSetCurSel(lphl, newFocused);
1327 ListBoxSendNotification(lphl, LBN_SELCHANGE);
1330 lphl->ItemFocused = newFocused;
1332 if( ListBoxScrollToFocus(lphl) || (lphl->dwStyle &
1333 (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) )
1334 InvalidateRect32( hwnd, NULL, TRUE );
1337 InvalidateRect16( hwnd, &rect, TRUE );
1338 if( newFocused < 0x8000 )
1340 ListBoxGetItemRect(lphl, newFocused, &rect);
1341 InvalidateRect16( hwnd, &rect, TRUE );
1345 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1350 /***********************************************************************
1353 static LONG LBChar(HWND hwnd, WORD wParam, LONG lParam)
1355 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1356 WORD newFocused = 0xFFFF;
1358 if ( (lphl->dwStyle & LBS_WANTKEYBOARDINPUT) && !(lphl->HasStrings))
1360 newFocused = (WORD)(INT)SendMessage16(lphl->hParent,WM_CHARTOITEM,
1361 wParam,MAKELPARAM(lphl->ItemFocused,hwnd));
1362 if ( newFocused == 0xFFFE ) return 0L;
1365 if (newFocused == 0xFFFF )
1366 newFocused = ListBoxFindNextMatch(lphl, wParam);
1368 if (newFocused == (WORD)LB_ERR) return 0;
1370 if (newFocused >= lphl->ItemsCount)
1371 newFocused = lphl->ItemsCount - 1;
1373 if (!(lphl->dwStyle & LBS_MULTIPLESEL))
1375 ListBoxSetCurSel(lphl, newFocused);
1376 ListBoxSendNotification(lphl, LBN_SELCHANGE);
1379 lphl->ItemFocused = newFocused;
1380 ListBoxScrollToFocus(lphl);
1381 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1383 InvalidateRect32( hwnd, NULL, TRUE );
1388 /***********************************************************************
1391 static LONG LBSetRedraw(HWND hwnd, WORD wParam, LONG lParam)
1393 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1395 dprintf_listbox(stddeb,"ListBox WM_SETREDRAW hWnd=%04x w=%04x !\n",
1397 lphl->bRedrawFlag = wParam;
1402 /***********************************************************************
1405 static LONG LBSetFont(HWND hwnd, WPARAM16 wParam, LPARAM lParam)
1407 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1411 lphl->hFont = GetStockObject32(SYSTEM_FONT);
1413 lphl->hFont = (HFONT16)wParam;
1415 /* a new font means possible new text height */
1416 /* does this mean the height of each entry must be separately changed? */
1417 /* or are we guaranteed to get a LBSetFont before the first insert/add? */
1418 if ((hdc = GetDC32(0)))
1421 GetTextMetrics16( hdc, &tm );
1422 lphl->StdItemHeight = tm.tmHeight;
1423 dprintf_listbox(stddeb,"LBSetFont: new font %d with height %d\n",
1424 lphl->hFont, lphl->StdItemHeight);
1425 ReleaseDC32( 0, hdc );
1431 /***********************************************************************
1434 static LONG LBPaint(HWND hwnd, WORD wParam, LONG lParam)
1436 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1441 HDC16 hdc = BeginPaint16( hwnd, &ps );
1442 DC *dc = (DC *)GDI_GetObjPtr(hdc, DC_MAGIC);
1443 RECT16 rect, paintRect, scratchRect;
1444 int i, top, height, maxwidth, ipc;
1448 if (!IsWindowVisible(hwnd) || !lphl->bRedrawFlag) {
1449 EndPaint16(hwnd, &ps);
1453 GetRgnBox16(dc->w.hGCClipRgn,&paintRect);
1454 GetClientRect16(hwnd, &rect);
1455 IntersectRect16(&paintRect,&rect,&paintRect);
1457 hOldFont = SelectObject32(hdc, lphl->hFont);
1459 hBrush = (HBRUSH16)SendMessage32A( lphl->hParent, WM_CTLCOLORLISTBOX,
1460 (WPARAM32)hdc, (LPARAM)hwnd);
1461 if (hBrush == 0) hBrush = GetStockObject32(WHITE_BRUSH);
1463 FillRect16(hdc, &rect, hBrush);
1465 maxwidth = rect.right;
1466 if (lphl->dwStyle & LBS_MULTICOLUMN) {
1467 rect.right = lphl->ColumnsWidth;
1469 lpls = lphl->lpFirst;
1471 lphl->ItemsVisible = 0;
1472 lphl->ItemsPerColumn = ipc = 0;
1474 for(i = 0; i < lphl->ItemsCount; i++) {
1475 if (lpls == NULL) break;
1477 if (i >= lphl->FirstVisible) {
1478 height = lpls->mis.itemHeight;
1480 if (top > (rect.bottom-height+1)) {
1481 if (lphl->dwStyle & LBS_MULTICOLUMN) {
1482 lphl->ItemsPerColumn = MAX(lphl->ItemsPerColumn, ipc);
1485 rect.left += lphl->ColumnsWidth;
1486 rect.right += lphl->ColumnsWidth;
1487 if (rect.left > maxwidth) break;
1493 lpls->itemRect.top = top;
1494 lpls->itemRect.bottom = top + height;
1495 lpls->itemRect.left = rect.left;
1496 lpls->itemRect.right = rect.right;
1498 if( IntersectRect16(&scratchRect,&paintRect,&lpls->itemRect) )
1500 dprintf_listbox(stddeb,"LBPaint: drawing item: %d %d %d %d %d\n",
1501 rect.left,top,rect.right,top+height,lpls->itemState);
1503 if (lphl->OwnerDrawn && (lphl->ItemFocused == i) && GetFocus32() == hwnd)
1505 ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_FOCUS,
1506 lpls->itemState & ~ODS_FOCUS);
1507 ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE,
1508 lpls->itemState & ~ODS_FOCUS);
1509 ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_FOCUS, lpls->itemState);
1512 ListBoxDrawItem (hwnd, lphl, hdc, lpls, &lpls->itemRect, ODA_DRAWENTIRE,
1517 lphl->ItemsVisible++;
1521 lpls = lpls->lpNext;
1523 ListBoxUpdateWindow(hwnd,lphl,FALSE);
1524 SelectObject32(hdc,hOldFont);
1525 EndPaint16( hwnd, &ps );
1529 /***********************************************************************
1532 static LONG LBSetFocus(HWND hwnd, WORD wParam, LONG lParam)
1534 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1536 dprintf_listbox(stddeb,"ListBox WM_SETFOCUS for %04x\n",hwnd);
1537 if(!(lphl->dwStyle & LBS_MULTIPLESEL) )
1538 if( lphl->ItemsCount && lphl->ItemFocused != -1)
1540 HDC32 hDC = GetDC32(hwnd);
1541 HFONT32 hOldFont = SelectObject32(hDC, lphl->hFont);
1544 lpls = ListBoxGetItem(lphl,lphl->ItemFocused);
1545 lpls->itemState |= ODS_FOCUS;
1547 ListBoxDrawItem(hwnd,lphl,hDC,lpls,&lpls->itemRect, ODA_FOCUS, lpls->itemState);
1548 SelectObject32(hDC, hOldFont);
1549 ReleaseDC32(hwnd,hDC);
1552 ListBoxSendNotification(lphl, LBN_SETFOCUS);
1557 /***********************************************************************
1560 static LONG LBKillFocus(HWND hwnd, WORD wParam, LONG lParam)
1562 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1564 dprintf_listbox(stddeb,"ListBox WM_KILLFOCUS for %04x\n",hwnd);
1565 if (!(lphl->dwStyle & LBS_MULTIPLESEL))
1567 if( lphl->ItemsCount )
1568 if( lphl->ItemFocused != -1 )
1570 HDC32 hDC = GetDC32(hwnd);
1571 HFONT32 hOldFont = SelectObject32(hDC, lphl->hFont);
1574 lpls = ListBoxGetItem(lphl,lphl->ItemFocused);
1575 lpls->itemState &= ~ODS_FOCUS;
1577 ListBoxDrawItem(hwnd,lphl,hDC,lpls,&lpls->itemRect, ODA_FOCUS, lpls->itemState);
1578 SelectObject32(hDC, hOldFont);
1579 ReleaseDC32(hwnd,hDC);
1582 dprintf_listbox(stddeb,"LBKillFocus: no focused item!\n");
1585 InvalidateRect32( hwnd, NULL, TRUE );
1587 ListBoxSendNotification(lphl, LBN_KILLFOCUS);
1592 /***********************************************************************
1595 static LONG LBResetContent(HWND hwnd, WORD wParam, LONG lParam)
1597 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1599 dprintf_listbox(stddeb,"ListBox LB_RESETCONTENT !\n");
1600 ListBoxResetContent(lphl);
1601 ListBoxUpdateWindow(hwnd, lphl, TRUE);
1605 /***********************************************************************
1608 static LONG LBDir(HWND hwnd, WORD wParam, LONG lParam)
1611 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1612 dprintf_listbox(stddeb,"ListBox LB_DIR !\n");
1614 ret = ListBoxDirectory(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
1615 ListBoxUpdateWindow(hwnd, lphl, TRUE);
1619 /***********************************************************************
1622 static LONG LBAddString(HWND hwnd, WORD wParam, LONG lParam)
1625 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1627 wRet = ListBoxAddString(lphl, (SEGPTR)lParam);
1629 ListBoxUpdateWindow(hwnd,lphl,TRUE);
1633 /***********************************************************************
1636 static LONG LBGetText(HWND hwnd, WORD wParam, LONG lParam)
1639 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1641 dprintf_listbox(stddeb, "LB_GETTEXT wParam=%d\n",wParam);
1642 wRet = ListBoxGetText(lphl, wParam, (LPSTR)PTR_SEG_TO_LIN(lParam));
1647 /***********************************************************************
1650 static LONG LBInsertString(HWND hwnd, WORD wParam, LONG lParam)
1653 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1655 if (lphl->HasStrings)
1656 wRet = ListBoxInsertString(lphl, wParam, (LPCSTR)PTR_SEG_TO_LIN(lParam));
1658 wRet = ListBoxInsertString(lphl, wParam, (LPCSTR)lParam);
1660 ListBoxUpdateWindow(hwnd,lphl,TRUE);
1664 /***********************************************************************
1667 static LONG LBDeleteString(HWND hwnd, WORD wParam, LONG lParam)
1669 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1670 LONG lRet = ListBoxDeleteString(lphl,wParam);
1672 ListBoxUpdateWindow(hwnd,lphl,TRUE);
1676 /***********************************************************************
1679 static LONG LBFindString(HWND hwnd, WORD wParam, LONG lParam)
1681 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1682 return lbFindString(lphl, wParam, (SEGPTR)lParam, MATCH_SUBSTR);
1685 /***********************************************************************
1688 static LONG LBFindStringExact(HWND hwnd, WORD wParam, LONG lParam)
1690 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1691 return lbFindString(lphl, wParam, (SEGPTR)lParam, MATCH_EXACT);
1694 /***********************************************************************
1697 static LONG LBGetCaretIndex(HWND hwnd, WORD wParam, LONG lParam)
1699 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1700 return lphl->ItemFocused;
1703 /***********************************************************************
1706 static LONG LBGetCount(HWND hwnd, WORD wParam, LONG lParam)
1710 lphl = ListBoxGetStorageHeader(hwnd);
1711 return lphl->ItemsCount;
1714 /***********************************************************************
1717 static LONG LBGetCurSel(HWND hwnd, WORD wParam, LONG lParam)
1721 lphl = ListBoxGetStorageHeader(hwnd);
1722 dprintf_listbox(stddeb,"ListBox LB_GETCURSEL %i !\n",
1724 return lphl->ItemFocused;
1727 /***********************************************************************
1728 * LBGetHorizontalExtent
1730 static LONG LBGetHorizontalExtent(HWND hwnd, WORD wParam, LONG lParam)
1735 /***********************************************************************
1738 static LONG LBGetItemHeight(HWND hwnd, WORD wParam, LONG lParam)
1740 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1741 LPLISTSTRUCT lpls = ListBoxGetItem (lphl, wParam);
1743 if (lpls == NULL) return LB_ERR;
1744 return lpls->mis.itemHeight;
1747 /***********************************************************************
1750 static LONG LBGetItemRect(HWND hwnd, WORD wParam, LONG lParam)
1752 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1753 return ListBoxGetItemRect(lphl, wParam, PTR_SEG_TO_LIN(lParam));
1756 /***********************************************************************
1759 static LONG LBGetSel(HWND hwnd, WORD wParam, LONG lParam)
1761 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1762 int iSel = ListBoxGetSel(lphl, wParam);
1764 dprintf_listbox(stdnimp,"LBGetSel: item %u - %i\n",wParam,iSel);
1766 return (iSel)? 1 : 0;
1769 /***********************************************************************
1772 static LONG LBGetSelCount(HWND hwnd, WORD wParam, LONG lParam)
1774 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1779 if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ))
1782 for( lpls = lphl->lpFirst;
1784 lpls = lpls->lpNext )
1787 if (lpls->itemState )
1794 /***********************************************************************
1797 static LONG LBGetSelItems(HWND hwnd, WORD wParam, LONG lParam)
1799 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1802 int *lpItems = PTR_SEG_TO_LIN(lParam);
1804 if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ))
1807 if (wParam == 0) return 0;
1809 lpls = lphl->lpFirst;
1812 while (lpls != NULL) {
1813 if (lpls->itemState > 0) lpItems[cnt++] = idx;
1815 if (cnt == wParam) break;
1817 lpls = lpls->lpNext;
1823 /***********************************************************************
1826 static LONG LBGetTextLen(HWND hwnd, WORD wParam, LONG lParam)
1828 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1829 LPLISTSTRUCT lpls = ListBoxGetItem(lphl,wParam);
1831 if (lpls == NULL || !lphl->HasStrings) return LB_ERR;
1832 return strlen(lpls->itemText);
1835 /***********************************************************************
1838 static LONG LBGetDlgCode(HWND hwnd, WORD wParam, LONG lParam)
1840 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1843 /***********************************************************************
1846 static LONG LBGetTopIndex(HWND hwnd, WORD wParam, LONG lParam)
1848 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1850 return lphl->FirstVisible;
1854 /***********************************************************************
1857 static LONG LBSelectString(HWND hwnd, WORD wParam, LONG lParam)
1859 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1862 iRet = lbFindString(lphl, wParam, (SEGPTR)lParam, MATCH_SUBSTR);
1866 if( lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) )
1867 ListBoxSetSel(lphl,iRet,TRUE);
1869 ListBoxSetCurSel(lphl,iRet);
1871 lphl->ItemFocused = iRet;
1872 InvalidateRect32( hwnd, 0, TRUE );
1877 /***********************************************************************
1880 static LONG LBSelItemRange(HWND hwnd, WORD wParam, LONG lParam)
1882 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1885 WORD first = LOWORD(lParam);
1886 WORD last = HIWORD(lParam);
1887 BOOL select = wParam;
1889 if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) ))
1892 if (first >= lphl->ItemsCount ||
1893 last >= lphl->ItemsCount) return LB_ERR;
1895 lpls = lphl->lpFirst;
1898 while (lpls != NULL) {
1900 lpls->itemState = select ? lpls->itemState | ODS_SELECTED : 0;
1905 lpls = lpls->lpNext;
1911 /***********************************************************************
1914 static LONG LBSetCaretIndex(HWND hwnd, WORD wParam, LONG lParam)
1916 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1919 if (!(lphl->dwStyle & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL) )) return 0;
1921 dprintf_listbox(stddeb,"LBSetCaretIndex: hwnd %04x n=%i\n",hwnd,wParam);
1923 if (wParam >= lphl->ItemsCount) return LB_ERR;
1925 lphl->ItemFocused = wParam;
1926 i = ListBoxScrollToFocus (lphl);
1928 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
1930 InvalidateRect32( hwnd, NULL, TRUE );
1935 /***********************************************************************
1938 static LONG LBSetColumnWidth(HWND hwnd, WORD wParam, LONG lParam)
1940 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1941 lphl->ColumnsWidth = wParam;
1942 InvalidateRect32( hwnd, NULL, TRUE );
1946 /***********************************************************************
1947 * LBSetHorizontalExtent
1949 static LONG LBSetHorizontalExtent(HWND hwnd, WORD wParam, LONG lParam)
1954 /***********************************************************************
1957 static LONG LBGetItemData(HWND hwnd, WORD wParam, LONG lParam)
1959 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1960 dprintf_listbox(stddeb, "LB_GETITEMDATA wParam=%x\n", wParam);
1961 return ListBoxGetItemData(lphl, wParam);
1964 /***********************************************************************
1967 static LONG LBSetItemData(HWND hwnd, WORD wParam, LONG lParam)
1969 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
1970 dprintf_listbox(stddeb, "LB_SETITEMDATA wParam=%x lParam=%lx\n", wParam, lParam);
1971 return ListBoxSetItemData(lphl, wParam, lParam);
1974 /***********************************************************************
1977 static LONG LBSetTabStops(HWND hwnd, WORD wParam, LONG lParam)
1981 lphl = ListBoxGetStorageHeader(hwnd);
1983 if (lphl->TabStops != NULL) {
1984 lphl->iNumStops = 0;
1985 free (lphl->TabStops);
1988 lphl->TabStops = malloc (wParam * sizeof (short));
1989 if (lphl->TabStops) {
1990 lphl->iNumStops = wParam;
1991 memcpy (lphl->TabStops, PTR_SEG_TO_LIN(lParam), wParam * sizeof (short));
1998 /***********************************************************************
2001 static LONG LBSetCurSel(HWND hwnd, WORD wParam, LONG lParam)
2003 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
2006 dprintf_listbox(stddeb,"ListBox LB_SETCURSEL wParam=%x !\n",
2009 wRet = ListBoxSetCurSel(lphl, wParam);
2011 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
2012 InvalidateRect32( hwnd, NULL, TRUE );
2017 /***********************************************************************
2020 static LONG LBSetSel(HWND hwnd, WORD wParam, LONG lParam)
2022 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
2026 dprintf_listbox(stddeb,"ListBox LB_SETSEL wParam=%x lParam=%lX !\n", wParam, lParam);
2028 iRet = ListBoxSetSel(lphl, LOWORD(lParam), wParam);
2030 if( iRet > 1 ) InvalidateRect32( hwnd, NULL, TRUE );
2031 else if( iRet != LB_ERR )
2033 if( lphl->dwStyle & LBS_EXTENDEDSEL &&
2034 lphl->ItemFocused != LOWORD(lParam) )
2036 ListBoxGetItemRect(lphl, lphl->ItemFocused , &rect);
2037 InvalidateRect16( hwnd, &rect, TRUE );
2038 lphl->ItemFocused = LOWORD(lParam);
2040 ListBoxGetItemRect(lphl,LOWORD(lParam),&rect);
2041 InvalidateRect16( hwnd, &rect, TRUE );
2044 return (iRet == (WORD)LB_ERR)? LB_ERR: 0;
2047 /***********************************************************************
2050 static LONG LBSetTopIndex(HWND hwnd, WORD wParam, LONG lParam)
2052 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
2054 dprintf_listbox(stddeb,"ListBox LB_SETTOPINDEX wParam=%x !\n",
2056 lphl->FirstVisible = wParam;
2057 SetScrollPos32(hwnd, SB_VERT, lphl->FirstVisible, TRUE);
2059 InvalidateRect32( hwnd, NULL, TRUE );
2064 /***********************************************************************
2067 static LONG LBSetItemHeight(HWND hwnd, WORD wParam, LONG lParam)
2069 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
2072 dprintf_listbox(stddeb,"ListBox LB_SETITEMHEIGHT wParam=%x lParam=%lX !\n", wParam, lParam);
2073 wRet = ListBoxSetItemHeight(lphl, wParam, lParam);
2074 InvalidateRect32( hwnd, NULL, TRUE );
2078 /***********************************************************************
2081 static LRESULT LBPassToParent(HWND hwnd, UINT message, WPARAM16 wParam, LPARAM lParam)
2083 WND* ptrWnd = WIN_FindWndPtr(hwnd);
2086 if( /* !(ptrWnd->dwExStyle & WS_EX_NOPARENTNOTIFY) && */
2088 return SendMessage16(ptrWnd->parent->hwndSelf,message,wParam,lParam);
2092 /***********************************************************************
2095 LRESULT ListBoxWndProc(HWND hwnd, UINT message, WPARAM16 wParam, LPARAM lParam)
2098 case WM_CREATE: return LBCreate(hwnd, wParam, lParam);
2099 case WM_DESTROY: return LBDestroy(hwnd, wParam, lParam);
2100 case WM_GETDLGCODE: return LBGetDlgCode(hwnd, wParam, lParam);
2101 case WM_VSCROLL: return LBVScroll(hwnd, wParam, lParam);
2102 case WM_HSCROLL: return LBHScroll(hwnd, wParam, lParam);
2103 case WM_LBUTTONDOWN: return LBLButtonDown(hwnd, wParam, lParam);
2104 case WM_LBUTTONUP: return LBLButtonUp(hwnd, wParam, lParam);
2105 case WM_RBUTTONUP: return LBRButtonUp(hwnd, wParam, lParam);
2106 case WM_LBUTTONDBLCLK: return LBRButtonUp(hwnd, wParam, lParam);
2107 case WM_MOUSEMOVE: return LBMouseMove(hwnd, wParam, lParam);
2108 case WM_KEYDOWN: return LBKeyDown(hwnd, wParam, lParam);
2109 case WM_CHAR: return LBChar(hwnd, wParam, lParam);
2110 case WM_SETFONT: return LBSetFont(hwnd, wParam, lParam);
2111 case WM_SETREDRAW: return LBSetRedraw(hwnd, wParam, lParam);
2112 case WM_PAINT: return LBPaint(hwnd, wParam, lParam);
2113 case WM_SETFOCUS: return LBSetFocus(hwnd, wParam, lParam);
2114 case WM_KILLFOCUS: return LBKillFocus(hwnd, wParam, lParam);
2115 case WM_NCCALCSIZE: return LBNCCalcSize(hwnd, wParam, lParam);
2116 case LB_RESETCONTENT16: return LBResetContent(hwnd, wParam, lParam);
2117 case LB_DIR16: return LBDir(hwnd, wParam, lParam);
2118 case LB_ADDSTRING16: return LBAddString(hwnd, wParam, lParam);
2119 case LB_INSERTSTRING16: return LBInsertString(hwnd, wParam, lParam);
2120 case LB_DELETESTRING16: return LBDeleteString(hwnd, wParam, lParam);
2121 case LB_FINDSTRING16: return LBFindString(hwnd, wParam, lParam);
2122 case LB_FINDSTRINGEXACT16: return LBFindStringExact(hwnd, wParam, lParam);
2123 case LB_GETCARETINDEX16: return LBGetCaretIndex(hwnd, wParam, lParam);
2124 case LB_GETCOUNT16: return LBGetCount(hwnd, wParam, lParam);
2125 case LB_GETCURSEL16: return LBGetCurSel(hwnd, wParam, lParam);
2126 case LB_GETHORIZONTALEXTENT16: return LBGetHorizontalExtent(hwnd, wParam, lParam);
2127 case LB_GETITEMDATA16: return LBGetItemData(hwnd, wParam, lParam);
2128 case LB_GETITEMHEIGHT16: return LBGetItemHeight(hwnd, wParam, lParam);
2129 case LB_GETITEMRECT16: return LBGetItemRect(hwnd, wParam, lParam);
2130 case LB_GETSEL16: return LBGetSel(hwnd, wParam, lParam);
2131 case LB_GETSELCOUNT16: return LBGetSelCount(hwnd, wParam, lParam);
2132 case LB_GETSELITEMS16: return LBGetSelItems(hwnd, wParam, lParam);
2133 case LB_GETTEXT16: return LBGetText(hwnd, wParam, lParam);
2134 case LB_GETTEXTLEN16: return LBGetTextLen(hwnd, wParam, lParam);
2135 case LB_GETTOPINDEX16: return LBGetTopIndex(hwnd, wParam, lParam);
2136 case LB_SELECTSTRING16: return LBSelectString(hwnd, wParam, lParam);
2137 case LB_SELITEMRANGE16: return LBSelItemRange(hwnd, wParam, lParam);
2138 case LB_SETCARETINDEX16: return LBSetCaretIndex(hwnd, wParam, lParam);
2139 case LB_SETCOLUMNWIDTH16: return LBSetColumnWidth(hwnd, wParam, lParam);
2140 case LB_SETHORIZONTALEXTENT16: return LBSetHorizontalExtent(hwnd, wParam, lParam);
2141 case LB_SETITEMDATA16: return LBSetItemData(hwnd, wParam, lParam);
2142 case LB_SETTABSTOPS16: return LBSetTabStops(hwnd, wParam, lParam);
2143 case LB_SETCURSEL16: return LBSetCurSel(hwnd, wParam, lParam);
2144 case LB_SETSEL16: return LBSetSel(hwnd, wParam, lParam);
2145 case LB_SETTOPINDEX16: return LBSetTopIndex(hwnd, wParam, lParam);
2146 case LB_SETITEMHEIGHT16: return LBSetItemHeight(hwnd, wParam, lParam);
2148 case WM_DROPFILES: return LBPassToParent(hwnd, message, wParam, lParam);
2150 /* these will have to be implemented for proper LBS_EXTENDEDSEL -
2152 * anchor item is an item that with caret (focused) item defines a
2153 * range of currently selected items when listbox is in the extended
2156 case LB_SETANCHORINDEX16: return LB_SETANCHORINDEX16; /* that's what Windows returns */
2157 case LB_GETANCHORINDEX16: return 0;
2160 case WM_QUERYDROPOBJECT:
2164 LPDRAGINFO lpDragInfo = (LPDRAGINFO) PTR_SEG_TO_LIN((SEGPTR)lParam);
2165 LPHEADLIST lphl = ListBoxGetStorageHeader(hwnd);
2167 lpDragInfo->l = ListBoxFindMouse(lphl,lpDragInfo->pt.x,
2170 return LBPassToParent(hwnd, message, wParam, lParam);
2174 return DefWindowProc16(hwnd, message, wParam, lParam);
2178 /**********************************************************************
2179 * DlgDirList (USER.100)
2181 INT DlgDirList( HWND hDlg, SEGPTR spec, INT idLBox, INT idStatic, UINT attrib )
2183 char *filespec = (char *)PTR_SEG_TO_LIN( spec );
2187 #define SENDMSG(msg,wparam,lparam) \
2188 ((attrib & DDL_POSTMSGS) ? PostMessage( hwnd, msg, wparam, lparam ) \
2189 : SendMessage16( hwnd, msg, wparam, lparam ))
2191 dprintf_listbox( stddeb, "DlgDirList: %04x '%s' %d %d %04x\n",
2192 hDlg, filespec ? filespec : "NULL",
2193 idLBox, idStatic, attrib );
2195 if (filespec && filespec[0] && (filespec[1] == ':'))
2197 drive = toupper( filespec[0] ) - 'A';
2199 if (!DRIVE_SetCurrentDrive( drive )) return FALSE;
2201 else drive = DRIVE_GetCurrentDrive();
2203 if (idLBox && ((hwnd = GetDlgItem( hDlg, idLBox )) != 0))
2207 if (!filespec || !filespec[0]) strcpy( mask, "*.*" );
2210 /* If the path exists and is a directory, chdir to it */
2211 if (DRIVE_Chdir( drive, filespec )) strcpy( mask, "*.*" );
2216 if ((p2 = strrchr( p, '\\' ))) p = p2 + 1;
2217 if ((p2 = strrchr( p, '/' ))) p = p2 + 1;
2218 lstrcpyn32A( mask, p, sizeof(mask) );
2222 if (!DRIVE_Chdir( drive, filespec )) return FALSE;
2227 strcpy( (char *)PTR_SEG_TO_LIN(spec), mask );
2229 dprintf_listbox(stddeb, "ListBoxDirectory: path=%c:\\%s mask=%s\n",
2230 'A' + drive, DRIVE_GetDosCwd(drive), mask);
2232 SENDMSG( LB_RESETCONTENT16, 0, 0 );
2233 if ((attrib & DDL_DIRECTORY) && !(attrib & DDL_EXCLUSIVE))
2236 if (SENDMSG( LB_DIR16, attrib & ~(DDL_DIRECTORY | DDL_DRIVES),
2237 (LPARAM)spec ) == LB_ERR) return FALSE;
2238 if (!(temp = SEGPTR_ALLOC( 4*sizeof(char) ))) return FALSE;
2239 strcpy( temp, "*.*" );
2240 /* FIXME: this won't work with PostMessage(), as temp will */
2241 /* have been freed by the time we do a DispatchMessage(). */
2242 if (SENDMSG( LB_DIR16, (attrib & (DDL_DIRECTORY | DDL_DRIVES)) | DDL_EXCLUSIVE,
2243 (LPARAM)SEGPTR_GET(temp) ) == LB_ERR)
2252 if (SENDMSG( LB_DIR16, attrib, (LPARAM)spec) == LB_ERR) return FALSE;
2256 if (idStatic && ((hwnd = GetDlgItem( hDlg, idStatic )) != 0))
2259 int drive = DRIVE_GetCurrentDrive();
2260 strcpy( temp, "A:\\" );
2262 lstrcpyn32A( temp + 3, DRIVE_GetDosCwd(drive), sizeof(temp)-3 );
2264 /* Can't use PostMessage() here, because the string is on the stack */
2265 SetDlgItemText32A( hDlg, idStatic, temp );