Population of oledlg.
[wine/hacks.git] / controls / listbox.c
blob736922ad2e9593e26ba6729228366ffb0f97d567
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <string.h>
8 #include <stdlib.h>
9 #include "wine/winuser16.h"
10 #include "winuser.h"
11 #include "winerror.h"
12 #include "drive.h"
13 #include "heap.h"
14 #include "spy.h"
15 #include "win.h"
16 #include "combo.h"
17 #include "debug.h"
18 #include "tweak.h"
20 /* Unimplemented yet:
21 * - LBS_NOSEL
22 * - LBS_USETABSTOPS
23 * - Unicode
24 * - Locale handling
27 /* Items array granularity */
28 #define LB_ARRAY_GRANULARITY 16
30 /* Scrolling timeout in ms */
31 #define LB_SCROLL_TIMEOUT 50
33 /* Listbox system timer id */
34 #define LB_TIMER_ID 2
36 /* Item structure */
37 typedef struct
39 LPSTR str; /* Item text */
40 BOOL selected; /* Is item selected? */
41 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
42 DWORD data; /* User data */
43 } LB_ITEMDATA;
45 /* Listbox structure */
46 typedef struct
48 HANDLE heap; /* Heap for this listbox */
49 HWND owner; /* Owner window to send notifications to */
50 UINT style; /* Window style */
51 INT width; /* Window width */
52 INT height; /* Window height */
53 LB_ITEMDATA *items; /* Array of items */
54 INT nb_items; /* Number of items */
55 INT top_item; /* Top visible item */
56 INT selected_item; /* Selected item */
57 INT focus_item; /* Item that has the focus */
58 INT anchor_item; /* Anchor item for extended selection */
59 INT item_height; /* Default item height */
60 INT page_size; /* Items per listbox page */
61 INT column_width; /* Column width for multi-column listboxes */
62 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
63 INT horz_pos; /* Horizontal position */
64 INT nb_tabs; /* Number of tabs in array */
65 INT *tabs; /* Array of tabs */
66 BOOL caret_on; /* Is caret on? */
67 HFONT font; /* Current font */
68 LCID locale; /* Current locale for string comparisons */
69 LPHEADCOMBO lphc; /* ComboLBox */
70 } LB_DESCR;
73 #define IS_OWNERDRAW(descr) \
74 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
76 #define HAS_STRINGS(descr) \
77 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
79 #define SEND_NOTIFICATION(wnd,descr,code) \
80 (SendMessageA( (descr)->owner, WM_COMMAND, \
81 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
83 /* Current timer status */
84 typedef enum
86 LB_TIMER_NONE,
87 LB_TIMER_UP,
88 LB_TIMER_LEFT,
89 LB_TIMER_DOWN,
90 LB_TIMER_RIGHT
91 } TIMER_DIRECTION;
93 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
96 /***********************************************************************
97 * LISTBOX_Dump
99 void LISTBOX_Dump( WND *wnd )
101 INT i;
102 LB_ITEMDATA *item;
103 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
105 DUMP( "Listbox:\n" );
106 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
107 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
108 descr->top_item );
109 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
111 DUMP( "%4d: %-40s %d %08lx %3d\n",
112 i, item->str, item->selected, item->data, item->height );
117 /***********************************************************************
118 * LISTBOX_GetCurrentPageSize
120 * Return the current page size
122 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
124 INT i, height;
125 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
126 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
128 if ((height += descr->items[i].height) > descr->height) break;
130 if (i == descr->top_item) return 1;
131 else return i - descr->top_item;
135 /***********************************************************************
136 * LISTBOX_GetMaxTopIndex
138 * Return the maximum possible index for the top of the listbox.
140 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
142 INT max, page;
144 if (descr->style & LBS_OWNERDRAWVARIABLE)
146 page = descr->height;
147 for (max = descr->nb_items - 1; max >= 0; max--)
148 if ((page -= descr->items[max].height) < 0) break;
149 if (max < descr->nb_items - 1) max++;
151 else if (descr->style & LBS_MULTICOLUMN)
153 if ((page = descr->width / descr->column_width) < 1) page = 1;
154 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
155 max = (max - page) * descr->page_size;
157 else
159 max = descr->nb_items - descr->page_size;
161 if (max < 0) max = 0;
162 return max;
166 /***********************************************************************
167 * LISTBOX_UpdateScroll
169 * Update the scrollbars. Should be called whenever the content
170 * of the listbox changes.
172 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
174 SCROLLINFO info;
176 if (!(descr->style & WS_VSCROLL)) return;
177 /* It is important that we check descr->style, and not wnd->dwStyle,
178 for WS_VSCROLL, as the former is exactly the one passed in
179 argument to CreateWindow.
180 In Windows (and from now on in Wine :) a listbox created
181 with such a style (no WS_SCROLL) does not update
182 the scrollbar with listbox-related data, thus letting
183 the programmer use it for his/her own purposes. */
185 if (descr->style & LBS_NOREDRAW) return;
186 info.cbSize = sizeof(info);
188 if (descr->style & LBS_MULTICOLUMN)
190 info.nMin = 0;
191 info.nMax = (descr->nb_items - 1) / descr->page_size;
192 info.nPos = descr->top_item / descr->page_size;
193 info.nPage = descr->width / descr->column_width;
194 if (info.nPage < 1) info.nPage = 1;
195 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
196 if (descr->style & LBS_DISABLENOSCROLL)
197 info.fMask |= SIF_DISABLENOSCROLL;
198 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
199 info.nMax = 0;
200 info.fMask = SIF_RANGE;
201 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
203 else
205 info.nMin = 0;
206 info.nMax = descr->nb_items - 1;
207 info.nPos = descr->top_item;
208 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
209 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
210 if (descr->style & LBS_DISABLENOSCROLL)
211 info.fMask |= SIF_DISABLENOSCROLL;
212 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
214 if (descr->horz_extent)
216 info.nMin = 0;
217 info.nMax = descr->horz_extent - 1;
218 info.nPos = descr->horz_pos;
219 info.nPage = descr->width;
220 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
221 if (descr->style & LBS_DISABLENOSCROLL)
222 info.fMask |= SIF_DISABLENOSCROLL;
223 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
229 /***********************************************************************
230 * LISTBOX_SetTopItem
232 * Set the top item of the listbox, scrolling up or down if necessary.
234 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
235 BOOL scroll )
237 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
238 if (index > max) index = max;
239 if (index < 0) index = 0;
240 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
241 if (descr->top_item == index) return LB_OKAY;
242 if (descr->style & LBS_MULTICOLUMN)
244 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
245 if (scroll && (abs(diff) < descr->width))
246 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
247 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
249 else
250 scroll = FALSE;
252 else if (scroll)
254 INT diff;
255 if (descr->style & LBS_OWNERDRAWVARIABLE)
257 INT i;
258 diff = 0;
259 if (index > descr->top_item)
261 for (i = index - 1; i >= descr->top_item; i--)
262 diff -= descr->items[i].height;
264 else
266 for (i = index; i < descr->top_item; i++)
267 diff += descr->items[i].height;
270 else
271 diff = (descr->top_item - index) * descr->item_height;
273 if (abs(diff) < descr->height)
274 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
275 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
276 else
277 scroll = FALSE;
279 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
280 descr->top_item = index;
281 LISTBOX_UpdateScroll( wnd, descr );
282 return LB_OKAY;
286 /***********************************************************************
287 * LISTBOX_UpdatePage
289 * Update the page size. Should be called when the size of
290 * the client area or the item height changes.
292 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
294 INT page_size;
296 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
297 if (page_size == descr->page_size) return;
298 descr->page_size = page_size;
299 if (descr->style & LBS_MULTICOLUMN)
300 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
301 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
305 /***********************************************************************
306 * LISTBOX_UpdateSize
308 * Update the size of the listbox. Should be called when the size of
309 * the client area changes.
311 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
313 RECT rect;
315 GetClientRect( wnd->hwndSelf, &rect );
316 descr->width = rect.right - rect.left;
317 descr->height = rect.bottom - rect.top;
318 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
320 if ((descr->height > descr->item_height) &&
321 (descr->height % descr->item_height))
323 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
324 wnd->hwndSelf, descr->height,
325 descr->height - descr->height%descr->item_height );
326 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
327 wnd->rectWindow.right - wnd->rectWindow.left,
328 wnd->rectWindow.bottom - wnd->rectWindow.top -
329 (descr->height % descr->item_height),
330 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
331 return;
334 TRACE(listbox, "[%04x]: new size = %d,%d\n",
335 wnd->hwndSelf, descr->width, descr->height );
336 LISTBOX_UpdatePage( wnd, descr );
337 LISTBOX_UpdateScroll( wnd, descr );
341 /***********************************************************************
342 * LISTBOX_GetItemRect
344 * Get the rectangle enclosing an item, in listbox client coordinates.
345 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
347 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
348 RECT *rect )
350 /* Index <= 0 is legal even on empty listboxes */
351 if (index && (index >= descr->nb_items)) return -1;
352 SetRect( rect, 0, 0, descr->width, descr->height );
353 if (descr->style & LBS_MULTICOLUMN)
355 INT col = (index / descr->page_size) -
356 (descr->top_item / descr->page_size);
357 rect->left += col * descr->column_width;
358 rect->right = rect->left + descr->column_width;
359 rect->top += (index % descr->page_size) * descr->item_height;
360 rect->bottom = rect->top + descr->item_height;
362 else if (descr->style & LBS_OWNERDRAWVARIABLE)
364 INT i;
365 rect->right += descr->horz_pos;
366 if ((index >= 0) && (index < descr->nb_items))
368 if (index < descr->top_item)
370 for (i = descr->top_item-1; i >= index; i--)
371 rect->top -= descr->items[i].height;
373 else
375 for (i = descr->top_item; i < index; i++)
376 rect->top += descr->items[i].height;
378 rect->bottom = rect->top + descr->items[index].height;
382 else
384 rect->top += (index - descr->top_item) * descr->item_height;
385 rect->bottom = rect->top + descr->item_height;
386 rect->right += descr->horz_pos;
389 return ((rect->left < descr->width) && (rect->right > 0) &&
390 (rect->top < descr->height) && (rect->bottom > 0));
394 /***********************************************************************
395 * LISTBOX_GetItemFromPoint
397 * Return the item nearest from point (x,y) (in client coordinates).
399 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
400 INT x, INT y )
402 INT index = descr->top_item;
404 if (!descr->nb_items) return -1; /* No items */
405 if (descr->style & LBS_OWNERDRAWVARIABLE)
407 INT pos = 0;
408 if (y >= 0)
410 while (index < descr->nb_items)
412 if ((pos += descr->items[index].height) > y) break;
413 index++;
416 else
418 while (index > 0)
420 index--;
421 if ((pos -= descr->items[index].height) <= y) break;
425 else if (descr->style & LBS_MULTICOLUMN)
427 if (y >= descr->item_height * descr->page_size) return -1;
428 if (y >= 0) index += y / descr->item_height;
429 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
430 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
432 else
434 index += (y / descr->item_height);
436 if (index < 0) return 0;
437 if (index >= descr->nb_items) return -1;
438 return index;
442 /***********************************************************************
443 * LISTBOX_PaintItem
445 * Paint an item.
447 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
448 const RECT *rect, INT index, UINT action )
450 LB_ITEMDATA *item = NULL;
451 if (index < descr->nb_items) item = &descr->items[index];
453 if (IS_OWNERDRAW(descr))
455 DRAWITEMSTRUCT dis;
456 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
458 if (!item)
460 if (action == ODA_FOCUS)
461 DrawFocusRect( hdc, rect );
462 else
463 FIXME(listbox,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
464 return;
466 dis.CtlType = ODT_LISTBOX;
467 dis.CtlID = id;
468 dis.hwndItem = wnd->hwndSelf;
469 dis.itemAction = action;
470 dis.hDC = hdc;
471 dis.itemID = index;
472 dis.itemState = 0;
473 if (item && item->selected) dis.itemState |= ODS_SELECTED;
474 if ((descr->focus_item == index) &&
475 (descr->caret_on) &&
476 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
477 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
478 dis.itemData = item ? item->data : 0;
479 dis.rcItem = *rect;
480 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
481 "state=%02x rect=%d,%d-%d,%d\n",
482 wnd->hwndSelf, index, item ? item->str : "", action,
483 dis.itemState, rect->left, rect->top,
484 rect->right, rect->bottom );
485 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
487 else
489 COLORREF oldText = 0, oldBk = 0;
491 if (action == ODA_FOCUS)
493 DrawFocusRect( hdc, rect );
494 return;
496 if (item && item->selected)
498 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
499 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
502 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
503 "rect=%d,%d-%d,%d\n",
504 wnd->hwndSelf, index, item ? item->str : "", action,
505 rect->left, rect->top, rect->right, rect->bottom );
506 if (!item)
507 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
508 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
509 else if (!(descr->style & LBS_USETABSTOPS))
510 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
511 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
512 strlen(item->str), NULL );
513 else
515 /* Output empty string to paint background in the full width. */
516 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
517 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
518 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
519 item->str, strlen(item->str),
520 descr->nb_tabs, descr->tabs, 0);
522 if (item && item->selected)
524 SetBkColor( hdc, oldBk );
525 SetTextColor( hdc, oldText );
527 if ((descr->focus_item == index) &&
528 (descr->caret_on) &&
529 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
534 /***********************************************************************
535 * LISTBOX_SetRedraw
537 * Change the redraw flag.
539 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
541 if (on)
543 if (!(descr->style & LBS_NOREDRAW)) return;
544 descr->style &= ~LBS_NOREDRAW;
545 LISTBOX_UpdateScroll( wnd, descr );
547 else descr->style |= LBS_NOREDRAW;
551 /***********************************************************************
552 * LISTBOX_RepaintItem
554 * Repaint a single item synchronously.
556 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
557 UINT action )
559 HDC hdc;
560 RECT rect;
561 HFONT oldFont = 0;
562 HBRUSH hbrush, oldBrush = 0;
564 if (descr->style & LBS_NOREDRAW) return;
565 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
566 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
567 if (descr->font) oldFont = SelectObject( hdc, descr->font );
568 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
569 hdc, (LPARAM)wnd->hwndSelf );
570 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
571 if (wnd->dwStyle & WS_DISABLED)
572 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
573 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
574 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
575 if (oldFont) SelectObject( hdc, oldFont );
576 if (oldBrush) SelectObject( hdc, oldBrush );
577 ReleaseDC( wnd->hwndSelf, hdc );
581 /***********************************************************************
582 * LISTBOX_InitStorage
584 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
585 DWORD bytes )
587 LB_ITEMDATA *item;
589 nb_items += LB_ARRAY_GRANULARITY - 1;
590 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
591 if (descr->items)
592 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
593 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
594 nb_items * sizeof(LB_ITEMDATA) )))
596 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
597 return LB_ERRSPACE;
599 descr->items = item;
600 return LB_OKAY;
604 /***********************************************************************
605 * LISTBOX_SetTabStops
607 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
608 LPINT tabs, BOOL short_ints )
610 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
611 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
612 if (!(descr->nb_tabs = count))
614 descr->tabs = NULL;
615 return TRUE;
617 /* FIXME: count = 1 */
618 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
619 descr->nb_tabs * sizeof(INT) )))
620 return FALSE;
621 if (short_ints)
623 INT i;
624 LPINT16 p = (LPINT16)tabs;
625 dbg_decl_str(listbox, 256);
627 for (i = 0; i < descr->nb_tabs; i++) {
628 descr->tabs[i] = *p++<<1; /* FIXME */
629 if(TRACE_ON(listbox))
630 dsprintf(listbox, "%hd ", descr->tabs[i]);
632 TRACE(listbox, "[%04x]: settabstops %s\n",
633 wnd->hwndSelf, dbg_str(listbox));
635 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
636 /* FIXME: repaint the window? */
637 return TRUE;
641 /***********************************************************************
642 * LISTBOX_GetText
644 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
645 LPSTR buffer )
647 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
648 if (HAS_STRINGS(descr))
650 if (!buffer)
651 return strlen(descr->items[index].str);
652 lstrcpyA( buffer, descr->items[index].str );
653 return strlen(buffer);
654 } else {
655 if (buffer)
656 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
657 return sizeof(DWORD);
662 /***********************************************************************
663 * LISTBOX_FindStringPos
665 * Find the nearest string located before a given string in sort order.
666 * If 'exact' is TRUE, return an error if we don't get an exact match.
668 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
669 BOOL exact )
671 INT index, min, max, res = -1;
673 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
674 min = 0;
675 max = descr->nb_items;
676 while (min != max)
678 index = (min + max) / 2;
679 if (HAS_STRINGS(descr))
680 res = lstrcmpiA( descr->items[index].str, str );
681 else
683 COMPAREITEMSTRUCT cis;
684 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
686 cis.CtlType = ODT_LISTBOX;
687 cis.CtlID = id;
688 cis.hwndItem = wnd->hwndSelf;
689 cis.itemID1 = index;
690 cis.itemData1 = descr->items[index].data;
691 cis.itemID2 = -1;
692 cis.itemData2 = (DWORD)str;
693 cis.dwLocaleId = descr->locale;
694 res = SendMessageA( descr->owner, WM_COMPAREITEM,
695 id, (LPARAM)&cis );
697 if (!res) return index;
698 if (res > 0) max = index;
699 else min = index + 1;
701 return exact ? -1 : max;
705 /***********************************************************************
706 * LISTBOX_FindFileStrPos
708 * Find the nearest string located before a given string in directory
709 * sort order (i.e. first files, then directories, then drives).
711 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
713 INT min, max, res = -1;
715 if (!HAS_STRINGS(descr))
716 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
717 min = 0;
718 max = descr->nb_items;
719 while (min != max)
721 INT index = (min + max) / 2;
722 const char *p = descr->items[index].str;
723 if (*p == '[') /* drive or directory */
725 if (*str != '[') res = -1;
726 else if (p[1] == '-') /* drive */
728 if (str[1] == '-') res = str[2] - p[2];
729 else res = -1;
731 else /* directory */
733 if (str[1] == '-') res = 1;
734 else res = lstrcmpiA( str, p );
737 else /* filename */
739 if (*str == '[') res = 1;
740 else res = lstrcmpiA( str, p );
742 if (!res) return index;
743 if (res < 0) max = index;
744 else min = index + 1;
746 return max;
750 /***********************************************************************
751 * LISTBOX_FindString
753 * Find the item beginning with a given string.
755 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
756 LPCSTR str, BOOL exact )
758 INT i;
759 LB_ITEMDATA *item;
761 if (start >= descr->nb_items) start = -1;
762 item = descr->items + start + 1;
763 if (HAS_STRINGS(descr))
765 if (!str) return LB_ERR;
766 if (exact)
768 for (i = start + 1; i < descr->nb_items; i++, item++)
769 if (!lstrcmpiA( str, item->str )) return i;
770 for (i = 0, item = descr->items; i <= start; i++, item++)
771 if (!lstrcmpiA( str, item->str )) return i;
773 else
775 /* Special case for drives and directories: ignore prefix */
776 #define CHECK_DRIVE(item) \
777 if ((item)->str[0] == '[') \
779 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
780 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
781 return i; \
784 INT len = strlen(str);
785 for (i = start + 1; i < descr->nb_items; i++, item++)
787 if (!lstrncmpiA( str, item->str, len )) return i;
788 CHECK_DRIVE(item);
790 for (i = 0, item = descr->items; i <= start; i++, item++)
792 if (!lstrncmpiA( str, item->str, len )) return i;
793 CHECK_DRIVE(item);
795 #undef CHECK_DRIVE
798 else
800 if (exact && (descr->style & LBS_SORT))
801 /* If sorted, use a WM_COMPAREITEM binary search */
802 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
804 /* Otherwise use a linear search */
805 for (i = start + 1; i < descr->nb_items; i++, item++)
806 if (item->data == (DWORD)str) return i;
807 for (i = 0, item = descr->items; i <= start; i++, item++)
808 if (item->data == (DWORD)str) return i;
810 return LB_ERR;
814 /***********************************************************************
815 * LISTBOX_GetSelCount
817 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
819 INT i, count;
820 LB_ITEMDATA *item = descr->items;
822 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
823 for (i = count = 0; i < descr->nb_items; i++, item++)
824 if (item->selected) count++;
825 return count;
829 /***********************************************************************
830 * LISTBOX_GetSelItems16
832 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
833 LPINT16 array )
835 INT i, count;
836 LB_ITEMDATA *item = descr->items;
838 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
839 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
840 if (item->selected) array[count++] = (INT16)i;
841 return count;
845 /***********************************************************************
846 * LISTBOX_GetSelItems32
848 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
849 LPINT array )
851 INT i, count;
852 LB_ITEMDATA *item = descr->items;
854 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
855 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
856 if (item->selected) array[count++] = i;
857 return count;
861 /***********************************************************************
862 * LISTBOX_Paint
864 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
866 INT i, col_pos = descr->page_size - 1;
867 RECT rect;
868 HFONT oldFont = 0;
869 HBRUSH hbrush, oldBrush = 0;
871 SetRect( &rect, 0, 0, descr->width, descr->height );
872 if (descr->style & LBS_NOREDRAW) return 0;
873 if (descr->style & LBS_MULTICOLUMN)
874 rect.right = rect.left + descr->column_width;
875 else if (descr->horz_pos)
877 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
878 rect.right += descr->horz_pos;
881 if (descr->font) oldFont = SelectObject( hdc, descr->font );
882 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
883 hdc, (LPARAM)wnd->hwndSelf );
884 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
885 if (wnd->dwStyle & WS_DISABLED)
886 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
888 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
889 (GetFocus() == wnd->hwndSelf))
891 /* Special case for empty listbox: paint focus rect */
892 rect.bottom = rect.top + descr->item_height;
893 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
894 ODA_FOCUS );
895 rect.top = rect.bottom;
898 for (i = descr->top_item; i < descr->nb_items; i++)
900 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
901 rect.bottom = rect.top + descr->item_height;
902 else
903 rect.bottom = rect.top + descr->items[i].height;
905 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
906 rect.top = rect.bottom;
908 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
910 if (!IS_OWNERDRAW(descr))
912 /* Clear the bottom of the column */
913 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
914 if (rect.top < descr->height)
916 rect.bottom = descr->height;
917 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
918 &rect, NULL, 0, NULL );
922 /* Go to the next column */
923 rect.left += descr->column_width;
924 rect.right += descr->column_width;
925 rect.top = 0;
926 col_pos = descr->page_size - 1;
928 else
930 col_pos--;
931 if (rect.top >= descr->height) break;
935 if (!IS_OWNERDRAW(descr))
937 /* Clear the remainder of the client area */
938 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
939 if (rect.top < descr->height)
941 rect.bottom = descr->height;
942 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
943 &rect, NULL, 0, NULL );
945 if (rect.right < descr->width)
947 rect.left = rect.right;
948 rect.right = descr->width;
949 rect.top = 0;
950 rect.bottom = descr->height;
951 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
952 &rect, NULL, 0, NULL );
955 if (oldFont) SelectObject( hdc, oldFont );
956 if (oldBrush) SelectObject( hdc, oldBrush );
957 return 0;
961 /***********************************************************************
962 * LISTBOX_InvalidateItems
964 * Invalidate all items from a given item. If the specified item is not
965 * visible, nothing happens.
967 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
969 RECT rect;
971 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
973 rect.bottom = descr->height;
974 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
975 if (descr->style & LBS_MULTICOLUMN)
977 /* Repaint the other columns */
978 rect.left = rect.right;
979 rect.right = descr->width;
980 rect.top = 0;
981 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
987 /***********************************************************************
988 * LISTBOX_GetItemHeight
990 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
992 if (descr->style & LBS_OWNERDRAWVARIABLE)
994 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
995 return descr->items[index].height;
997 else return descr->item_height;
1001 /***********************************************************************
1002 * LISTBOX_SetItemHeight
1004 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1005 UINT height )
1007 if (!height) height = 1;
1009 if (descr->style & LBS_OWNERDRAWVARIABLE)
1011 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1012 TRACE(listbox, "[%04x]: item %d height = %d\n",
1013 wnd->hwndSelf, index, height );
1014 descr->items[index].height = height;
1015 LISTBOX_UpdateScroll( wnd, descr );
1016 LISTBOX_InvalidateItems( wnd, descr, index );
1018 else if (height != descr->item_height)
1020 TRACE(listbox, "[%04x]: new height = %d\n",
1021 wnd->hwndSelf, height );
1022 descr->item_height = height;
1023 LISTBOX_UpdatePage( wnd, descr );
1024 LISTBOX_UpdateScroll( wnd, descr );
1025 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1027 return LB_OKAY;
1031 /***********************************************************************
1032 * LISTBOX_SetHorizontalPos
1034 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1036 INT diff;
1038 if (pos > descr->horz_extent - descr->width)
1039 pos = descr->horz_extent - descr->width;
1040 if (pos < 0) pos = 0;
1041 if (!(diff = descr->horz_pos - pos)) return;
1042 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1043 wnd->hwndSelf, pos );
1044 descr->horz_pos = pos;
1045 LISTBOX_UpdateScroll( wnd, descr );
1046 if (abs(diff) < descr->width)
1047 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1048 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1049 else
1050 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1054 /***********************************************************************
1055 * LISTBOX_SetHorizontalExtent
1057 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1058 UINT extent )
1060 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1061 return LB_OKAY;
1062 if (extent <= 0) extent = 1;
1063 if (extent == descr->horz_extent) return LB_OKAY;
1064 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1065 wnd->hwndSelf, extent );
1066 descr->horz_extent = extent;
1067 if (descr->horz_pos > extent - descr->width)
1068 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1069 else
1070 LISTBOX_UpdateScroll( wnd, descr );
1071 return LB_OKAY;
1075 /***********************************************************************
1076 * LISTBOX_SetColumnWidth
1078 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1080 width += 2; /* For left and right margin */
1081 if (width == descr->column_width) return LB_OKAY;
1082 TRACE(listbox, "[%04x]: new column width = %d\n",
1083 wnd->hwndSelf, width );
1084 descr->column_width = width;
1085 LISTBOX_UpdatePage( wnd, descr );
1086 return LB_OKAY;
1090 /***********************************************************************
1091 * LISTBOX_SetFont
1093 * Returns the item height.
1095 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1097 HDC hdc;
1098 HFONT oldFont = 0;
1099 TEXTMETRICA tm;
1101 descr->font = font;
1103 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1105 ERR(listbox, "unable to get DC.\n" );
1106 return 16;
1108 if (font) oldFont = SelectObject( hdc, font );
1109 GetTextMetricsA( hdc, &tm );
1110 if (oldFont) SelectObject( hdc, oldFont );
1111 ReleaseDC( wnd->hwndSelf, hdc );
1112 if (!IS_OWNERDRAW(descr))
1113 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1114 return tm.tmHeight ;
1118 /***********************************************************************
1119 * LISTBOX_MakeItemVisible
1121 * Make sure that a given item is partially or fully visible.
1123 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1124 BOOL fully )
1126 INT top;
1128 if (index <= descr->top_item) top = index;
1129 else if (descr->style & LBS_MULTICOLUMN)
1131 INT cols = descr->width;
1132 if (!fully) cols += descr->column_width - 1;
1133 if (cols >= descr->column_width) cols /= descr->column_width;
1134 else cols = 1;
1135 if (index < descr->top_item + (descr->page_size * cols)) return;
1136 top = index - descr->page_size * (cols - 1);
1138 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1140 INT height = fully ? descr->items[index].height : 1;
1141 for (top = index; top > descr->top_item; top--)
1142 if ((height += descr->items[top-1].height) > descr->height) break;
1144 else
1146 if (index < descr->top_item + descr->page_size) return;
1147 if (!fully && (index == descr->top_item + descr->page_size) &&
1148 (descr->height > (descr->page_size * descr->item_height))) return;
1149 top = index - descr->page_size + 1;
1151 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1155 /***********************************************************************
1156 * LISTBOX_SelectItemRange
1158 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1160 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1161 INT last, BOOL on )
1163 INT i;
1165 /* A few sanity checks */
1167 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1168 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1169 if (last == -1) last = descr->nb_items - 1;
1170 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1171 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1172 /* selected_item reflects last selected/unselected item on multiple sel */
1173 descr->selected_item = last;
1175 if (on) /* Turn selection on */
1177 for (i = first; i <= last; i++)
1179 if (descr->items[i].selected) continue;
1180 descr->items[i].selected = TRUE;
1181 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1184 else /* Turn selection off */
1186 for (i = first; i <= last; i++)
1188 if (!descr->items[i].selected) continue;
1189 descr->items[i].selected = FALSE;
1190 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1193 return LB_OKAY;
1197 /***********************************************************************
1198 * LISTBOX_SetCaretIndex
1200 * NOTES
1201 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1204 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1205 BOOL fully_visible )
1207 INT oldfocus = descr->focus_item;
1209 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1210 if (index == oldfocus) return LB_OKAY;
1211 descr->focus_item = index;
1212 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1213 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1215 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1216 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1217 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1219 return LB_OKAY;
1223 /***********************************************************************
1224 * LISTBOX_SetSelection
1226 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1227 BOOL on, BOOL send_notify )
1229 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1230 if (descr->style & LBS_MULTIPLESEL)
1232 if (index == -1) /* Select all items */
1233 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1234 else /* Only one item */
1235 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1237 else
1239 INT oldsel = descr->selected_item;
1240 if (index == oldsel) return LB_OKAY;
1241 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1242 if (index != -1) descr->items[index].selected = TRUE;
1243 descr->selected_item = index;
1244 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1245 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1246 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1247 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1248 else
1249 if( descr->lphc ) /* set selection change flag for parent combo */
1250 descr->lphc->wState |= CBF_SELCHANGE;
1252 return LB_OKAY;
1256 /***********************************************************************
1257 * LISTBOX_MoveCaret
1259 * Change the caret position and extend the selection to the new caret.
1261 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1262 BOOL fully_visible )
1264 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1265 if (descr->style & LBS_EXTENDEDSEL)
1267 if (descr->anchor_item != -1)
1269 INT first = MIN( descr->focus_item, descr->anchor_item );
1270 INT last = MAX( descr->focus_item, descr->anchor_item );
1271 if (first > 0)
1272 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1273 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1274 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1277 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1279 /* Set selection to new caret item */
1280 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1285 /***********************************************************************
1286 * LISTBOX_InsertItem
1288 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1289 LPSTR str, DWORD data )
1291 LB_ITEMDATA *item;
1292 INT max_items;
1294 if (index == -1) index = descr->nb_items;
1295 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1296 if (!descr->items) max_items = 0;
1297 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1298 if (descr->nb_items == max_items)
1300 /* We need to grow the array */
1301 max_items += LB_ARRAY_GRANULARITY;
1302 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1303 max_items * sizeof(LB_ITEMDATA) )))
1305 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1306 return LB_ERRSPACE;
1308 descr->items = item;
1311 /* Insert the item structure */
1313 item = &descr->items[index];
1314 if (index < descr->nb_items)
1315 RtlMoveMemory( item + 1, item,
1316 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1317 item->str = str;
1318 item->data = data;
1319 item->height = 0;
1320 item->selected = FALSE;
1321 descr->nb_items++;
1323 /* Get item height */
1325 if (descr->style & LBS_OWNERDRAWVARIABLE)
1327 MEASUREITEMSTRUCT mis;
1328 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1330 mis.CtlType = ODT_LISTBOX;
1331 mis.CtlID = id;
1332 mis.itemID = index;
1333 mis.itemData = descr->items[index].data;
1334 mis.itemHeight = descr->item_height;
1335 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1336 item->height = mis.itemHeight ? mis.itemHeight : 1;
1337 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1338 wnd->hwndSelf, index, str ? str : "", item->height );
1341 /* Repaint the items */
1343 LISTBOX_UpdateScroll( wnd, descr );
1344 LISTBOX_InvalidateItems( wnd, descr, index );
1346 /* Move selection and focused item */
1348 if (index <= descr->selected_item) descr->selected_item++;
1349 if (index <= descr->focus_item)
1351 descr->focus_item++;
1352 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1355 /* If listbox was empty, set focus to the first item */
1357 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1358 return LB_OKAY;
1362 /***********************************************************************
1363 * LISTBOX_InsertString
1365 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1366 LPCSTR str )
1368 LPSTR new_str = NULL;
1369 DWORD data = 0;
1370 LRESULT ret;
1372 if (HAS_STRINGS(descr))
1374 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1376 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1377 return LB_ERRSPACE;
1380 else data = (DWORD)str;
1382 if (index == -1) index = descr->nb_items;
1383 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1385 if (new_str) HeapFree( descr->heap, 0, new_str );
1386 return ret;
1389 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1390 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1391 return index;
1395 /***********************************************************************
1396 * LISTBOX_DeleteItem
1398 * Delete the content of an item. 'index' must be a valid index.
1400 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1402 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1403 * while Win95 sends it for all items with user data.
1404 * It's probably better to send it too often than not
1405 * often enough, so this is what we do here.
1407 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1409 DELETEITEMSTRUCT dis;
1410 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1412 dis.CtlType = ODT_LISTBOX;
1413 dis.CtlID = id;
1414 dis.itemID = index;
1415 dis.hwndItem = wnd->hwndSelf;
1416 dis.itemData = descr->items[index].data;
1417 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1419 if (HAS_STRINGS(descr) && descr->items[index].str)
1420 HeapFree( descr->heap, 0, descr->items[index].str );
1424 /***********************************************************************
1425 * LISTBOX_RemoveItem
1427 * Remove an item from the listbox and delete its content.
1429 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1431 LB_ITEMDATA *item;
1432 INT max_items;
1434 if (index == -1) index = descr->nb_items - 1;
1435 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1436 LISTBOX_DeleteItem( wnd, descr, index );
1438 /* Remove the item */
1440 item = &descr->items[index];
1441 if (index < descr->nb_items-1)
1442 RtlMoveMemory( item, item + 1,
1443 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1444 descr->nb_items--;
1445 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1447 /* Shrink the item array if possible */
1449 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1450 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1452 max_items -= LB_ARRAY_GRANULARITY;
1453 item = HeapReAlloc( descr->heap, 0, descr->items,
1454 max_items * sizeof(LB_ITEMDATA) );
1455 if (item) descr->items = item;
1458 /* Repaint the items */
1460 LISTBOX_UpdateScroll( wnd, descr );
1461 LISTBOX_InvalidateItems( wnd, descr, index );
1463 /* Move selection and focused item */
1465 if (index <= descr->selected_item) descr->selected_item--;
1466 if (index <= descr->focus_item)
1468 descr->focus_item--;
1469 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1471 return LB_OKAY;
1475 /***********************************************************************
1476 * LISTBOX_ResetContent
1478 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1480 INT i;
1482 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1483 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1484 descr->nb_items = 0;
1485 descr->top_item = 0;
1486 descr->selected_item = -1;
1487 descr->focus_item = 0;
1488 descr->anchor_item = -1;
1489 descr->items = NULL;
1490 LISTBOX_UpdateScroll( wnd, descr );
1491 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1495 /***********************************************************************
1496 * LISTBOX_SetCount
1498 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1500 LRESULT ret;
1502 if (HAS_STRINGS(descr)) return LB_ERR;
1503 /* FIXME: this is far from optimal... */
1504 if (count > descr->nb_items)
1506 while (count > descr->nb_items)
1507 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1508 return ret;
1510 else if (count < descr->nb_items)
1512 while (count < descr->nb_items)
1513 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1514 return ret;
1516 return LB_OKAY;
1520 /***********************************************************************
1521 * LISTBOX_Directory
1523 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1524 LPCSTR filespec, BOOL long_names )
1526 HANDLE handle;
1527 LRESULT ret = LB_OKAY;
1528 WIN32_FIND_DATAA entry;
1529 int pos;
1531 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1533 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1535 else
1539 char buffer[270];
1540 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1542 if (!(attrib & DDL_DIRECTORY) ||
1543 !strcmp( entry.cAlternateFileName, "." )) continue;
1544 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1545 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1547 else /* not a directory */
1549 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1550 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1552 if ((attrib & DDL_EXCLUSIVE) &&
1553 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1554 continue;
1555 #undef ATTRIBS
1556 if (long_names) strcpy( buffer, entry.cFileName );
1557 else strcpy( buffer, entry.cAlternateFileName );
1559 if (!long_names) CharLowerA( buffer );
1560 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1561 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1562 break;
1563 } while (FindNextFileA( handle, &entry ));
1564 FindClose( handle );
1567 if ((ret >= 0) && (attrib & DDL_DRIVES))
1569 char buffer[] = "[-a-]";
1570 int drive;
1571 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1573 if (!DRIVE_IsValid(drive)) continue;
1574 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1575 break;
1578 return ret;
1582 /***********************************************************************
1583 * LISTBOX_HandleVScroll
1585 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1586 WPARAM wParam, LPARAM lParam )
1588 SCROLLINFO info;
1590 if (descr->style & LBS_MULTICOLUMN) return 0;
1591 switch(LOWORD(wParam))
1593 case SB_LINEUP:
1594 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1595 break;
1596 case SB_LINEDOWN:
1597 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1598 break;
1599 case SB_PAGEUP:
1600 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1601 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1602 break;
1603 case SB_PAGEDOWN:
1604 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1605 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1606 break;
1607 case SB_THUMBPOSITION:
1608 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1609 break;
1610 case SB_THUMBTRACK:
1611 info.cbSize = sizeof(info);
1612 info.fMask = SIF_TRACKPOS;
1613 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1614 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1615 break;
1616 case SB_TOP:
1617 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1618 break;
1619 case SB_BOTTOM:
1620 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1621 break;
1623 return 0;
1627 /***********************************************************************
1628 * LISTBOX_HandleHScroll
1630 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1631 WPARAM wParam, LPARAM lParam )
1633 SCROLLINFO info;
1634 INT page;
1636 if (descr->style & LBS_MULTICOLUMN)
1638 switch(LOWORD(wParam))
1640 case SB_LINELEFT:
1641 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1642 TRUE );
1643 break;
1644 case SB_LINERIGHT:
1645 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1646 TRUE );
1647 break;
1648 case SB_PAGELEFT:
1649 page = descr->width / descr->column_width;
1650 if (page < 1) page = 1;
1651 LISTBOX_SetTopItem( wnd, descr,
1652 descr->top_item - page * descr->page_size, TRUE );
1653 break;
1654 case SB_PAGERIGHT:
1655 page = descr->width / descr->column_width;
1656 if (page < 1) page = 1;
1657 LISTBOX_SetTopItem( wnd, descr,
1658 descr->top_item + page * descr->page_size, TRUE );
1659 break;
1660 case SB_THUMBPOSITION:
1661 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1662 TRUE );
1663 break;
1664 case SB_THUMBTRACK:
1665 info.cbSize = sizeof(info);
1666 info.fMask = SIF_TRACKPOS;
1667 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1668 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1669 TRUE );
1670 break;
1671 case SB_LEFT:
1672 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1673 break;
1674 case SB_RIGHT:
1675 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1676 break;
1679 else if (descr->horz_extent)
1681 switch(LOWORD(wParam))
1683 case SB_LINELEFT:
1684 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1685 break;
1686 case SB_LINERIGHT:
1687 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1688 break;
1689 case SB_PAGELEFT:
1690 LISTBOX_SetHorizontalPos( wnd, descr,
1691 descr->horz_pos - descr->width );
1692 break;
1693 case SB_PAGERIGHT:
1694 LISTBOX_SetHorizontalPos( wnd, descr,
1695 descr->horz_pos + descr->width );
1696 break;
1697 case SB_THUMBPOSITION:
1698 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1699 break;
1700 case SB_THUMBTRACK:
1701 info.cbSize = sizeof(info);
1702 info.fMask = SIF_TRACKPOS;
1703 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1704 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1705 break;
1706 case SB_LEFT:
1707 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1708 break;
1709 case SB_RIGHT:
1710 LISTBOX_SetHorizontalPos( wnd, descr,
1711 descr->horz_extent - descr->width );
1712 break;
1715 return 0;
1719 /***********************************************************************
1720 * LISTBOX_HandleLButtonDown
1722 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1723 WPARAM wParam, INT x, INT y )
1725 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1726 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1727 wnd->hwndSelf, x, y, index );
1728 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1729 if (index != -1)
1731 if (descr->style & LBS_EXTENDEDSEL)
1733 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1734 if (wParam & MK_CONTROL)
1736 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1737 LISTBOX_SetSelection( wnd, descr, index,
1738 !descr->items[index].selected, FALSE );
1740 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1742 else
1744 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1745 LISTBOX_SetSelection( wnd, descr, index,
1746 (!(descr->style & LBS_MULTIPLESEL) ||
1747 !descr->items[index].selected), FALSE );
1751 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1752 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1753 : descr->lphc->self->hwndSelf ) ;
1755 SetCapture( wnd->hwndSelf );
1756 if (index != -1 && !descr->lphc)
1758 if (descr->style & LBS_NOTIFY )
1759 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1760 MAKELPARAM( x, y ) );
1761 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1763 POINT pt = { x, y };
1764 if (DragDetect( wnd->hwndSelf, pt ))
1765 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1768 return 0;
1772 /***********************************************************************
1773 * LISTBOX_HandleLButtonUp
1775 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1777 if (LISTBOX_Timer != LB_TIMER_NONE)
1778 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1779 LISTBOX_Timer = LB_TIMER_NONE;
1780 if (GetCapture() == wnd->hwndSelf)
1782 ReleaseCapture();
1783 if (descr->style & LBS_NOTIFY)
1784 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1786 return 0;
1790 /***********************************************************************
1791 * LISTBOX_HandleTimer
1793 * Handle scrolling upon a timer event.
1794 * Return TRUE if scrolling should continue.
1796 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1797 INT index, TIMER_DIRECTION dir )
1799 switch(dir)
1801 case LB_TIMER_UP:
1802 if (descr->top_item) index = descr->top_item - 1;
1803 else index = 0;
1804 break;
1805 case LB_TIMER_LEFT:
1806 if (descr->top_item) index -= descr->page_size;
1807 break;
1808 case LB_TIMER_DOWN:
1809 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1810 if (index == descr->focus_item) index++;
1811 if (index >= descr->nb_items) index = descr->nb_items - 1;
1812 break;
1813 case LB_TIMER_RIGHT:
1814 if (index + descr->page_size < descr->nb_items)
1815 index += descr->page_size;
1816 break;
1817 case LB_TIMER_NONE:
1818 break;
1820 if (index == descr->focus_item) return FALSE;
1821 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1822 return TRUE;
1826 /***********************************************************************
1827 * LISTBOX_HandleSystemTimer
1829 * WM_SYSTIMER handler.
1831 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1833 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1835 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1836 LISTBOX_Timer = LB_TIMER_NONE;
1838 return 0;
1842 /***********************************************************************
1843 * LISTBOX_HandleMouseMove
1845 * WM_MOUSEMOVE handler.
1847 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1848 INT x, INT y )
1850 INT index;
1851 TIMER_DIRECTION dir;
1853 if (descr->style & LBS_MULTICOLUMN)
1855 if (y < 0) y = 0;
1856 else if (y >= descr->item_height * descr->page_size)
1857 y = descr->item_height * descr->page_size - 1;
1859 if (x < 0)
1861 dir = LB_TIMER_LEFT;
1862 x = 0;
1864 else if (x >= descr->width)
1866 dir = LB_TIMER_RIGHT;
1867 x = descr->width - 1;
1869 else dir = LB_TIMER_NONE; /* inside */
1871 else
1873 if (y < 0) dir = LB_TIMER_UP; /* above */
1874 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1875 else dir = LB_TIMER_NONE; /* inside */
1878 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1879 if (index == -1) index = descr->focus_item;
1880 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1882 /* Start/stop the system timer */
1884 if (dir != LB_TIMER_NONE)
1885 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1886 else if (LISTBOX_Timer != LB_TIMER_NONE)
1887 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1888 LISTBOX_Timer = dir;
1892 /***********************************************************************
1893 * LISTBOX_HandleKeyDown
1895 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1897 INT caret = -1;
1898 if (descr->style & LBS_WANTKEYBOARDINPUT)
1900 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1901 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1902 wnd->hwndSelf );
1903 if (caret == -2) return 0;
1905 if (caret == -1) switch(wParam)
1907 case VK_LEFT:
1908 if (descr->style & LBS_MULTICOLUMN)
1910 if (descr->focus_item >= descr->page_size)
1911 caret = descr->focus_item - descr->page_size;
1912 break;
1914 /* fall through */
1915 case VK_UP:
1916 caret = descr->focus_item - 1;
1917 if (caret < 0) caret = 0;
1918 break;
1919 case VK_RIGHT:
1920 if (descr->style & LBS_MULTICOLUMN)
1922 if (descr->focus_item + descr->page_size < descr->nb_items)
1923 caret = descr->focus_item + descr->page_size;
1924 break;
1926 /* fall through */
1927 case VK_DOWN:
1928 caret = descr->focus_item + 1;
1929 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1930 break;
1931 case VK_PRIOR:
1932 if (descr->style & LBS_MULTICOLUMN)
1934 INT page = descr->width / descr->column_width;
1935 if (page < 1) page = 1;
1936 caret = descr->focus_item - (page * descr->page_size) + 1;
1938 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1939 if (caret < 0) caret = 0;
1940 break;
1941 case VK_NEXT:
1942 if (descr->style & LBS_MULTICOLUMN)
1944 INT page = descr->width / descr->column_width;
1945 if (page < 1) page = 1;
1946 caret = descr->focus_item + (page * descr->page_size) - 1;
1948 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1949 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1950 break;
1951 case VK_HOME:
1952 caret = 0;
1953 break;
1954 case VK_END:
1955 caret = descr->nb_items - 1;
1956 break;
1957 case VK_SPACE:
1958 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1959 else if (descr->style & LBS_MULTIPLESEL)
1961 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1962 !descr->items[descr->focus_item].selected,
1963 (descr->style & LBS_NOTIFY) != 0 );
1965 else if (descr->selected_item == -1)
1967 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1968 (descr->style & LBS_NOTIFY) != 0 );
1970 break;
1972 if (caret >= 0)
1974 if ((descr->style & LBS_EXTENDEDSEL) &&
1975 !(GetKeyState( VK_SHIFT ) & 0x8000))
1976 descr->anchor_item = caret;
1977 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1978 if (descr->style & LBS_NOTIFY)
1980 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1982 /* make sure that combo parent doesn't hide us */
1983 descr->lphc->wState |= CBF_NOROLLUP;
1985 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1988 return 0;
1992 /***********************************************************************
1993 * LISTBOX_HandleChar
1995 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1996 WPARAM wParam )
1998 INT caret = -1;
1999 char str[2] = { wParam & 0xff, '\0' };
2001 if (descr->style & LBS_WANTKEYBOARDINPUT)
2003 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2004 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2005 wnd->hwndSelf );
2006 if (caret == -2) return 0;
2008 if (caret == -1)
2009 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2010 if (caret != -1)
2012 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2013 if (descr->style & LBS_NOTIFY)
2014 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2016 return 0;
2020 /***********************************************************************
2021 * LISTBOX_Create
2023 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2025 LB_DESCR *descr;
2026 MEASUREITEMSTRUCT mis;
2027 RECT rect;
2029 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2030 return FALSE;
2031 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2033 HeapFree( GetProcessHeap(), 0, descr );
2034 return FALSE;
2036 GetClientRect( wnd->hwndSelf, &rect );
2037 descr->owner = GetParent( wnd->hwndSelf );
2038 descr->style = wnd->dwStyle;
2039 descr->width = rect.right - rect.left;
2040 descr->height = rect.bottom - rect.top;
2041 descr->items = NULL;
2042 descr->nb_items = 0;
2043 descr->top_item = 0;
2044 descr->selected_item = -1;
2045 descr->focus_item = 0;
2046 descr->anchor_item = -1;
2047 descr->item_height = 1;
2048 descr->page_size = 1;
2049 descr->column_width = 150;
2050 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2051 descr->horz_pos = 0;
2052 descr->nb_tabs = 0;
2053 descr->tabs = NULL;
2054 descr->caret_on = TRUE;
2055 descr->font = 0;
2056 descr->locale = 0; /* FIXME */
2057 descr->lphc = lphc;
2059 if( lphc )
2061 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2062 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2063 descr->owner = lphc->self->hwndSelf;
2066 *(LB_DESCR **)wnd->wExtra = descr;
2068 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2070 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2071 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2072 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2073 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2075 if (descr->style & LBS_OWNERDRAWFIXED)
2077 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2079 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2080 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2082 else
2084 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2086 mis.CtlType = ODT_LISTBOX;
2087 mis.CtlID = id;
2088 mis.itemID = -1;
2089 mis.itemWidth = 0;
2090 mis.itemData = 0;
2091 mis.itemHeight = descr->item_height;
2092 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2093 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2097 return TRUE;
2101 /***********************************************************************
2102 * LISTBOX_Destroy
2104 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2106 LISTBOX_ResetContent( wnd, descr );
2107 HeapDestroy( descr->heap );
2108 HeapFree( GetProcessHeap(), 0, descr );
2109 wnd->wExtra[0] = 0;
2110 return TRUE;
2114 /***********************************************************************
2115 * ListBoxWndProc
2117 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2118 WPARAM wParam, LPARAM lParam )
2120 LRESULT ret;
2121 LB_DESCR *descr;
2122 WND *wnd = WIN_FindWndPtr( hwnd );
2123 LRESULT retvalue;
2125 if (!wnd) return 0;
2126 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2128 if (msg == WM_CREATE)
2130 if (!LISTBOX_Create( wnd, NULL ))
2132 retvalue = -1;
2133 goto END;
2135 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2136 hwnd, *(LB_DESCR **)wnd->wExtra );
2137 retvalue = 0;
2138 goto END;
2140 /* Ignore all other messages before we get a WM_CREATE */
2141 retvalue = DefWindowProcA( hwnd, msg, wParam, lParam );
2142 goto END;
2145 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2146 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2147 switch(msg)
2149 case LB_RESETCONTENT16:
2150 case LB_RESETCONTENT:
2151 LISTBOX_ResetContent( wnd, descr );
2152 retvalue = 0;
2153 goto END;
2155 case LB_ADDSTRING16:
2156 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2157 /* fall through */
2158 case LB_ADDSTRING:
2159 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2160 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2161 goto END;
2163 case LB_INSERTSTRING16:
2164 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2165 wParam = (INT)(INT16)wParam;
2166 /* fall through */
2167 case LB_INSERTSTRING:
2168 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2169 goto END;
2171 case LB_ADDFILE16:
2172 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2173 /* fall through */
2174 case LB_ADDFILE:
2175 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2176 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2177 goto END;
2179 case LB_DELETESTRING16:
2180 case LB_DELETESTRING:
2181 retvalue = LISTBOX_RemoveItem( wnd, descr, wParam );
2182 goto END;
2184 case LB_GETITEMDATA16:
2185 case LB_GETITEMDATA:
2186 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2188 retvalue = LB_ERR;
2189 goto END;
2191 retvalue = descr->items[wParam].data;
2192 goto END;
2194 case LB_SETITEMDATA16:
2195 case LB_SETITEMDATA:
2196 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2198 retvalue = LB_ERR;
2199 goto END;
2201 descr->items[wParam].data = (DWORD)lParam;
2202 retvalue = LB_OKAY;
2203 goto END;
2205 case LB_GETCOUNT16:
2206 case LB_GETCOUNT:
2207 retvalue = descr->nb_items;
2208 goto END;
2210 case LB_GETTEXT16:
2211 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2212 /* fall through */
2213 case LB_GETTEXT:
2214 retvalue = LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2215 goto END;
2217 case LB_GETTEXTLEN16:
2218 /* fall through */
2219 case LB_GETTEXTLEN:
2220 if (wParam >= descr->nb_items)
2222 retvalue = LB_ERR;
2223 goto END;
2225 retvalue = (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2226 : sizeof(DWORD));
2227 goto END;
2229 case LB_GETCURSEL16:
2230 case LB_GETCURSEL:
2231 retvalue = descr->selected_item;
2232 if (retvalue == -1) retvalue = descr->focus_item;
2233 /* otherwise, if the user tries to move the selection with the */
2234 /* arrow keys, we will give the application something to choke on */
2235 goto END;
2237 case LB_GETTOPINDEX16:
2238 case LB_GETTOPINDEX:
2239 retvalue = descr->top_item;
2240 goto END;
2242 case LB_GETITEMHEIGHT16:
2243 case LB_GETITEMHEIGHT:
2244 retvalue = LISTBOX_GetItemHeight( wnd, descr, wParam );
2245 goto END;
2247 case LB_SETITEMHEIGHT16:
2248 lParam = LOWORD(lParam);
2249 /* fall through */
2250 case LB_SETITEMHEIGHT:
2251 retvalue = LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2252 goto END;
2254 case LB_ITEMFROMPOINT:
2256 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
2257 RECT rect = { 0, 0, descr->width, descr->height };
2258 retvalue = MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2259 PtInRect( &rect, pt ) );
2260 goto END;
2263 case LB_SETCARETINDEX16:
2264 case LB_SETCARETINDEX:
2265 retvalue = LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2266 goto END;
2268 case LB_GETCARETINDEX16:
2269 case LB_GETCARETINDEX:
2270 retvalue = descr->focus_item;
2271 goto END;
2273 case LB_SETTOPINDEX16:
2274 case LB_SETTOPINDEX:
2275 retvalue = LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2276 goto END;
2278 case LB_SETCOLUMNWIDTH16:
2279 case LB_SETCOLUMNWIDTH:
2280 retvalue = LISTBOX_SetColumnWidth( wnd, descr, wParam );
2281 goto END;
2283 case LB_GETITEMRECT16:
2285 RECT rect;
2286 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2287 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2289 retvalue = ret;
2290 goto END;
2292 case LB_GETITEMRECT:
2293 retvalue = LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2294 goto END;
2296 case LB_FINDSTRING16:
2297 wParam = (INT)(INT16)wParam;
2298 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2299 /* fall through */
2300 case LB_FINDSTRING:
2301 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2302 goto END;
2304 case LB_FINDSTRINGEXACT16:
2305 wParam = (INT)(INT16)wParam;
2306 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2307 /* fall through */
2308 case LB_FINDSTRINGEXACT:
2309 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2310 goto END;
2312 case LB_SELECTSTRING16:
2313 wParam = (INT)(INT16)wParam;
2314 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2315 /* fall through */
2316 case LB_SELECTSTRING:
2318 INT index = LISTBOX_FindString( wnd, descr, wParam,
2319 (LPCSTR)lParam, FALSE );
2320 if (index == LB_ERR)
2322 retvalue = LB_ERR;
2323 goto END;
2325 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2326 retvalue = index;
2327 goto END;
2330 case LB_GETSEL16:
2331 wParam = (INT)(INT16)wParam;
2332 /* fall through */
2333 case LB_GETSEL:
2334 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2336 retvalue = LB_ERR;
2337 goto END;
2339 retvalue = descr->items[wParam].selected;
2340 goto END;
2342 case LB_SETSEL16:
2343 lParam = (INT)(INT16)lParam;
2344 /* fall through */
2345 case LB_SETSEL:
2346 retvalue = LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2347 goto END;
2349 case LB_SETCURSEL16:
2350 wParam = (INT)(INT16)wParam;
2351 /* fall through */
2352 case LB_SETCURSEL:
2353 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2354 retvalue = LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2355 goto END;
2357 case LB_GETSELCOUNT16:
2358 case LB_GETSELCOUNT:
2359 retvalue = LISTBOX_GetSelCount( wnd, descr );
2360 goto END;
2362 case LB_GETSELITEMS16:
2363 retvalue = LISTBOX_GetSelItems16( wnd, descr, wParam,
2364 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2365 goto END;
2367 case LB_GETSELITEMS:
2368 retvalue = LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2369 goto END;
2371 case LB_SELITEMRANGE16:
2372 case LB_SELITEMRANGE:
2373 if (LOWORD(lParam) <= HIWORD(lParam))
2375 retvalue = LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2376 HIWORD(lParam), wParam );
2378 else
2380 retvalue = LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2381 LOWORD(lParam), wParam );
2383 goto END;
2385 case LB_SELITEMRANGEEX16:
2386 case LB_SELITEMRANGEEX:
2387 if ((INT)lParam >= (INT)wParam)
2388 retvalue = LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2389 else
2390 retvalue = LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2391 goto END;
2393 case LB_GETHORIZONTALEXTENT16:
2394 case LB_GETHORIZONTALEXTENT:
2395 retvalue = descr->horz_extent;
2396 goto END;
2398 case LB_SETHORIZONTALEXTENT16:
2399 case LB_SETHORIZONTALEXTENT:
2400 retvalue = LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2401 goto END;
2403 case LB_GETANCHORINDEX16:
2404 case LB_GETANCHORINDEX:
2405 retvalue = descr->anchor_item;
2406 goto END;
2408 case LB_SETANCHORINDEX16:
2409 wParam = (INT)(INT16)wParam;
2410 /* fall through */
2411 case LB_SETANCHORINDEX:
2412 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2414 retvalue = LB_ERR;
2415 goto END;
2417 descr->anchor_item = (INT)wParam;
2418 retvalue = LB_OKAY;
2419 goto END;
2421 case LB_DIR16:
2422 retvalue = LISTBOX_Directory( wnd, descr, wParam,
2423 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2424 goto END;
2426 case LB_DIR:
2427 retvalue = LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2428 goto END;
2430 case LB_GETLOCALE:
2431 retvalue = descr->locale;
2432 goto END;
2434 case LB_SETLOCALE:
2435 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2436 retvalue = LB_OKAY;
2437 goto END;
2439 case LB_INITSTORAGE:
2440 retvalue = LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2441 goto END;
2443 case LB_SETCOUNT:
2444 retvalue = LISTBOX_SetCount( wnd, descr, (INT)wParam );
2445 goto END;
2447 case LB_SETTABSTOPS16:
2448 retvalue = LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2449 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2450 goto END;
2452 case LB_SETTABSTOPS:
2453 retvalue = LISTBOX_SetTabStops( wnd, descr, wParam,
2454 (LPINT)lParam, FALSE );
2455 goto END;
2457 case LB_CARETON16:
2458 case LB_CARETON:
2459 if (descr->caret_on)
2461 retvalue = LB_OKAY;
2462 goto END;
2464 descr->caret_on = TRUE;
2465 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2466 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2467 retvalue = LB_OKAY;
2468 goto END;
2470 case LB_CARETOFF16:
2471 case LB_CARETOFF:
2472 if (!descr->caret_on)
2474 retvalue = LB_OKAY;
2475 goto END;
2477 descr->caret_on = FALSE;
2478 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2479 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2480 retvalue = LB_OKAY;
2481 goto END;
2483 case WM_DESTROY:
2484 retvalue = LISTBOX_Destroy( wnd, descr );
2485 goto END;
2487 case WM_ENABLE:
2488 InvalidateRect( hwnd, NULL, TRUE );
2489 retvalue = 0;
2490 goto END;
2492 case WM_SETREDRAW:
2493 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2494 retvalue = 0;
2495 goto END;
2497 case WM_GETDLGCODE:
2498 retvalue =DLGC_WANTARROWS | DLGC_WANTCHARS;
2499 goto END;
2500 case WM_PAINT:
2502 PAINTSTRUCT ps;
2503 HDC hdc = ( wParam ) ? ((HDC)wParam)
2504 : BeginPaint( hwnd, &ps );
2505 ret = LISTBOX_Paint( wnd, descr, hdc );
2506 if( !wParam ) EndPaint( hwnd, &ps );
2508 retvalue =ret;
2509 goto END;
2510 case WM_SIZE:
2511 LISTBOX_UpdateSize( wnd, descr );
2512 retvalue =0;
2513 goto END;
2514 case WM_GETFONT:
2515 retvalue =descr->font;
2516 goto END;
2517 case WM_SETFONT:
2518 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2519 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2520 retvalue =0;
2521 goto END;
2522 case WM_SETFOCUS:
2523 descr->caret_on = TRUE;
2524 if (descr->focus_item != -1)
2525 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2526 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2527 retvalue =0;
2528 goto END;
2529 case WM_KILLFOCUS:
2530 if ((descr->focus_item != -1) && descr->caret_on)
2531 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2532 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2533 retvalue =0;
2534 goto END;
2535 case WM_HSCROLL:
2536 retvalue =LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2537 goto END;
2538 case WM_VSCROLL:
2539 retvalue =LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2540 goto END;
2541 case WM_LBUTTONDOWN:
2542 retvalue =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2543 (INT16)LOWORD(lParam),
2544 (INT16)HIWORD(lParam) );
2545 goto END;
2546 case WM_LBUTTONDBLCLK:
2547 if (descr->style & LBS_NOTIFY)
2548 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2549 retvalue =0;
2550 goto END;
2551 case WM_MOUSEMOVE:
2552 if (GetCapture() == hwnd)
2553 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2554 (INT16)HIWORD(lParam) );
2555 retvalue =0;
2556 goto END;
2557 case WM_LBUTTONUP:
2558 retvalue =LISTBOX_HandleLButtonUp( wnd, descr );
2559 goto END;
2560 case WM_KEYDOWN:
2561 retvalue =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2562 goto END;
2563 case WM_CHAR:
2564 retvalue =LISTBOX_HandleChar( wnd, descr, wParam );
2565 goto END;
2566 case WM_SYSTIMER:
2567 retvalue =LISTBOX_HandleSystemTimer( wnd, descr );
2568 goto END;
2569 case WM_ERASEBKGND:
2570 if (IS_OWNERDRAW(descr))
2572 RECT rect = { 0, 0, descr->width, descr->height };
2573 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2574 wParam, (LPARAM)wnd->hwndSelf );
2575 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2577 retvalue =1;
2578 goto END;
2579 case WM_DROPFILES:
2580 if( !descr->lphc )
2582 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2583 goto END;
2585 break;
2587 case WM_DROPOBJECT:
2588 case WM_QUERYDROPOBJECT:
2589 case WM_DRAGSELECT:
2590 case WM_DRAGMOVE:
2591 if( !descr->lphc )
2593 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2594 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2595 dragInfo->pt.y );
2596 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2597 goto END;
2599 break;
2601 case WM_NCCREATE:
2602 if (TWEAK_WineLook > WIN31_LOOK)
2603 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2604 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2605 goto END;
2606 default:
2607 if ((msg >= WM_USER) && (msg < 0xc000))
2608 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2609 hwnd, msg, wParam, lParam );
2610 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2611 goto END;
2613 retvalue =0;
2614 END:
2615 WIN_ReleaseWndPtr(wnd);
2616 return retvalue;
2619 /***********************************************************************
2620 * COMBO_Directory
2622 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2624 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2626 if( wnd )
2628 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2629 if( descr )
2631 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2633 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2634 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2635 WIN_ReleaseWndPtr(wnd);
2636 return lRet;
2638 WIN_ReleaseWndPtr(wnd);
2640 return CB_ERR;
2643 /***********************************************************************
2644 * ComboLBWndProc
2646 * NOTE: in Windows, winproc address of the ComboLBox is the same
2647 * as that of the Listbox.
2649 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2650 WPARAM wParam, LPARAM lParam )
2652 LRESULT lRet = 0;
2653 WND *wnd = WIN_FindWndPtr( hwnd );
2655 if (wnd)
2657 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2659 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2660 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2662 if( descr || msg == WM_CREATE )
2664 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2666 switch( msg )
2668 case WM_CREATE:
2669 #define lpcs ((LPCREATESTRUCTA)lParam)
2670 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2671 (UINT)lpcs->lpCreateParams);
2673 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2674 #undef lpcs
2675 lRet =LISTBOX_Create( wnd, lphc );
2676 goto END;
2677 case WM_LBUTTONDOWN:
2678 lRet =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2679 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2680 goto END;
2681 /* avoid activation at all costs */
2683 case WM_MOUSEACTIVATE:
2684 lRet =MA_NOACTIVATE;
2685 goto END;
2686 case WM_NCACTIVATE:
2687 lRet =FALSE;
2688 goto END;
2689 case WM_KEYDOWN:
2690 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2692 /* for some reason(?) Windows makes it possible to
2693 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2695 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2696 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2697 && (wParam == VK_DOWN || wParam == VK_UP)) )
2699 COMBO_FlipListbox( lphc, FALSE );
2700 lRet =0;
2701 goto END;
2704 lRet =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2705 goto END;
2707 case LB_SETCURSEL16:
2708 case LB_SETCURSEL:
2709 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2710 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2711 goto END;
2712 case WM_NCDESTROY:
2713 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2714 lphc->hWndLBox = 0;
2715 /* fall through */
2717 default:
2718 lRet =ListBoxWndProc( hwnd, msg, wParam, lParam );
2719 goto END;
2722 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2724 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2726 END:
2727 WIN_ReleaseWndPtr(wnd);
2728 return lRet;