Authors: Chris Morgan <cmorgan@wpi.edu>, James Abbatiello <abbejy@wpi.edu>
[wine/multimedia.git] / controls / listbox.c
blob7f9b58dc94a108f4b84221a5397bc7971a2e2cf5
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 DECLARE_DEBUG_CHANNEL(combo)
21 DECLARE_DEBUG_CHANNEL(listbox)
23 /* Unimplemented yet:
24 * - LBS_NOSEL
25 * - LBS_USETABSTOPS
26 * - Unicode
27 * - Locale handling
30 /* Items array granularity */
31 #define LB_ARRAY_GRANULARITY 16
33 /* Scrolling timeout in ms */
34 #define LB_SCROLL_TIMEOUT 50
36 /* Listbox system timer id */
37 #define LB_TIMER_ID 2
39 /* Item structure */
40 typedef struct
42 LPSTR str; /* Item text */
43 BOOL selected; /* Is item selected? */
44 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
45 DWORD data; /* User data */
46 } LB_ITEMDATA;
48 /* Listbox structure */
49 typedef struct
51 HANDLE heap; /* Heap for this listbox */
52 HWND owner; /* Owner window to send notifications to */
53 UINT style; /* Window style */
54 INT width; /* Window width */
55 INT height; /* Window height */
56 LB_ITEMDATA *items; /* Array of items */
57 INT nb_items; /* Number of items */
58 INT top_item; /* Top visible item */
59 INT selected_item; /* Selected item */
60 INT focus_item; /* Item that has the focus */
61 INT anchor_item; /* Anchor item for extended selection */
62 INT item_height; /* Default item height */
63 INT page_size; /* Items per listbox page */
64 INT column_width; /* Column width for multi-column listboxes */
65 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
66 INT horz_pos; /* Horizontal position */
67 INT nb_tabs; /* Number of tabs in array */
68 INT *tabs; /* Array of tabs */
69 BOOL caret_on; /* Is caret on? */
70 BOOL captured; /* Is mouse captured? */
71 HFONT font; /* Current font */
72 LCID locale; /* Current locale for string comparisons */
73 LPHEADCOMBO lphc; /* ComboLBox */
74 } LB_DESCR;
77 #define IS_OWNERDRAW(descr) \
78 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
80 #define HAS_STRINGS(descr) \
81 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
83 #define SEND_NOTIFICATION(wnd,descr,code) \
84 (SendMessageA( (descr)->owner, WM_COMMAND, \
85 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
87 /* Current timer status */
88 typedef enum
90 LB_TIMER_NONE,
91 LB_TIMER_UP,
92 LB_TIMER_LEFT,
93 LB_TIMER_DOWN,
94 LB_TIMER_RIGHT
95 } TIMER_DIRECTION;
97 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
100 /***********************************************************************
101 * LISTBOX_Dump
103 void LISTBOX_Dump( WND *wnd )
105 INT i;
106 LB_ITEMDATA *item;
107 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
109 DUMP( "Listbox:\n" );
110 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
111 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
112 descr->top_item );
113 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
115 DUMP( "%4d: %-40s %d %08lx %3d\n",
116 i, item->str, item->selected, item->data, item->height );
121 /***********************************************************************
122 * LISTBOX_GetCurrentPageSize
124 * Return the current page size
126 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
128 INT i, height;
129 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
130 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
132 if ((height += descr->items[i].height) > descr->height) break;
134 if (i == descr->top_item) return 1;
135 else return i - descr->top_item;
139 /***********************************************************************
140 * LISTBOX_GetMaxTopIndex
142 * Return the maximum possible index for the top of the listbox.
144 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
146 INT max, page;
148 if (descr->style & LBS_OWNERDRAWVARIABLE)
150 page = descr->height;
151 for (max = descr->nb_items - 1; max >= 0; max--)
152 if ((page -= descr->items[max].height) < 0) break;
153 if (max < descr->nb_items - 1) max++;
155 else if (descr->style & LBS_MULTICOLUMN)
157 if ((page = descr->width / descr->column_width) < 1) page = 1;
158 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
159 max = (max - page) * descr->page_size;
161 else
163 max = descr->nb_items - descr->page_size;
165 if (max < 0) max = 0;
166 return max;
170 /***********************************************************************
171 * LISTBOX_UpdateScroll
173 * Update the scrollbars. Should be called whenever the content
174 * of the listbox changes.
176 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
178 SCROLLINFO info;
180 if (!(descr->style & WS_VSCROLL)) return;
181 /* It is important that we check descr->style, and not wnd->dwStyle,
182 for WS_VSCROLL, as the former is exactly the one passed in
183 argument to CreateWindow.
184 In Windows (and from now on in Wine :) a listbox created
185 with such a style (no WS_SCROLL) does not update
186 the scrollbar with listbox-related data, thus letting
187 the programmer use it for his/her own purposes. */
189 if (descr->style & LBS_NOREDRAW) return;
190 info.cbSize = sizeof(info);
192 if (descr->style & LBS_MULTICOLUMN)
194 info.nMin = 0;
195 info.nMax = (descr->nb_items - 1) / descr->page_size;
196 info.nPos = descr->top_item / descr->page_size;
197 info.nPage = descr->width / descr->column_width;
198 if (info.nPage < 1) info.nPage = 1;
199 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
200 if (descr->style & LBS_DISABLENOSCROLL)
201 info.fMask |= SIF_DISABLENOSCROLL;
202 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
203 info.nMax = 0;
204 info.fMask = SIF_RANGE;
205 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
207 else
209 info.nMin = 0;
210 info.nMax = descr->nb_items - 1;
211 info.nPos = descr->top_item;
212 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
213 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
214 if (descr->style & LBS_DISABLENOSCROLL)
215 info.fMask |= SIF_DISABLENOSCROLL;
216 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
218 if (descr->horz_extent)
220 info.nMin = 0;
221 info.nMax = descr->horz_extent - 1;
222 info.nPos = descr->horz_pos;
223 info.nPage = descr->width;
224 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
225 if (descr->style & LBS_DISABLENOSCROLL)
226 info.fMask |= SIF_DISABLENOSCROLL;
227 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
233 /***********************************************************************
234 * LISTBOX_SetTopItem
236 * Set the top item of the listbox, scrolling up or down if necessary.
238 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
239 BOOL scroll )
241 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
242 if (index > max) index = max;
243 if (index < 0) index = 0;
244 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
245 if (descr->top_item == index) return LB_OKAY;
246 if (descr->style & LBS_MULTICOLUMN)
248 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
249 if (scroll && (abs(diff) < descr->width))
250 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
251 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
253 else
254 scroll = FALSE;
256 else if (scroll)
258 INT diff;
259 if (descr->style & LBS_OWNERDRAWVARIABLE)
261 INT i;
262 diff = 0;
263 if (index > descr->top_item)
265 for (i = index - 1; i >= descr->top_item; i--)
266 diff -= descr->items[i].height;
268 else
270 for (i = index; i < descr->top_item; i++)
271 diff += descr->items[i].height;
274 else
275 diff = (descr->top_item - index) * descr->item_height;
277 if (abs(diff) < descr->height)
278 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
279 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
280 else
281 scroll = FALSE;
283 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
284 descr->top_item = index;
285 LISTBOX_UpdateScroll( wnd, descr );
286 return LB_OKAY;
290 /***********************************************************************
291 * LISTBOX_UpdatePage
293 * Update the page size. Should be called when the size of
294 * the client area or the item height changes.
296 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
298 INT page_size;
300 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
301 if (page_size == descr->page_size) return;
302 descr->page_size = page_size;
303 if (descr->style & LBS_MULTICOLUMN)
304 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
305 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
309 /***********************************************************************
310 * LISTBOX_UpdateSize
312 * Update the size of the listbox. Should be called when the size of
313 * the client area changes.
315 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
317 RECT rect;
319 GetClientRect( wnd->hwndSelf, &rect );
320 descr->width = rect.right - rect.left;
321 descr->height = rect.bottom - rect.top;
322 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
324 if ((descr->height > descr->item_height) &&
325 (descr->height % descr->item_height))
327 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
328 wnd->hwndSelf, descr->height,
329 descr->height - descr->height%descr->item_height );
330 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
331 wnd->rectWindow.right - wnd->rectWindow.left,
332 wnd->rectWindow.bottom - wnd->rectWindow.top -
333 (descr->height % descr->item_height),
334 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
335 return;
338 TRACE(listbox, "[%04x]: new size = %d,%d\n",
339 wnd->hwndSelf, descr->width, descr->height );
340 LISTBOX_UpdatePage( wnd, descr );
341 LISTBOX_UpdateScroll( wnd, descr );
345 /***********************************************************************
346 * LISTBOX_GetItemRect
348 * Get the rectangle enclosing an item, in listbox client coordinates.
349 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
351 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
352 RECT *rect )
354 /* Index <= 0 is legal even on empty listboxes */
355 if (index && (index >= descr->nb_items)) return -1;
356 SetRect( rect, 0, 0, descr->width, descr->height );
357 if (descr->style & LBS_MULTICOLUMN)
359 INT col = (index / descr->page_size) -
360 (descr->top_item / descr->page_size);
361 rect->left += col * descr->column_width;
362 rect->right = rect->left + descr->column_width;
363 rect->top += (index % descr->page_size) * descr->item_height;
364 rect->bottom = rect->top + descr->item_height;
366 else if (descr->style & LBS_OWNERDRAWVARIABLE)
368 INT i;
369 rect->right += descr->horz_pos;
370 if ((index >= 0) && (index < descr->nb_items))
372 if (index < descr->top_item)
374 for (i = descr->top_item-1; i >= index; i--)
375 rect->top -= descr->items[i].height;
377 else
379 for (i = descr->top_item; i < index; i++)
380 rect->top += descr->items[i].height;
382 rect->bottom = rect->top + descr->items[index].height;
386 else
388 rect->top += (index - descr->top_item) * descr->item_height;
389 rect->bottom = rect->top + descr->item_height;
390 rect->right += descr->horz_pos;
393 return ((rect->left < descr->width) && (rect->right > 0) &&
394 (rect->top < descr->height) && (rect->bottom > 0));
398 /***********************************************************************
399 * LISTBOX_GetItemFromPoint
401 * Return the item nearest from point (x,y) (in client coordinates).
403 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
404 INT x, INT y )
406 INT index = descr->top_item;
408 if (!descr->nb_items) return -1; /* No items */
409 if (descr->style & LBS_OWNERDRAWVARIABLE)
411 INT pos = 0;
412 if (y >= 0)
414 while (index < descr->nb_items)
416 if ((pos += descr->items[index].height) > y) break;
417 index++;
420 else
422 while (index > 0)
424 index--;
425 if ((pos -= descr->items[index].height) <= y) break;
429 else if (descr->style & LBS_MULTICOLUMN)
431 if (y >= descr->item_height * descr->page_size) return -1;
432 if (y >= 0) index += y / descr->item_height;
433 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
434 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
436 else
438 index += (y / descr->item_height);
440 if (index < 0) return 0;
441 if (index >= descr->nb_items) return -1;
442 return index;
446 /***********************************************************************
447 * LISTBOX_PaintItem
449 * Paint an item.
451 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
452 const RECT *rect, INT index, UINT action )
454 LB_ITEMDATA *item = NULL;
455 if (index < descr->nb_items) item = &descr->items[index];
457 if (IS_OWNERDRAW(descr))
459 DRAWITEMSTRUCT dis;
460 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
462 if (!item)
464 if (action == ODA_FOCUS)
465 DrawFocusRect( hdc, rect );
466 else
467 FIXME(listbox,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
468 return;
470 dis.CtlType = ODT_LISTBOX;
471 dis.CtlID = id;
472 dis.hwndItem = wnd->hwndSelf;
473 dis.itemAction = action;
474 dis.hDC = hdc;
475 dis.itemID = index;
476 dis.itemState = 0;
477 if (item && item->selected) dis.itemState |= ODS_SELECTED;
478 if ((descr->focus_item == index) &&
479 (descr->caret_on) &&
480 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
481 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
482 dis.itemData = item ? item->data : 0;
483 dis.rcItem = *rect;
484 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
485 "state=%02x rect=%d,%d-%d,%d\n",
486 wnd->hwndSelf, index, item ? item->str : "", action,
487 dis.itemState, rect->left, rect->top,
488 rect->right, rect->bottom );
489 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
491 else
493 COLORREF oldText = 0, oldBk = 0;
495 if (action == ODA_FOCUS)
497 DrawFocusRect( hdc, rect );
498 return;
500 if (item && item->selected)
502 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
503 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
506 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
507 "rect=%d,%d-%d,%d\n",
508 wnd->hwndSelf, index, item ? item->str : "", action,
509 rect->left, rect->top, rect->right, rect->bottom );
510 if (!item)
511 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
512 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
513 else if (!(descr->style & LBS_USETABSTOPS))
514 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
515 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
516 strlen(item->str), NULL );
517 else
519 /* Output empty string to paint background in the full width. */
520 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
521 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
522 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
523 item->str, strlen(item->str),
524 descr->nb_tabs, descr->tabs, 0);
526 if (item && item->selected)
528 SetBkColor( hdc, oldBk );
529 SetTextColor( hdc, oldText );
531 if ((descr->focus_item == index) &&
532 (descr->caret_on) &&
533 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
538 /***********************************************************************
539 * LISTBOX_SetRedraw
541 * Change the redraw flag.
543 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
545 if (on)
547 if (!(descr->style & LBS_NOREDRAW)) return;
548 descr->style &= ~LBS_NOREDRAW;
549 LISTBOX_UpdateScroll( wnd, descr );
551 else descr->style |= LBS_NOREDRAW;
555 /***********************************************************************
556 * LISTBOX_RepaintItem
558 * Repaint a single item synchronously.
560 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
561 UINT action )
563 HDC hdc;
564 RECT rect;
565 HFONT oldFont = 0;
566 HBRUSH hbrush, oldBrush = 0;
568 if (descr->style & LBS_NOREDRAW) return;
569 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
570 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
571 if (descr->font) oldFont = SelectObject( hdc, descr->font );
572 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
573 hdc, (LPARAM)wnd->hwndSelf );
574 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
575 if (wnd->dwStyle & WS_DISABLED)
576 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
577 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
578 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
579 if (oldFont) SelectObject( hdc, oldFont );
580 if (oldBrush) SelectObject( hdc, oldBrush );
581 ReleaseDC( wnd->hwndSelf, hdc );
585 /***********************************************************************
586 * LISTBOX_InitStorage
588 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
589 DWORD bytes )
591 LB_ITEMDATA *item;
593 nb_items += LB_ARRAY_GRANULARITY - 1;
594 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
595 if (descr->items)
596 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
597 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
598 nb_items * sizeof(LB_ITEMDATA) )))
600 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
601 return LB_ERRSPACE;
603 descr->items = item;
604 return LB_OKAY;
608 /***********************************************************************
609 * LISTBOX_SetTabStops
611 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
612 LPINT tabs, BOOL short_ints )
614 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
615 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
616 if (!(descr->nb_tabs = count))
618 descr->tabs = NULL;
619 return TRUE;
621 /* FIXME: count = 1 */
622 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
623 descr->nb_tabs * sizeof(INT) )))
624 return FALSE;
625 if (short_ints)
627 INT i;
628 LPINT16 p = (LPINT16)tabs;
629 dbg_decl_str(listbox, 256);
631 for (i = 0; i < descr->nb_tabs; i++) {
632 descr->tabs[i] = *p++<<1; /* FIXME */
633 if(TRACE_ON(listbox))
634 dsprintf(listbox, "%hd ", descr->tabs[i]);
636 TRACE(listbox, "[%04x]: settabstops %s\n",
637 wnd->hwndSelf, dbg_str(listbox));
639 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
640 /* FIXME: repaint the window? */
641 return TRUE;
645 /***********************************************************************
646 * LISTBOX_GetText
648 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
649 LPSTR buffer )
651 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
652 if (HAS_STRINGS(descr))
654 if (!buffer)
655 return strlen(descr->items[index].str);
656 lstrcpyA( buffer, descr->items[index].str );
657 return strlen(buffer);
658 } else {
659 if (buffer)
660 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
661 return sizeof(DWORD);
666 /***********************************************************************
667 * LISTBOX_FindStringPos
669 * Find the nearest string located before a given string in sort order.
670 * If 'exact' is TRUE, return an error if we don't get an exact match.
672 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
673 BOOL exact )
675 INT index, min, max, res = -1;
677 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
678 min = 0;
679 max = descr->nb_items;
680 while (min != max)
682 index = (min + max) / 2;
683 if (HAS_STRINGS(descr))
684 res = lstrcmpiA( descr->items[index].str, str );
685 else
687 COMPAREITEMSTRUCT cis;
688 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
690 cis.CtlType = ODT_LISTBOX;
691 cis.CtlID = id;
692 cis.hwndItem = wnd->hwndSelf;
693 cis.itemID1 = index;
694 cis.itemData1 = descr->items[index].data;
695 cis.itemID2 = -1;
696 cis.itemData2 = (DWORD)str;
697 cis.dwLocaleId = descr->locale;
698 res = SendMessageA( descr->owner, WM_COMPAREITEM,
699 id, (LPARAM)&cis );
701 if (!res) return index;
702 if (res > 0) max = index;
703 else min = index + 1;
705 return exact ? -1 : max;
709 /***********************************************************************
710 * LISTBOX_FindFileStrPos
712 * Find the nearest string located before a given string in directory
713 * sort order (i.e. first files, then directories, then drives).
715 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
717 INT min, max, res = -1;
719 if (!HAS_STRINGS(descr))
720 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
721 min = 0;
722 max = descr->nb_items;
723 while (min != max)
725 INT index = (min + max) / 2;
726 const char *p = descr->items[index].str;
727 if (*p == '[') /* drive or directory */
729 if (*str != '[') res = -1;
730 else if (p[1] == '-') /* drive */
732 if (str[1] == '-') res = str[2] - p[2];
733 else res = -1;
735 else /* directory */
737 if (str[1] == '-') res = 1;
738 else res = lstrcmpiA( str, p );
741 else /* filename */
743 if (*str == '[') res = 1;
744 else res = lstrcmpiA( str, p );
746 if (!res) return index;
747 if (res < 0) max = index;
748 else min = index + 1;
750 return max;
754 /***********************************************************************
755 * LISTBOX_FindString
757 * Find the item beginning with a given string.
759 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
760 LPCSTR str, BOOL exact )
762 INT i;
763 LB_ITEMDATA *item;
765 if (start >= descr->nb_items) start = -1;
766 item = descr->items + start + 1;
767 if (HAS_STRINGS(descr))
769 if (!str) return LB_ERR;
770 if (exact)
772 for (i = start + 1; i < descr->nb_items; i++, item++)
773 if (!lstrcmpiA( str, item->str )) return i;
774 for (i = 0, item = descr->items; i <= start; i++, item++)
775 if (!lstrcmpiA( str, item->str )) return i;
777 else
779 /* Special case for drives and directories: ignore prefix */
780 #define CHECK_DRIVE(item) \
781 if ((item)->str[0] == '[') \
783 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
784 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
785 return i; \
788 INT len = strlen(str);
789 for (i = start + 1; i < descr->nb_items; i++, item++)
791 if (!lstrncmpiA( str, item->str, len )) return i;
792 CHECK_DRIVE(item);
794 for (i = 0, item = descr->items; i <= start; i++, item++)
796 if (!lstrncmpiA( str, item->str, len )) return i;
797 CHECK_DRIVE(item);
799 #undef CHECK_DRIVE
802 else
804 if (exact && (descr->style & LBS_SORT))
805 /* If sorted, use a WM_COMPAREITEM binary search */
806 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
808 /* Otherwise use a linear search */
809 for (i = start + 1; i < descr->nb_items; i++, item++)
810 if (item->data == (DWORD)str) return i;
811 for (i = 0, item = descr->items; i <= start; i++, item++)
812 if (item->data == (DWORD)str) return i;
814 return LB_ERR;
818 /***********************************************************************
819 * LISTBOX_GetSelCount
821 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
823 INT i, count;
824 LB_ITEMDATA *item = descr->items;
826 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
827 for (i = count = 0; i < descr->nb_items; i++, item++)
828 if (item->selected) count++;
829 return count;
833 /***********************************************************************
834 * LISTBOX_GetSelItems16
836 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
837 LPINT16 array )
839 INT i, count;
840 LB_ITEMDATA *item = descr->items;
842 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
843 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
844 if (item->selected) array[count++] = (INT16)i;
845 return count;
849 /***********************************************************************
850 * LISTBOX_GetSelItems32
852 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
853 LPINT array )
855 INT i, count;
856 LB_ITEMDATA *item = descr->items;
858 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
859 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
860 if (item->selected) array[count++] = i;
861 return count;
865 /***********************************************************************
866 * LISTBOX_Paint
868 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
870 INT i, col_pos = descr->page_size - 1;
871 RECT rect;
872 HFONT oldFont = 0;
873 HBRUSH hbrush, oldBrush = 0;
875 SetRect( &rect, 0, 0, descr->width, descr->height );
876 if (descr->style & LBS_NOREDRAW) return 0;
877 if (descr->style & LBS_MULTICOLUMN)
878 rect.right = rect.left + descr->column_width;
879 else if (descr->horz_pos)
881 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
882 rect.right += descr->horz_pos;
885 if (descr->font) oldFont = SelectObject( hdc, descr->font );
886 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
887 hdc, (LPARAM)wnd->hwndSelf );
888 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
889 if (wnd->dwStyle & WS_DISABLED)
890 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
892 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
893 (GetFocus() == wnd->hwndSelf))
895 /* Special case for empty listbox: paint focus rect */
896 rect.bottom = rect.top + descr->item_height;
897 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
898 ODA_FOCUS );
899 rect.top = rect.bottom;
902 for (i = descr->top_item; i < descr->nb_items; i++)
904 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
905 rect.bottom = rect.top + descr->item_height;
906 else
907 rect.bottom = rect.top + descr->items[i].height;
909 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
910 rect.top = rect.bottom;
912 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
914 if (!IS_OWNERDRAW(descr))
916 /* Clear the bottom of the column */
917 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
918 if (rect.top < descr->height)
920 rect.bottom = descr->height;
921 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
922 &rect, NULL, 0, NULL );
926 /* Go to the next column */
927 rect.left += descr->column_width;
928 rect.right += descr->column_width;
929 rect.top = 0;
930 col_pos = descr->page_size - 1;
932 else
934 col_pos--;
935 if (rect.top >= descr->height) break;
939 if (!IS_OWNERDRAW(descr))
941 /* Clear the remainder of the client area */
942 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
943 if (rect.top < descr->height)
945 rect.bottom = descr->height;
946 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
947 &rect, NULL, 0, NULL );
949 if (rect.right < descr->width)
951 rect.left = rect.right;
952 rect.right = descr->width;
953 rect.top = 0;
954 rect.bottom = descr->height;
955 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
956 &rect, NULL, 0, NULL );
959 if (oldFont) SelectObject( hdc, oldFont );
960 if (oldBrush) SelectObject( hdc, oldBrush );
961 return 0;
965 /***********************************************************************
966 * LISTBOX_InvalidateItems
968 * Invalidate all items from a given item. If the specified item is not
969 * visible, nothing happens.
971 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
973 RECT rect;
975 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
977 rect.bottom = descr->height;
978 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
979 if (descr->style & LBS_MULTICOLUMN)
981 /* Repaint the other columns */
982 rect.left = rect.right;
983 rect.right = descr->width;
984 rect.top = 0;
985 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
991 /***********************************************************************
992 * LISTBOX_GetItemHeight
994 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
996 if (descr->style & LBS_OWNERDRAWVARIABLE)
998 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
999 return descr->items[index].height;
1001 else return descr->item_height;
1005 /***********************************************************************
1006 * LISTBOX_SetItemHeight
1008 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1009 UINT height )
1011 if (!height) height = 1;
1013 if (descr->style & LBS_OWNERDRAWVARIABLE)
1015 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1016 TRACE(listbox, "[%04x]: item %d height = %d\n",
1017 wnd->hwndSelf, index, height );
1018 descr->items[index].height = height;
1019 LISTBOX_UpdateScroll( wnd, descr );
1020 LISTBOX_InvalidateItems( wnd, descr, index );
1022 else if (height != descr->item_height)
1024 TRACE(listbox, "[%04x]: new height = %d\n",
1025 wnd->hwndSelf, height );
1026 descr->item_height = height;
1027 LISTBOX_UpdatePage( wnd, descr );
1028 LISTBOX_UpdateScroll( wnd, descr );
1029 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1031 return LB_OKAY;
1035 /***********************************************************************
1036 * LISTBOX_SetHorizontalPos
1038 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1040 INT diff;
1042 if (pos > descr->horz_extent - descr->width)
1043 pos = descr->horz_extent - descr->width;
1044 if (pos < 0) pos = 0;
1045 if (!(diff = descr->horz_pos - pos)) return;
1046 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1047 wnd->hwndSelf, pos );
1048 descr->horz_pos = pos;
1049 LISTBOX_UpdateScroll( wnd, descr );
1050 if (abs(diff) < descr->width)
1051 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1052 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1053 else
1054 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1058 /***********************************************************************
1059 * LISTBOX_SetHorizontalExtent
1061 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1062 UINT extent )
1064 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1065 return LB_OKAY;
1066 if (extent <= 0) extent = 1;
1067 if (extent == descr->horz_extent) return LB_OKAY;
1068 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1069 wnd->hwndSelf, extent );
1070 descr->horz_extent = extent;
1071 if (descr->horz_pos > extent - descr->width)
1072 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1073 else
1074 LISTBOX_UpdateScroll( wnd, descr );
1075 return LB_OKAY;
1079 /***********************************************************************
1080 * LISTBOX_SetColumnWidth
1082 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1084 width += 2; /* For left and right margin */
1085 if (width == descr->column_width) return LB_OKAY;
1086 TRACE(listbox, "[%04x]: new column width = %d\n",
1087 wnd->hwndSelf, width );
1088 descr->column_width = width;
1089 LISTBOX_UpdatePage( wnd, descr );
1090 return LB_OKAY;
1094 /***********************************************************************
1095 * LISTBOX_SetFont
1097 * Returns the item height.
1099 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1101 HDC hdc;
1102 HFONT oldFont = 0;
1103 TEXTMETRICA tm;
1105 descr->font = font;
1107 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1109 ERR(listbox, "unable to get DC.\n" );
1110 return 16;
1112 if (font) oldFont = SelectObject( hdc, font );
1113 GetTextMetricsA( hdc, &tm );
1114 if (oldFont) SelectObject( hdc, oldFont );
1115 ReleaseDC( wnd->hwndSelf, hdc );
1116 if (!IS_OWNERDRAW(descr))
1117 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1118 return tm.tmHeight ;
1122 /***********************************************************************
1123 * LISTBOX_MakeItemVisible
1125 * Make sure that a given item is partially or fully visible.
1127 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1128 BOOL fully )
1130 INT top;
1132 if (index <= descr->top_item) top = index;
1133 else if (descr->style & LBS_MULTICOLUMN)
1135 INT cols = descr->width;
1136 if (!fully) cols += descr->column_width - 1;
1137 if (cols >= descr->column_width) cols /= descr->column_width;
1138 else cols = 1;
1139 if (index < descr->top_item + (descr->page_size * cols)) return;
1140 top = index - descr->page_size * (cols - 1);
1142 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1144 INT height = fully ? descr->items[index].height : 1;
1145 for (top = index; top > descr->top_item; top--)
1146 if ((height += descr->items[top-1].height) > descr->height) break;
1148 else
1150 if (index < descr->top_item + descr->page_size) return;
1151 if (!fully && (index == descr->top_item + descr->page_size) &&
1152 (descr->height > (descr->page_size * descr->item_height))) return;
1153 top = index - descr->page_size + 1;
1155 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1159 /***********************************************************************
1160 * LISTBOX_SelectItemRange
1162 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1164 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1165 INT last, BOOL on )
1167 INT i;
1169 /* A few sanity checks */
1171 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1172 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1173 if (last == -1) last = descr->nb_items - 1;
1174 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1175 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1176 /* selected_item reflects last selected/unselected item on multiple sel */
1177 descr->selected_item = last;
1179 if (on) /* Turn selection on */
1181 for (i = first; i <= last; i++)
1183 if (descr->items[i].selected) continue;
1184 descr->items[i].selected = TRUE;
1185 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1188 else /* Turn selection off */
1190 for (i = first; i <= last; i++)
1192 if (!descr->items[i].selected) continue;
1193 descr->items[i].selected = FALSE;
1194 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1197 return LB_OKAY;
1201 /***********************************************************************
1202 * LISTBOX_SetCaretIndex
1204 * NOTES
1205 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1208 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1209 BOOL fully_visible )
1211 INT oldfocus = descr->focus_item;
1213 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1214 if (index == oldfocus) return LB_OKAY;
1215 descr->focus_item = index;
1216 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1217 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1219 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1220 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1221 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1223 return LB_OKAY;
1227 /***********************************************************************
1228 * LISTBOX_SetSelection
1230 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1231 BOOL on, BOOL send_notify )
1233 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1234 if (descr->style & LBS_MULTIPLESEL)
1236 if (index == -1) /* Select all items */
1237 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1238 else /* Only one item */
1239 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1241 else
1243 INT oldsel = descr->selected_item;
1244 if (index == oldsel) return LB_OKAY;
1245 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1246 if (index != -1) descr->items[index].selected = TRUE;
1247 descr->selected_item = index;
1248 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1249 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1250 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1251 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1252 else
1253 if( descr->lphc ) /* set selection change flag for parent combo */
1254 descr->lphc->wState |= CBF_SELCHANGE;
1256 return LB_OKAY;
1260 /***********************************************************************
1261 * LISTBOX_MoveCaret
1263 * Change the caret position and extend the selection to the new caret.
1265 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1266 BOOL fully_visible )
1268 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1269 if (descr->style & LBS_EXTENDEDSEL)
1271 if (descr->anchor_item != -1)
1273 INT first = MIN( descr->focus_item, descr->anchor_item );
1274 INT last = MAX( descr->focus_item, descr->anchor_item );
1275 if (first > 0)
1276 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1277 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1278 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1281 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1283 /* Set selection to new caret item */
1284 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1289 /***********************************************************************
1290 * LISTBOX_InsertItem
1292 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1293 LPSTR str, DWORD data )
1295 LB_ITEMDATA *item;
1296 INT max_items;
1298 if (index == -1) index = descr->nb_items;
1299 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1300 if (!descr->items) max_items = 0;
1301 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1302 if (descr->nb_items == max_items)
1304 /* We need to grow the array */
1305 max_items += LB_ARRAY_GRANULARITY;
1306 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1307 max_items * sizeof(LB_ITEMDATA) )))
1309 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1310 return LB_ERRSPACE;
1312 descr->items = item;
1315 /* Insert the item structure */
1317 item = &descr->items[index];
1318 if (index < descr->nb_items)
1319 RtlMoveMemory( item + 1, item,
1320 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1321 item->str = str;
1322 item->data = data;
1323 item->height = 0;
1324 item->selected = FALSE;
1325 descr->nb_items++;
1327 /* Get item height */
1329 if (descr->style & LBS_OWNERDRAWVARIABLE)
1331 MEASUREITEMSTRUCT mis;
1332 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1334 mis.CtlType = ODT_LISTBOX;
1335 mis.CtlID = id;
1336 mis.itemID = index;
1337 mis.itemData = descr->items[index].data;
1338 mis.itemHeight = descr->item_height;
1339 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1340 item->height = mis.itemHeight ? mis.itemHeight : 1;
1341 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1342 wnd->hwndSelf, index, str ? str : "", item->height );
1345 /* Repaint the items */
1347 LISTBOX_UpdateScroll( wnd, descr );
1348 LISTBOX_InvalidateItems( wnd, descr, index );
1350 /* Move selection and focused item */
1352 if (index <= descr->selected_item) descr->selected_item++;
1353 if (index <= descr->focus_item)
1355 descr->focus_item++;
1356 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1359 /* If listbox was empty, set focus to the first item */
1361 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1362 return LB_OKAY;
1366 /***********************************************************************
1367 * LISTBOX_InsertString
1369 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1370 LPCSTR str )
1372 LPSTR new_str = NULL;
1373 DWORD data = 0;
1374 LRESULT ret;
1376 if (HAS_STRINGS(descr))
1378 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1380 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1381 return LB_ERRSPACE;
1384 else data = (DWORD)str;
1386 if (index == -1) index = descr->nb_items;
1387 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1389 if (new_str) HeapFree( descr->heap, 0, new_str );
1390 return ret;
1393 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1394 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1395 return index;
1399 /***********************************************************************
1400 * LISTBOX_DeleteItem
1402 * Delete the content of an item. 'index' must be a valid index.
1404 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1406 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1407 * while Win95 sends it for all items with user data.
1408 * It's probably better to send it too often than not
1409 * often enough, so this is what we do here.
1411 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1413 DELETEITEMSTRUCT dis;
1414 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1416 dis.CtlType = ODT_LISTBOX;
1417 dis.CtlID = id;
1418 dis.itemID = index;
1419 dis.hwndItem = wnd->hwndSelf;
1420 dis.itemData = descr->items[index].data;
1421 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1423 if (HAS_STRINGS(descr) && descr->items[index].str)
1424 HeapFree( descr->heap, 0, descr->items[index].str );
1428 /***********************************************************************
1429 * LISTBOX_RemoveItem
1431 * Remove an item from the listbox and delete its content.
1433 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1435 LB_ITEMDATA *item;
1436 INT max_items;
1438 if (index == -1) index = descr->nb_items - 1;
1439 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1440 LISTBOX_DeleteItem( wnd, descr, index );
1442 /* Remove the item */
1444 item = &descr->items[index];
1445 if (index < descr->nb_items-1)
1446 RtlMoveMemory( item, item + 1,
1447 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1448 descr->nb_items--;
1449 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1451 /* Shrink the item array if possible */
1453 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1454 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1456 max_items -= LB_ARRAY_GRANULARITY;
1457 item = HeapReAlloc( descr->heap, 0, descr->items,
1458 max_items * sizeof(LB_ITEMDATA) );
1459 if (item) descr->items = item;
1462 /* Repaint the items */
1464 LISTBOX_UpdateScroll( wnd, descr );
1465 LISTBOX_InvalidateItems( wnd, descr, index );
1467 /* Move selection and focused item */
1469 if (index <= descr->selected_item) descr->selected_item--;
1470 if (index <= descr->focus_item)
1472 descr->focus_item--;
1473 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1475 return LB_OKAY;
1479 /***********************************************************************
1480 * LISTBOX_ResetContent
1482 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1484 INT i;
1486 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1487 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1488 descr->nb_items = 0;
1489 descr->top_item = 0;
1490 descr->selected_item = -1;
1491 descr->focus_item = 0;
1492 descr->anchor_item = -1;
1493 descr->items = NULL;
1494 LISTBOX_UpdateScroll( wnd, descr );
1495 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1499 /***********************************************************************
1500 * LISTBOX_SetCount
1502 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1504 LRESULT ret;
1506 if (HAS_STRINGS(descr)) return LB_ERR;
1507 /* FIXME: this is far from optimal... */
1508 if (count > descr->nb_items)
1510 while (count > descr->nb_items)
1511 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1512 return ret;
1514 else if (count < descr->nb_items)
1516 while (count < descr->nb_items)
1517 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1518 return ret;
1520 return LB_OKAY;
1524 /***********************************************************************
1525 * LISTBOX_Directory
1527 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1528 LPCSTR filespec, BOOL long_names )
1530 HANDLE handle;
1531 LRESULT ret = LB_OKAY;
1532 WIN32_FIND_DATAA entry;
1533 int pos;
1535 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1537 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1539 else
1543 char buffer[270];
1544 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1546 if (!(attrib & DDL_DIRECTORY) ||
1547 !strcmp( entry.cAlternateFileName, "." )) continue;
1548 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1549 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1551 else /* not a directory */
1553 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1554 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1556 if ((attrib & DDL_EXCLUSIVE) &&
1557 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1558 continue;
1559 #undef ATTRIBS
1560 if (long_names) strcpy( buffer, entry.cFileName );
1561 else strcpy( buffer, entry.cAlternateFileName );
1563 if (!long_names) CharLowerA( buffer );
1564 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1565 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1566 break;
1567 } while (FindNextFileA( handle, &entry ));
1568 FindClose( handle );
1571 if ((ret >= 0) && (attrib & DDL_DRIVES))
1573 char buffer[] = "[-a-]";
1574 int drive;
1575 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1577 if (!DRIVE_IsValid(drive)) continue;
1578 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1579 break;
1582 return ret;
1586 /***********************************************************************
1587 * LISTBOX_HandleVScroll
1589 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1590 WPARAM wParam, LPARAM lParam )
1592 SCROLLINFO info;
1594 if (descr->style & LBS_MULTICOLUMN) return 0;
1595 switch(LOWORD(wParam))
1597 case SB_LINEUP:
1598 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1599 break;
1600 case SB_LINEDOWN:
1601 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1602 break;
1603 case SB_PAGEUP:
1604 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1605 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1606 break;
1607 case SB_PAGEDOWN:
1608 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1609 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1610 break;
1611 case SB_THUMBPOSITION:
1612 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1613 break;
1614 case SB_THUMBTRACK:
1615 info.cbSize = sizeof(info);
1616 info.fMask = SIF_TRACKPOS;
1617 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1618 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1619 break;
1620 case SB_TOP:
1621 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1622 break;
1623 case SB_BOTTOM:
1624 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1625 break;
1627 return 0;
1631 /***********************************************************************
1632 * LISTBOX_HandleHScroll
1634 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1635 WPARAM wParam, LPARAM lParam )
1637 SCROLLINFO info;
1638 INT page;
1640 if (descr->style & LBS_MULTICOLUMN)
1642 switch(LOWORD(wParam))
1644 case SB_LINELEFT:
1645 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1646 TRUE );
1647 break;
1648 case SB_LINERIGHT:
1649 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1650 TRUE );
1651 break;
1652 case SB_PAGELEFT:
1653 page = descr->width / descr->column_width;
1654 if (page < 1) page = 1;
1655 LISTBOX_SetTopItem( wnd, descr,
1656 descr->top_item - page * descr->page_size, TRUE );
1657 break;
1658 case SB_PAGERIGHT:
1659 page = descr->width / descr->column_width;
1660 if (page < 1) page = 1;
1661 LISTBOX_SetTopItem( wnd, descr,
1662 descr->top_item + page * descr->page_size, TRUE );
1663 break;
1664 case SB_THUMBPOSITION:
1665 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1666 TRUE );
1667 break;
1668 case SB_THUMBTRACK:
1669 info.cbSize = sizeof(info);
1670 info.fMask = SIF_TRACKPOS;
1671 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1672 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1673 TRUE );
1674 break;
1675 case SB_LEFT:
1676 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1677 break;
1678 case SB_RIGHT:
1679 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1680 break;
1683 else if (descr->horz_extent)
1685 switch(LOWORD(wParam))
1687 case SB_LINELEFT:
1688 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1689 break;
1690 case SB_LINERIGHT:
1691 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1692 break;
1693 case SB_PAGELEFT:
1694 LISTBOX_SetHorizontalPos( wnd, descr,
1695 descr->horz_pos - descr->width );
1696 break;
1697 case SB_PAGERIGHT:
1698 LISTBOX_SetHorizontalPos( wnd, descr,
1699 descr->horz_pos + descr->width );
1700 break;
1701 case SB_THUMBPOSITION:
1702 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1703 break;
1704 case SB_THUMBTRACK:
1705 info.cbSize = sizeof(info);
1706 info.fMask = SIF_TRACKPOS;
1707 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1708 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1709 break;
1710 case SB_LEFT:
1711 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1712 break;
1713 case SB_RIGHT:
1714 LISTBOX_SetHorizontalPos( wnd, descr,
1715 descr->horz_extent - descr->width );
1716 break;
1719 return 0;
1723 /***********************************************************************
1724 * LISTBOX_HandleLButtonDown
1726 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1727 WPARAM wParam, INT x, INT y )
1729 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1730 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1731 wnd->hwndSelf, x, y, index );
1732 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1733 if (index != -1)
1735 if (descr->style & LBS_EXTENDEDSEL)
1737 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1738 if (wParam & MK_CONTROL)
1740 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1741 LISTBOX_SetSelection( wnd, descr, index,
1742 !descr->items[index].selected, FALSE );
1744 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1746 else
1748 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1749 LISTBOX_SetSelection( wnd, descr, index,
1750 (!(descr->style & LBS_MULTIPLESEL) ||
1751 !descr->items[index].selected), FALSE );
1755 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1756 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1757 : descr->lphc->self->hwndSelf ) ;
1759 descr->captured = TRUE;
1760 SetCapture( wnd->hwndSelf );
1761 if (index != -1 && !descr->lphc)
1763 if (descr->style & LBS_NOTIFY )
1764 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1765 MAKELPARAM( x, y ) );
1766 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1768 POINT pt;
1770 pt.x = x;
1771 pt.y = y;
1773 if (DragDetect( wnd->hwndSelf, pt ))
1774 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1777 return 0;
1781 /***********************************************************************
1782 * LISTBOX_HandleLButtonUp
1784 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1786 if (LISTBOX_Timer != LB_TIMER_NONE)
1787 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1788 LISTBOX_Timer = LB_TIMER_NONE;
1789 if (descr->captured)
1791 descr->captured = FALSE;
1792 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1793 if (descr->style & LBS_NOTIFY)
1794 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1796 return 0;
1800 /***********************************************************************
1801 * LISTBOX_HandleTimer
1803 * Handle scrolling upon a timer event.
1804 * Return TRUE if scrolling should continue.
1806 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1807 INT index, TIMER_DIRECTION dir )
1809 switch(dir)
1811 case LB_TIMER_UP:
1812 if (descr->top_item) index = descr->top_item - 1;
1813 else index = 0;
1814 break;
1815 case LB_TIMER_LEFT:
1816 if (descr->top_item) index -= descr->page_size;
1817 break;
1818 case LB_TIMER_DOWN:
1819 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1820 if (index == descr->focus_item) index++;
1821 if (index >= descr->nb_items) index = descr->nb_items - 1;
1822 break;
1823 case LB_TIMER_RIGHT:
1824 if (index + descr->page_size < descr->nb_items)
1825 index += descr->page_size;
1826 break;
1827 case LB_TIMER_NONE:
1828 break;
1830 if (index == descr->focus_item) return FALSE;
1831 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1832 return TRUE;
1836 /***********************************************************************
1837 * LISTBOX_HandleSystemTimer
1839 * WM_SYSTIMER handler.
1841 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1843 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1845 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1846 LISTBOX_Timer = LB_TIMER_NONE;
1848 return 0;
1852 /***********************************************************************
1853 * LISTBOX_HandleMouseMove
1855 * WM_MOUSEMOVE handler.
1857 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1858 INT x, INT y )
1860 INT index;
1861 TIMER_DIRECTION dir;
1863 if (!descr->captured) return;
1865 if (descr->style & LBS_MULTICOLUMN)
1867 if (y < 0) y = 0;
1868 else if (y >= descr->item_height * descr->page_size)
1869 y = descr->item_height * descr->page_size - 1;
1871 if (x < 0)
1873 dir = LB_TIMER_LEFT;
1874 x = 0;
1876 else if (x >= descr->width)
1878 dir = LB_TIMER_RIGHT;
1879 x = descr->width - 1;
1881 else dir = LB_TIMER_NONE; /* inside */
1883 else
1885 if (y < 0) dir = LB_TIMER_UP; /* above */
1886 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1887 else dir = LB_TIMER_NONE; /* inside */
1890 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1891 if (index == -1) index = descr->focus_item;
1892 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1894 /* Start/stop the system timer */
1896 if (dir != LB_TIMER_NONE)
1897 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1898 else if (LISTBOX_Timer != LB_TIMER_NONE)
1899 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1900 LISTBOX_Timer = dir;
1904 /***********************************************************************
1905 * LISTBOX_HandleKeyDown
1907 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1909 INT caret = -1;
1910 if (descr->style & LBS_WANTKEYBOARDINPUT)
1912 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1913 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1914 wnd->hwndSelf );
1915 if (caret == -2) return 0;
1917 if (caret == -1) switch(wParam)
1919 case VK_LEFT:
1920 if (descr->style & LBS_MULTICOLUMN)
1922 if (descr->focus_item >= descr->page_size)
1923 caret = descr->focus_item - descr->page_size;
1924 break;
1926 /* fall through */
1927 case VK_UP:
1928 caret = descr->focus_item - 1;
1929 if (caret < 0) caret = 0;
1930 break;
1931 case VK_RIGHT:
1932 if (descr->style & LBS_MULTICOLUMN)
1934 if (descr->focus_item + descr->page_size < descr->nb_items)
1935 caret = descr->focus_item + descr->page_size;
1936 break;
1938 /* fall through */
1939 case VK_DOWN:
1940 caret = descr->focus_item + 1;
1941 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1942 break;
1943 case VK_PRIOR:
1944 if (descr->style & LBS_MULTICOLUMN)
1946 INT page = descr->width / descr->column_width;
1947 if (page < 1) page = 1;
1948 caret = descr->focus_item - (page * descr->page_size) + 1;
1950 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1951 if (caret < 0) caret = 0;
1952 break;
1953 case VK_NEXT:
1954 if (descr->style & LBS_MULTICOLUMN)
1956 INT page = descr->width / descr->column_width;
1957 if (page < 1) page = 1;
1958 caret = descr->focus_item + (page * descr->page_size) - 1;
1960 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1961 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1962 break;
1963 case VK_HOME:
1964 caret = 0;
1965 break;
1966 case VK_END:
1967 caret = descr->nb_items - 1;
1968 break;
1969 case VK_SPACE:
1970 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1971 else if (descr->style & LBS_MULTIPLESEL)
1973 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1974 !descr->items[descr->focus_item].selected,
1975 (descr->style & LBS_NOTIFY) != 0 );
1977 else if (descr->selected_item == -1)
1979 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1980 (descr->style & LBS_NOTIFY) != 0 );
1982 break;
1984 if (caret >= 0)
1986 if ((descr->style & LBS_EXTENDEDSEL) &&
1987 !(GetKeyState( VK_SHIFT ) & 0x8000))
1988 descr->anchor_item = caret;
1989 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1990 if (descr->style & LBS_NOTIFY)
1992 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1994 /* make sure that combo parent doesn't hide us */
1995 descr->lphc->wState |= CBF_NOROLLUP;
1997 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2000 return 0;
2004 /***********************************************************************
2005 * LISTBOX_HandleChar
2007 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2008 WPARAM wParam )
2010 INT caret = -1;
2011 char str[2];
2013 str[0] = wParam & 0xff;
2014 str[1] = '\0';
2016 if (descr->style & LBS_WANTKEYBOARDINPUT)
2018 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2019 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2020 wnd->hwndSelf );
2021 if (caret == -2) return 0;
2023 if (caret == -1)
2024 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2025 if (caret != -1)
2027 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2028 if (descr->style & LBS_NOTIFY)
2029 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2031 return 0;
2035 /***********************************************************************
2036 * LISTBOX_Create
2038 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2040 LB_DESCR *descr;
2041 MEASUREITEMSTRUCT mis;
2042 RECT rect;
2044 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2045 return FALSE;
2046 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2048 HeapFree( GetProcessHeap(), 0, descr );
2049 return FALSE;
2051 GetClientRect( wnd->hwndSelf, &rect );
2052 descr->owner = GetParent( wnd->hwndSelf );
2053 descr->style = wnd->dwStyle;
2054 descr->width = rect.right - rect.left;
2055 descr->height = rect.bottom - rect.top;
2056 descr->items = NULL;
2057 descr->nb_items = 0;
2058 descr->top_item = 0;
2059 descr->selected_item = -1;
2060 descr->focus_item = 0;
2061 descr->anchor_item = -1;
2062 descr->item_height = 1;
2063 descr->page_size = 1;
2064 descr->column_width = 150;
2065 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2066 descr->horz_pos = 0;
2067 descr->nb_tabs = 0;
2068 descr->tabs = NULL;
2069 descr->caret_on = TRUE;
2070 descr->captured = FALSE;
2071 descr->font = 0;
2072 descr->locale = 0; /* FIXME */
2073 descr->lphc = lphc;
2075 if( lphc )
2077 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2078 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2079 descr->owner = lphc->self->hwndSelf;
2082 *(LB_DESCR **)wnd->wExtra = descr;
2084 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2086 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2087 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2088 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2089 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2091 if (descr->style & LBS_OWNERDRAWFIXED)
2093 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2095 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2096 descr->item_height = lphc->fixedOwnerDrawHeight;
2098 else
2100 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2102 mis.CtlType = ODT_LISTBOX;
2103 mis.CtlID = id;
2104 mis.itemID = -1;
2105 mis.itemWidth = 0;
2106 mis.itemData = 0;
2107 mis.itemHeight = descr->item_height;
2108 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2109 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2113 return TRUE;
2117 /***********************************************************************
2118 * LISTBOX_Destroy
2120 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2122 LISTBOX_ResetContent( wnd, descr );
2123 HeapDestroy( descr->heap );
2124 HeapFree( GetProcessHeap(), 0, descr );
2125 wnd->wExtra[0] = 0;
2126 return TRUE;
2130 /***********************************************************************
2131 * ListBoxWndProc
2133 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2134 WPARAM wParam, LPARAM lParam )
2136 LRESULT ret;
2137 LB_DESCR *descr;
2138 WND *wnd = WIN_FindWndPtr( hwnd );
2139 LRESULT retvalue;
2141 if (!wnd) return 0;
2142 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2144 if (msg == WM_CREATE)
2146 if (!LISTBOX_Create( wnd, NULL ))
2148 retvalue = -1;
2149 goto END;
2151 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2152 hwnd, *(LB_DESCR **)wnd->wExtra );
2153 retvalue = 0;
2154 goto END;
2156 /* Ignore all other messages before we get a WM_CREATE */
2157 retvalue = DefWindowProcA( hwnd, msg, wParam, lParam );
2158 goto END;
2161 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2162 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2163 switch(msg)
2165 case LB_RESETCONTENT16:
2166 case LB_RESETCONTENT:
2167 LISTBOX_ResetContent( wnd, descr );
2168 retvalue = 0;
2169 goto END;
2171 case LB_ADDSTRING16:
2172 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2173 /* fall through */
2174 case LB_ADDSTRING:
2175 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2176 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2177 goto END;
2179 case LB_INSERTSTRING16:
2180 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2181 wParam = (INT)(INT16)wParam;
2182 /* fall through */
2183 case LB_INSERTSTRING:
2184 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2185 goto END;
2187 case LB_ADDFILE16:
2188 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2189 /* fall through */
2190 case LB_ADDFILE:
2191 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2192 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2193 goto END;
2195 case LB_DELETESTRING16:
2196 case LB_DELETESTRING:
2197 retvalue = LISTBOX_RemoveItem( wnd, descr, wParam );
2198 goto END;
2200 case LB_GETITEMDATA16:
2201 case LB_GETITEMDATA:
2202 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2204 retvalue = LB_ERR;
2205 goto END;
2207 retvalue = descr->items[wParam].data;
2208 goto END;
2210 case LB_SETITEMDATA16:
2211 case LB_SETITEMDATA:
2212 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2214 retvalue = LB_ERR;
2215 goto END;
2217 descr->items[wParam].data = (DWORD)lParam;
2218 retvalue = LB_OKAY;
2219 goto END;
2221 case LB_GETCOUNT16:
2222 case LB_GETCOUNT:
2223 retvalue = descr->nb_items;
2224 goto END;
2226 case LB_GETTEXT16:
2227 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2228 /* fall through */
2229 case LB_GETTEXT:
2230 retvalue = LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2231 goto END;
2233 case LB_GETTEXTLEN16:
2234 /* fall through */
2235 case LB_GETTEXTLEN:
2236 if (wParam >= descr->nb_items)
2238 retvalue = LB_ERR;
2239 goto END;
2241 retvalue = (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2242 : sizeof(DWORD));
2243 goto END;
2245 case LB_GETCURSEL16:
2246 case LB_GETCURSEL:
2247 if (descr->nb_items==0)
2248 retvalue = LB_ERR;
2249 else
2251 retvalue = descr->selected_item;
2252 if (retvalue == -1) retvalue = descr->focus_item;
2254 /* otherwise, if the user tries to move the selection with the */
2255 /* arrow keys, we will give the application something to choke on */
2256 goto END;
2258 case LB_GETTOPINDEX16:
2259 case LB_GETTOPINDEX:
2260 retvalue = descr->top_item;
2261 goto END;
2263 case LB_GETITEMHEIGHT16:
2264 case LB_GETITEMHEIGHT:
2265 retvalue = LISTBOX_GetItemHeight( wnd, descr, wParam );
2266 goto END;
2268 case LB_SETITEMHEIGHT16:
2269 lParam = LOWORD(lParam);
2270 /* fall through */
2271 case LB_SETITEMHEIGHT:
2272 retvalue = LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2273 goto END;
2275 case LB_ITEMFROMPOINT:
2277 POINT pt;
2278 RECT rect;
2280 pt.x = LOWORD(lParam);
2281 pt.y = HIWORD(lParam);
2282 rect.left = 0;
2283 rect.top = 0;
2284 rect.right = descr->width;
2285 rect.bottom = descr->height;
2287 retvalue = MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2288 PtInRect( &rect, pt ) );
2289 goto END;
2292 case LB_SETCARETINDEX16:
2293 case LB_SETCARETINDEX:
2294 retvalue = LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2295 goto END;
2297 case LB_GETCARETINDEX16:
2298 case LB_GETCARETINDEX:
2299 retvalue = descr->focus_item;
2300 goto END;
2302 case LB_SETTOPINDEX16:
2303 case LB_SETTOPINDEX:
2304 retvalue = LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2305 goto END;
2307 case LB_SETCOLUMNWIDTH16:
2308 case LB_SETCOLUMNWIDTH:
2309 retvalue = LISTBOX_SetColumnWidth( wnd, descr, wParam );
2310 goto END;
2312 case LB_GETITEMRECT16:
2314 RECT rect;
2315 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2316 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2318 retvalue = ret;
2319 goto END;
2321 case LB_GETITEMRECT:
2322 retvalue = LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2323 goto END;
2325 case LB_FINDSTRING16:
2326 wParam = (INT)(INT16)wParam;
2327 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2328 /* fall through */
2329 case LB_FINDSTRING:
2330 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2331 goto END;
2333 case LB_FINDSTRINGEXACT16:
2334 wParam = (INT)(INT16)wParam;
2335 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2336 /* fall through */
2337 case LB_FINDSTRINGEXACT:
2338 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2339 goto END;
2341 case LB_SELECTSTRING16:
2342 wParam = (INT)(INT16)wParam;
2343 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2344 /* fall through */
2345 case LB_SELECTSTRING:
2347 INT index = LISTBOX_FindString( wnd, descr, wParam,
2348 (LPCSTR)lParam, FALSE );
2349 if (index == LB_ERR)
2351 retvalue = LB_ERR;
2352 goto END;
2354 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2355 retvalue = index;
2356 goto END;
2359 case LB_GETSEL16:
2360 wParam = (INT)(INT16)wParam;
2361 /* fall through */
2362 case LB_GETSEL:
2363 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2365 retvalue = LB_ERR;
2366 goto END;
2368 retvalue = descr->items[wParam].selected;
2369 goto END;
2371 case LB_SETSEL16:
2372 lParam = (INT)(INT16)lParam;
2373 /* fall through */
2374 case LB_SETSEL:
2375 retvalue = LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2376 goto END;
2378 case LB_SETCURSEL16:
2379 wParam = (INT)(INT16)wParam;
2380 /* fall through */
2381 case LB_SETCURSEL:
2382 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2383 retvalue = LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2384 goto END;
2386 case LB_GETSELCOUNT16:
2387 case LB_GETSELCOUNT:
2388 retvalue = LISTBOX_GetSelCount( wnd, descr );
2389 goto END;
2391 case LB_GETSELITEMS16:
2392 retvalue = LISTBOX_GetSelItems16( wnd, descr, wParam,
2393 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2394 goto END;
2396 case LB_GETSELITEMS:
2397 retvalue = LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2398 goto END;
2400 case LB_SELITEMRANGE16:
2401 case LB_SELITEMRANGE:
2402 if (LOWORD(lParam) <= HIWORD(lParam))
2404 retvalue = LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2405 HIWORD(lParam), wParam );
2407 else
2409 retvalue = LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2410 LOWORD(lParam), wParam );
2412 goto END;
2414 case LB_SELITEMRANGEEX16:
2415 case LB_SELITEMRANGEEX:
2416 if ((INT)lParam >= (INT)wParam)
2417 retvalue = LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2418 else
2419 retvalue = LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2420 goto END;
2422 case LB_GETHORIZONTALEXTENT16:
2423 case LB_GETHORIZONTALEXTENT:
2424 retvalue = descr->horz_extent;
2425 goto END;
2427 case LB_SETHORIZONTALEXTENT16:
2428 case LB_SETHORIZONTALEXTENT:
2429 retvalue = LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2430 goto END;
2432 case LB_GETANCHORINDEX16:
2433 case LB_GETANCHORINDEX:
2434 retvalue = descr->anchor_item;
2435 goto END;
2437 case LB_SETANCHORINDEX16:
2438 wParam = (INT)(INT16)wParam;
2439 /* fall through */
2440 case LB_SETANCHORINDEX:
2441 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2443 retvalue = LB_ERR;
2444 goto END;
2446 descr->anchor_item = (INT)wParam;
2447 retvalue = LB_OKAY;
2448 goto END;
2450 case LB_DIR16:
2451 retvalue = LISTBOX_Directory( wnd, descr, wParam,
2452 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2453 goto END;
2455 case LB_DIR:
2456 retvalue = LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2457 goto END;
2459 case LB_GETLOCALE:
2460 retvalue = descr->locale;
2461 goto END;
2463 case LB_SETLOCALE:
2464 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2465 retvalue = LB_OKAY;
2466 goto END;
2468 case LB_INITSTORAGE:
2469 retvalue = LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2470 goto END;
2472 case LB_SETCOUNT:
2473 retvalue = LISTBOX_SetCount( wnd, descr, (INT)wParam );
2474 goto END;
2476 case LB_SETTABSTOPS16:
2477 retvalue = LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2478 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2479 goto END;
2481 case LB_SETTABSTOPS:
2482 retvalue = LISTBOX_SetTabStops( wnd, descr, wParam,
2483 (LPINT)lParam, FALSE );
2484 goto END;
2486 case LB_CARETON16:
2487 case LB_CARETON:
2488 if (descr->caret_on)
2490 retvalue = LB_OKAY;
2491 goto END;
2493 descr->caret_on = TRUE;
2494 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2495 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2496 retvalue = LB_OKAY;
2497 goto END;
2499 case LB_CARETOFF16:
2500 case LB_CARETOFF:
2501 if (!descr->caret_on)
2503 retvalue = LB_OKAY;
2504 goto END;
2506 descr->caret_on = FALSE;
2507 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2508 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2509 retvalue = LB_OKAY;
2510 goto END;
2512 case WM_DESTROY:
2513 retvalue = LISTBOX_Destroy( wnd, descr );
2514 goto END;
2516 case WM_ENABLE:
2517 InvalidateRect( hwnd, NULL, TRUE );
2518 retvalue = 0;
2519 goto END;
2521 case WM_SETREDRAW:
2522 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2523 retvalue = 0;
2524 goto END;
2526 case WM_GETDLGCODE:
2527 retvalue =DLGC_WANTARROWS | DLGC_WANTCHARS;
2528 goto END;
2529 case WM_PAINT:
2531 PAINTSTRUCT ps;
2532 HDC hdc = ( wParam ) ? ((HDC)wParam)
2533 : BeginPaint( hwnd, &ps );
2534 ret = LISTBOX_Paint( wnd, descr, hdc );
2535 if( !wParam ) EndPaint( hwnd, &ps );
2537 retvalue =ret;
2538 goto END;
2539 case WM_SIZE:
2540 LISTBOX_UpdateSize( wnd, descr );
2541 retvalue =0;
2542 goto END;
2543 case WM_GETFONT:
2544 retvalue =descr->font;
2545 goto END;
2546 case WM_SETFONT:
2547 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2548 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2549 retvalue =0;
2550 goto END;
2551 case WM_SETFOCUS:
2552 descr->caret_on = TRUE;
2553 if (descr->focus_item != -1)
2554 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2555 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2556 retvalue =0;
2557 goto END;
2558 case WM_KILLFOCUS:
2559 if ((descr->focus_item != -1) && descr->caret_on)
2560 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2561 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2562 retvalue =0;
2563 goto END;
2564 case WM_HSCROLL:
2565 retvalue =LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2566 goto END;
2567 case WM_VSCROLL:
2568 retvalue =LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2569 goto END;
2570 case WM_LBUTTONDOWN:
2571 retvalue =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2572 (INT16)LOWORD(lParam),
2573 (INT16)HIWORD(lParam) );
2574 goto END;
2575 case WM_LBUTTONDBLCLK:
2576 if (descr->style & LBS_NOTIFY)
2577 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2578 retvalue =0;
2579 goto END;
2580 case WM_MOUSEMOVE:
2581 if (GetCapture() == hwnd)
2582 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2583 (INT16)HIWORD(lParam) );
2584 retvalue =0;
2585 goto END;
2586 case WM_LBUTTONUP:
2587 retvalue =LISTBOX_HandleLButtonUp( wnd, descr );
2588 goto END;
2589 case WM_KEYDOWN:
2590 retvalue =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2591 goto END;
2592 case WM_CHAR:
2593 retvalue =LISTBOX_HandleChar( wnd, descr, wParam );
2594 goto END;
2595 case WM_SYSTIMER:
2596 retvalue =LISTBOX_HandleSystemTimer( wnd, descr );
2597 goto END;
2598 case WM_ERASEBKGND:
2599 if (IS_OWNERDRAW(descr))
2601 RECT rect;
2602 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2603 wParam, (LPARAM)wnd->hwndSelf );
2604 GetClientRect(hwnd, &rect);
2605 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2607 retvalue =1;
2608 goto END;
2609 case WM_DROPFILES:
2610 if( !descr->lphc )
2612 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2613 goto END;
2615 break;
2617 case WM_DROPOBJECT:
2618 case WM_QUERYDROPOBJECT:
2619 case WM_DRAGSELECT:
2620 case WM_DRAGMOVE:
2621 if( !descr->lphc )
2623 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2624 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2625 dragInfo->pt.y );
2626 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2627 goto END;
2629 break;
2631 case WM_NCCREATE:
2632 if (TWEAK_WineLook > WIN31_LOOK)
2633 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2634 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2635 goto END;
2636 default:
2637 if ((msg >= WM_USER) && (msg < 0xc000))
2638 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2639 hwnd, msg, wParam, lParam );
2640 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2641 goto END;
2643 retvalue =0;
2644 END:
2645 WIN_ReleaseWndPtr(wnd);
2646 return retvalue;
2649 /***********************************************************************
2650 * COMBO_Directory
2652 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2654 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2656 if( wnd )
2658 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2659 if( descr )
2661 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2663 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2664 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2665 WIN_ReleaseWndPtr(wnd);
2666 return lRet;
2668 WIN_ReleaseWndPtr(wnd);
2670 return CB_ERR;
2673 /***********************************************************************
2674 * ComboLBWndProc
2676 * NOTE: in Windows, winproc address of the ComboLBox is the same
2677 * as that of the Listbox.
2679 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2680 WPARAM wParam, LPARAM lParam )
2682 LRESULT lRet = 0;
2683 WND *wnd = WIN_FindWndPtr( hwnd );
2685 if (wnd)
2687 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2689 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2690 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2692 if( descr || msg == WM_CREATE )
2694 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2696 switch( msg )
2698 case WM_CREATE:
2699 #define lpcs ((LPCREATESTRUCTA)lParam)
2700 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2701 (UINT)lpcs->lpCreateParams);
2703 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2704 #undef lpcs
2705 lRet =LISTBOX_Create( wnd, lphc );
2706 goto END;
2707 case WM_LBUTTONDOWN:
2708 lRet =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2709 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2710 goto END;
2711 /* avoid activation at all costs */
2713 case WM_MOUSEACTIVATE:
2714 lRet =MA_NOACTIVATE;
2715 goto END;
2716 case WM_NCACTIVATE:
2717 lRet =FALSE;
2718 goto END;
2719 case WM_KEYDOWN:
2720 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2722 /* for some reason(?) Windows makes it possible to
2723 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2725 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2726 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2727 && (wParam == VK_DOWN || wParam == VK_UP)) )
2729 COMBO_FlipListbox( lphc, FALSE );
2730 lRet =0;
2731 goto END;
2734 lRet =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2735 goto END;
2737 case LB_SETCURSEL16:
2738 case LB_SETCURSEL:
2739 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2740 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2741 goto END;
2742 case WM_NCDESTROY:
2743 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2744 lphc->hWndLBox = 0;
2745 /* fall through */
2747 default:
2748 lRet =ListBoxWndProc( hwnd, msg, wParam, lParam );
2749 goto END;
2752 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2754 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2756 END:
2757 WIN_ReleaseWndPtr(wnd);
2758 return lRet;