Only declare console "driver" once, use external refs elsewhere.
[wine/multimedia.git] / controls / listbox.c
blobdae876df9f7127714e6ac2994902862607d3d148
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 BOOL captured; /* Is mouse captured? */
68 HFONT font; /* Current font */
69 LCID locale; /* Current locale for string comparisons */
70 LPHEADCOMBO lphc; /* ComboLBox */
71 } LB_DESCR;
74 #define IS_OWNERDRAW(descr) \
75 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
77 #define HAS_STRINGS(descr) \
78 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
80 #define SEND_NOTIFICATION(wnd,descr,code) \
81 (SendMessageA( (descr)->owner, WM_COMMAND, \
82 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
84 /* Current timer status */
85 typedef enum
87 LB_TIMER_NONE,
88 LB_TIMER_UP,
89 LB_TIMER_LEFT,
90 LB_TIMER_DOWN,
91 LB_TIMER_RIGHT
92 } TIMER_DIRECTION;
94 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
97 /***********************************************************************
98 * LISTBOX_Dump
100 void LISTBOX_Dump( WND *wnd )
102 INT i;
103 LB_ITEMDATA *item;
104 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
106 DUMP( "Listbox:\n" );
107 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
108 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
109 descr->top_item );
110 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
112 DUMP( "%4d: %-40s %d %08lx %3d\n",
113 i, item->str, item->selected, item->data, item->height );
118 /***********************************************************************
119 * LISTBOX_GetCurrentPageSize
121 * Return the current page size
123 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
125 INT i, height;
126 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
127 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
129 if ((height += descr->items[i].height) > descr->height) break;
131 if (i == descr->top_item) return 1;
132 else return i - descr->top_item;
136 /***********************************************************************
137 * LISTBOX_GetMaxTopIndex
139 * Return the maximum possible index for the top of the listbox.
141 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
143 INT max, page;
145 if (descr->style & LBS_OWNERDRAWVARIABLE)
147 page = descr->height;
148 for (max = descr->nb_items - 1; max >= 0; max--)
149 if ((page -= descr->items[max].height) < 0) break;
150 if (max < descr->nb_items - 1) max++;
152 else if (descr->style & LBS_MULTICOLUMN)
154 if ((page = descr->width / descr->column_width) < 1) page = 1;
155 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
156 max = (max - page) * descr->page_size;
158 else
160 max = descr->nb_items - descr->page_size;
162 if (max < 0) max = 0;
163 return max;
167 /***********************************************************************
168 * LISTBOX_UpdateScroll
170 * Update the scrollbars. Should be called whenever the content
171 * of the listbox changes.
173 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
175 SCROLLINFO info;
177 if (!(descr->style & WS_VSCROLL)) return;
178 /* It is important that we check descr->style, and not wnd->dwStyle,
179 for WS_VSCROLL, as the former is exactly the one passed in
180 argument to CreateWindow.
181 In Windows (and from now on in Wine :) a listbox created
182 with such a style (no WS_SCROLL) does not update
183 the scrollbar with listbox-related data, thus letting
184 the programmer use it for his/her own purposes. */
186 if (descr->style & LBS_NOREDRAW) return;
187 info.cbSize = sizeof(info);
189 if (descr->style & LBS_MULTICOLUMN)
191 info.nMin = 0;
192 info.nMax = (descr->nb_items - 1) / descr->page_size;
193 info.nPos = descr->top_item / descr->page_size;
194 info.nPage = descr->width / descr->column_width;
195 if (info.nPage < 1) info.nPage = 1;
196 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
197 if (descr->style & LBS_DISABLENOSCROLL)
198 info.fMask |= SIF_DISABLENOSCROLL;
199 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
200 info.nMax = 0;
201 info.fMask = SIF_RANGE;
202 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
204 else
206 info.nMin = 0;
207 info.nMax = descr->nb_items - 1;
208 info.nPos = descr->top_item;
209 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
210 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
211 if (descr->style & LBS_DISABLENOSCROLL)
212 info.fMask |= SIF_DISABLENOSCROLL;
213 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
215 if (descr->horz_extent)
217 info.nMin = 0;
218 info.nMax = descr->horz_extent - 1;
219 info.nPos = descr->horz_pos;
220 info.nPage = descr->width;
221 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
222 if (descr->style & LBS_DISABLENOSCROLL)
223 info.fMask |= SIF_DISABLENOSCROLL;
224 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
230 /***********************************************************************
231 * LISTBOX_SetTopItem
233 * Set the top item of the listbox, scrolling up or down if necessary.
235 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
236 BOOL scroll )
238 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
239 if (index > max) index = max;
240 if (index < 0) index = 0;
241 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
242 if (descr->top_item == index) return LB_OKAY;
243 if (descr->style & LBS_MULTICOLUMN)
245 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
246 if (scroll && (abs(diff) < descr->width))
247 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
248 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
250 else
251 scroll = FALSE;
253 else if (scroll)
255 INT diff;
256 if (descr->style & LBS_OWNERDRAWVARIABLE)
258 INT i;
259 diff = 0;
260 if (index > descr->top_item)
262 for (i = index - 1; i >= descr->top_item; i--)
263 diff -= descr->items[i].height;
265 else
267 for (i = index; i < descr->top_item; i++)
268 diff += descr->items[i].height;
271 else
272 diff = (descr->top_item - index) * descr->item_height;
274 if (abs(diff) < descr->height)
275 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
276 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
277 else
278 scroll = FALSE;
280 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
281 descr->top_item = index;
282 LISTBOX_UpdateScroll( wnd, descr );
283 return LB_OKAY;
287 /***********************************************************************
288 * LISTBOX_UpdatePage
290 * Update the page size. Should be called when the size of
291 * the client area or the item height changes.
293 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
295 INT page_size;
297 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
298 if (page_size == descr->page_size) return;
299 descr->page_size = page_size;
300 if (descr->style & LBS_MULTICOLUMN)
301 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
302 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
306 /***********************************************************************
307 * LISTBOX_UpdateSize
309 * Update the size of the listbox. Should be called when the size of
310 * the client area changes.
312 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
314 RECT rect;
316 GetClientRect( wnd->hwndSelf, &rect );
317 descr->width = rect.right - rect.left;
318 descr->height = rect.bottom - rect.top;
319 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
321 if ((descr->height > descr->item_height) &&
322 (descr->height % descr->item_height))
324 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
325 wnd->hwndSelf, descr->height,
326 descr->height - descr->height%descr->item_height );
327 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
328 wnd->rectWindow.right - wnd->rectWindow.left,
329 wnd->rectWindow.bottom - wnd->rectWindow.top -
330 (descr->height % descr->item_height),
331 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
332 return;
335 TRACE(listbox, "[%04x]: new size = %d,%d\n",
336 wnd->hwndSelf, descr->width, descr->height );
337 LISTBOX_UpdatePage( wnd, descr );
338 LISTBOX_UpdateScroll( wnd, descr );
342 /***********************************************************************
343 * LISTBOX_GetItemRect
345 * Get the rectangle enclosing an item, in listbox client coordinates.
346 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
348 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
349 RECT *rect )
351 /* Index <= 0 is legal even on empty listboxes */
352 if (index && (index >= descr->nb_items)) return -1;
353 SetRect( rect, 0, 0, descr->width, descr->height );
354 if (descr->style & LBS_MULTICOLUMN)
356 INT col = (index / descr->page_size) -
357 (descr->top_item / descr->page_size);
358 rect->left += col * descr->column_width;
359 rect->right = rect->left + descr->column_width;
360 rect->top += (index % descr->page_size) * descr->item_height;
361 rect->bottom = rect->top + descr->item_height;
363 else if (descr->style & LBS_OWNERDRAWVARIABLE)
365 INT i;
366 rect->right += descr->horz_pos;
367 if ((index >= 0) && (index < descr->nb_items))
369 if (index < descr->top_item)
371 for (i = descr->top_item-1; i >= index; i--)
372 rect->top -= descr->items[i].height;
374 else
376 for (i = descr->top_item; i < index; i++)
377 rect->top += descr->items[i].height;
379 rect->bottom = rect->top + descr->items[index].height;
383 else
385 rect->top += (index - descr->top_item) * descr->item_height;
386 rect->bottom = rect->top + descr->item_height;
387 rect->right += descr->horz_pos;
390 return ((rect->left < descr->width) && (rect->right > 0) &&
391 (rect->top < descr->height) && (rect->bottom > 0));
395 /***********************************************************************
396 * LISTBOX_GetItemFromPoint
398 * Return the item nearest from point (x,y) (in client coordinates).
400 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
401 INT x, INT y )
403 INT index = descr->top_item;
405 if (!descr->nb_items) return -1; /* No items */
406 if (descr->style & LBS_OWNERDRAWVARIABLE)
408 INT pos = 0;
409 if (y >= 0)
411 while (index < descr->nb_items)
413 if ((pos += descr->items[index].height) > y) break;
414 index++;
417 else
419 while (index > 0)
421 index--;
422 if ((pos -= descr->items[index].height) <= y) break;
426 else if (descr->style & LBS_MULTICOLUMN)
428 if (y >= descr->item_height * descr->page_size) return -1;
429 if (y >= 0) index += y / descr->item_height;
430 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
431 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
433 else
435 index += (y / descr->item_height);
437 if (index < 0) return 0;
438 if (index >= descr->nb_items) return -1;
439 return index;
443 /***********************************************************************
444 * LISTBOX_PaintItem
446 * Paint an item.
448 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
449 const RECT *rect, INT index, UINT action )
451 LB_ITEMDATA *item = NULL;
452 if (index < descr->nb_items) item = &descr->items[index];
454 if (IS_OWNERDRAW(descr))
456 DRAWITEMSTRUCT dis;
457 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
459 if (!item)
461 if (action == ODA_FOCUS)
462 DrawFocusRect( hdc, rect );
463 else
464 FIXME(listbox,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
465 return;
467 dis.CtlType = ODT_LISTBOX;
468 dis.CtlID = id;
469 dis.hwndItem = wnd->hwndSelf;
470 dis.itemAction = action;
471 dis.hDC = hdc;
472 dis.itemID = index;
473 dis.itemState = 0;
474 if (item && item->selected) dis.itemState |= ODS_SELECTED;
475 if ((descr->focus_item == index) &&
476 (descr->caret_on) &&
477 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
478 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
479 dis.itemData = item ? item->data : 0;
480 dis.rcItem = *rect;
481 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
482 "state=%02x rect=%d,%d-%d,%d\n",
483 wnd->hwndSelf, index, item ? item->str : "", action,
484 dis.itemState, rect->left, rect->top,
485 rect->right, rect->bottom );
486 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
488 else
490 COLORREF oldText = 0, oldBk = 0;
492 if (action == ODA_FOCUS)
494 DrawFocusRect( hdc, rect );
495 return;
497 if (item && item->selected)
499 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
500 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
503 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
504 "rect=%d,%d-%d,%d\n",
505 wnd->hwndSelf, index, item ? item->str : "", action,
506 rect->left, rect->top, rect->right, rect->bottom );
507 if (!item)
508 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
509 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
510 else if (!(descr->style & LBS_USETABSTOPS))
511 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
512 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
513 strlen(item->str), NULL );
514 else
516 /* Output empty string to paint background in the full width. */
517 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
518 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
519 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
520 item->str, strlen(item->str),
521 descr->nb_tabs, descr->tabs, 0);
523 if (item && item->selected)
525 SetBkColor( hdc, oldBk );
526 SetTextColor( hdc, oldText );
528 if ((descr->focus_item == index) &&
529 (descr->caret_on) &&
530 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
535 /***********************************************************************
536 * LISTBOX_SetRedraw
538 * Change the redraw flag.
540 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
542 if (on)
544 if (!(descr->style & LBS_NOREDRAW)) return;
545 descr->style &= ~LBS_NOREDRAW;
546 LISTBOX_UpdateScroll( wnd, descr );
548 else descr->style |= LBS_NOREDRAW;
552 /***********************************************************************
553 * LISTBOX_RepaintItem
555 * Repaint a single item synchronously.
557 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
558 UINT action )
560 HDC hdc;
561 RECT rect;
562 HFONT oldFont = 0;
563 HBRUSH hbrush, oldBrush = 0;
565 if (descr->style & LBS_NOREDRAW) return;
566 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
567 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
568 if (descr->font) oldFont = SelectObject( hdc, descr->font );
569 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
570 hdc, (LPARAM)wnd->hwndSelf );
571 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
572 if (wnd->dwStyle & WS_DISABLED)
573 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
574 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
575 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
576 if (oldFont) SelectObject( hdc, oldFont );
577 if (oldBrush) SelectObject( hdc, oldBrush );
578 ReleaseDC( wnd->hwndSelf, hdc );
582 /***********************************************************************
583 * LISTBOX_InitStorage
585 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
586 DWORD bytes )
588 LB_ITEMDATA *item;
590 nb_items += LB_ARRAY_GRANULARITY - 1;
591 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
592 if (descr->items)
593 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
594 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
595 nb_items * sizeof(LB_ITEMDATA) )))
597 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
598 return LB_ERRSPACE;
600 descr->items = item;
601 return LB_OKAY;
605 /***********************************************************************
606 * LISTBOX_SetTabStops
608 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
609 LPINT tabs, BOOL short_ints )
611 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
612 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
613 if (!(descr->nb_tabs = count))
615 descr->tabs = NULL;
616 return TRUE;
618 /* FIXME: count = 1 */
619 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
620 descr->nb_tabs * sizeof(INT) )))
621 return FALSE;
622 if (short_ints)
624 INT i;
625 LPINT16 p = (LPINT16)tabs;
626 dbg_decl_str(listbox, 256);
628 for (i = 0; i < descr->nb_tabs; i++) {
629 descr->tabs[i] = *p++<<1; /* FIXME */
630 if(TRACE_ON(listbox))
631 dsprintf(listbox, "%hd ", descr->tabs[i]);
633 TRACE(listbox, "[%04x]: settabstops %s\n",
634 wnd->hwndSelf, dbg_str(listbox));
636 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
637 /* FIXME: repaint the window? */
638 return TRUE;
642 /***********************************************************************
643 * LISTBOX_GetText
645 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
646 LPSTR buffer )
648 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
649 if (HAS_STRINGS(descr))
651 if (!buffer)
652 return strlen(descr->items[index].str);
653 lstrcpyA( buffer, descr->items[index].str );
654 return strlen(buffer);
655 } else {
656 if (buffer)
657 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
658 return sizeof(DWORD);
663 /***********************************************************************
664 * LISTBOX_FindStringPos
666 * Find the nearest string located before a given string in sort order.
667 * If 'exact' is TRUE, return an error if we don't get an exact match.
669 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
670 BOOL exact )
672 INT index, min, max, res = -1;
674 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
675 min = 0;
676 max = descr->nb_items;
677 while (min != max)
679 index = (min + max) / 2;
680 if (HAS_STRINGS(descr))
681 res = lstrcmpiA( descr->items[index].str, str );
682 else
684 COMPAREITEMSTRUCT cis;
685 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
687 cis.CtlType = ODT_LISTBOX;
688 cis.CtlID = id;
689 cis.hwndItem = wnd->hwndSelf;
690 cis.itemID1 = index;
691 cis.itemData1 = descr->items[index].data;
692 cis.itemID2 = -1;
693 cis.itemData2 = (DWORD)str;
694 cis.dwLocaleId = descr->locale;
695 res = SendMessageA( descr->owner, WM_COMPAREITEM,
696 id, (LPARAM)&cis );
698 if (!res) return index;
699 if (res > 0) max = index;
700 else min = index + 1;
702 return exact ? -1 : max;
706 /***********************************************************************
707 * LISTBOX_FindFileStrPos
709 * Find the nearest string located before a given string in directory
710 * sort order (i.e. first files, then directories, then drives).
712 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
714 INT min, max, res = -1;
716 if (!HAS_STRINGS(descr))
717 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
718 min = 0;
719 max = descr->nb_items;
720 while (min != max)
722 INT index = (min + max) / 2;
723 const char *p = descr->items[index].str;
724 if (*p == '[') /* drive or directory */
726 if (*str != '[') res = -1;
727 else if (p[1] == '-') /* drive */
729 if (str[1] == '-') res = str[2] - p[2];
730 else res = -1;
732 else /* directory */
734 if (str[1] == '-') res = 1;
735 else res = lstrcmpiA( str, p );
738 else /* filename */
740 if (*str == '[') res = 1;
741 else res = lstrcmpiA( str, p );
743 if (!res) return index;
744 if (res < 0) max = index;
745 else min = index + 1;
747 return max;
751 /***********************************************************************
752 * LISTBOX_FindString
754 * Find the item beginning with a given string.
756 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
757 LPCSTR str, BOOL exact )
759 INT i;
760 LB_ITEMDATA *item;
762 if (start >= descr->nb_items) start = -1;
763 item = descr->items + start + 1;
764 if (HAS_STRINGS(descr))
766 if (!str) return LB_ERR;
767 if (exact)
769 for (i = start + 1; i < descr->nb_items; i++, item++)
770 if (!lstrcmpiA( str, item->str )) return i;
771 for (i = 0, item = descr->items; i <= start; i++, item++)
772 if (!lstrcmpiA( str, item->str )) return i;
774 else
776 /* Special case for drives and directories: ignore prefix */
777 #define CHECK_DRIVE(item) \
778 if ((item)->str[0] == '[') \
780 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
781 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
782 return i; \
785 INT len = strlen(str);
786 for (i = start + 1; i < descr->nb_items; i++, item++)
788 if (!lstrncmpiA( str, item->str, len )) return i;
789 CHECK_DRIVE(item);
791 for (i = 0, item = descr->items; i <= start; i++, item++)
793 if (!lstrncmpiA( str, item->str, len )) return i;
794 CHECK_DRIVE(item);
796 #undef CHECK_DRIVE
799 else
801 if (exact && (descr->style & LBS_SORT))
802 /* If sorted, use a WM_COMPAREITEM binary search */
803 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
805 /* Otherwise use a linear search */
806 for (i = start + 1; i < descr->nb_items; i++, item++)
807 if (item->data == (DWORD)str) return i;
808 for (i = 0, item = descr->items; i <= start; i++, item++)
809 if (item->data == (DWORD)str) return i;
811 return LB_ERR;
815 /***********************************************************************
816 * LISTBOX_GetSelCount
818 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
820 INT i, count;
821 LB_ITEMDATA *item = descr->items;
823 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
824 for (i = count = 0; i < descr->nb_items; i++, item++)
825 if (item->selected) count++;
826 return count;
830 /***********************************************************************
831 * LISTBOX_GetSelItems16
833 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
834 LPINT16 array )
836 INT i, count;
837 LB_ITEMDATA *item = descr->items;
839 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
840 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
841 if (item->selected) array[count++] = (INT16)i;
842 return count;
846 /***********************************************************************
847 * LISTBOX_GetSelItems32
849 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
850 LPINT array )
852 INT i, count;
853 LB_ITEMDATA *item = descr->items;
855 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
856 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
857 if (item->selected) array[count++] = i;
858 return count;
862 /***********************************************************************
863 * LISTBOX_Paint
865 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
867 INT i, col_pos = descr->page_size - 1;
868 RECT rect;
869 HFONT oldFont = 0;
870 HBRUSH hbrush, oldBrush = 0;
872 SetRect( &rect, 0, 0, descr->width, descr->height );
873 if (descr->style & LBS_NOREDRAW) return 0;
874 if (descr->style & LBS_MULTICOLUMN)
875 rect.right = rect.left + descr->column_width;
876 else if (descr->horz_pos)
878 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
879 rect.right += descr->horz_pos;
882 if (descr->font) oldFont = SelectObject( hdc, descr->font );
883 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
884 hdc, (LPARAM)wnd->hwndSelf );
885 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
886 if (wnd->dwStyle & WS_DISABLED)
887 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
889 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
890 (GetFocus() == wnd->hwndSelf))
892 /* Special case for empty listbox: paint focus rect */
893 rect.bottom = rect.top + descr->item_height;
894 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
895 ODA_FOCUS );
896 rect.top = rect.bottom;
899 for (i = descr->top_item; i < descr->nb_items; i++)
901 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
902 rect.bottom = rect.top + descr->item_height;
903 else
904 rect.bottom = rect.top + descr->items[i].height;
906 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
907 rect.top = rect.bottom;
909 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
911 if (!IS_OWNERDRAW(descr))
913 /* Clear the bottom of the column */
914 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
915 if (rect.top < descr->height)
917 rect.bottom = descr->height;
918 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
919 &rect, NULL, 0, NULL );
923 /* Go to the next column */
924 rect.left += descr->column_width;
925 rect.right += descr->column_width;
926 rect.top = 0;
927 col_pos = descr->page_size - 1;
929 else
931 col_pos--;
932 if (rect.top >= descr->height) break;
936 if (!IS_OWNERDRAW(descr))
938 /* Clear the remainder of the client area */
939 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
940 if (rect.top < descr->height)
942 rect.bottom = descr->height;
943 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
944 &rect, NULL, 0, NULL );
946 if (rect.right < descr->width)
948 rect.left = rect.right;
949 rect.right = descr->width;
950 rect.top = 0;
951 rect.bottom = descr->height;
952 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
953 &rect, NULL, 0, NULL );
956 if (oldFont) SelectObject( hdc, oldFont );
957 if (oldBrush) SelectObject( hdc, oldBrush );
958 return 0;
962 /***********************************************************************
963 * LISTBOX_InvalidateItems
965 * Invalidate all items from a given item. If the specified item is not
966 * visible, nothing happens.
968 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
970 RECT rect;
972 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
974 rect.bottom = descr->height;
975 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
976 if (descr->style & LBS_MULTICOLUMN)
978 /* Repaint the other columns */
979 rect.left = rect.right;
980 rect.right = descr->width;
981 rect.top = 0;
982 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
988 /***********************************************************************
989 * LISTBOX_GetItemHeight
991 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
993 if (descr->style & LBS_OWNERDRAWVARIABLE)
995 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
996 return descr->items[index].height;
998 else return descr->item_height;
1002 /***********************************************************************
1003 * LISTBOX_SetItemHeight
1005 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1006 UINT height )
1008 if (!height) height = 1;
1010 if (descr->style & LBS_OWNERDRAWVARIABLE)
1012 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1013 TRACE(listbox, "[%04x]: item %d height = %d\n",
1014 wnd->hwndSelf, index, height );
1015 descr->items[index].height = height;
1016 LISTBOX_UpdateScroll( wnd, descr );
1017 LISTBOX_InvalidateItems( wnd, descr, index );
1019 else if (height != descr->item_height)
1021 TRACE(listbox, "[%04x]: new height = %d\n",
1022 wnd->hwndSelf, height );
1023 descr->item_height = height;
1024 LISTBOX_UpdatePage( wnd, descr );
1025 LISTBOX_UpdateScroll( wnd, descr );
1026 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1028 return LB_OKAY;
1032 /***********************************************************************
1033 * LISTBOX_SetHorizontalPos
1035 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1037 INT diff;
1039 if (pos > descr->horz_extent - descr->width)
1040 pos = descr->horz_extent - descr->width;
1041 if (pos < 0) pos = 0;
1042 if (!(diff = descr->horz_pos - pos)) return;
1043 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1044 wnd->hwndSelf, pos );
1045 descr->horz_pos = pos;
1046 LISTBOX_UpdateScroll( wnd, descr );
1047 if (abs(diff) < descr->width)
1048 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1049 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1050 else
1051 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1055 /***********************************************************************
1056 * LISTBOX_SetHorizontalExtent
1058 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1059 UINT extent )
1061 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1062 return LB_OKAY;
1063 if (extent <= 0) extent = 1;
1064 if (extent == descr->horz_extent) return LB_OKAY;
1065 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1066 wnd->hwndSelf, extent );
1067 descr->horz_extent = extent;
1068 if (descr->horz_pos > extent - descr->width)
1069 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1070 else
1071 LISTBOX_UpdateScroll( wnd, descr );
1072 return LB_OKAY;
1076 /***********************************************************************
1077 * LISTBOX_SetColumnWidth
1079 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1081 width += 2; /* For left and right margin */
1082 if (width == descr->column_width) return LB_OKAY;
1083 TRACE(listbox, "[%04x]: new column width = %d\n",
1084 wnd->hwndSelf, width );
1085 descr->column_width = width;
1086 LISTBOX_UpdatePage( wnd, descr );
1087 return LB_OKAY;
1091 /***********************************************************************
1092 * LISTBOX_SetFont
1094 * Returns the item height.
1096 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1098 HDC hdc;
1099 HFONT oldFont = 0;
1100 TEXTMETRICA tm;
1102 descr->font = font;
1104 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1106 ERR(listbox, "unable to get DC.\n" );
1107 return 16;
1109 if (font) oldFont = SelectObject( hdc, font );
1110 GetTextMetricsA( hdc, &tm );
1111 if (oldFont) SelectObject( hdc, oldFont );
1112 ReleaseDC( wnd->hwndSelf, hdc );
1113 if (!IS_OWNERDRAW(descr))
1114 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1115 return tm.tmHeight ;
1119 /***********************************************************************
1120 * LISTBOX_MakeItemVisible
1122 * Make sure that a given item is partially or fully visible.
1124 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1125 BOOL fully )
1127 INT top;
1129 if (index <= descr->top_item) top = index;
1130 else if (descr->style & LBS_MULTICOLUMN)
1132 INT cols = descr->width;
1133 if (!fully) cols += descr->column_width - 1;
1134 if (cols >= descr->column_width) cols /= descr->column_width;
1135 else cols = 1;
1136 if (index < descr->top_item + (descr->page_size * cols)) return;
1137 top = index - descr->page_size * (cols - 1);
1139 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1141 INT height = fully ? descr->items[index].height : 1;
1142 for (top = index; top > descr->top_item; top--)
1143 if ((height += descr->items[top-1].height) > descr->height) break;
1145 else
1147 if (index < descr->top_item + descr->page_size) return;
1148 if (!fully && (index == descr->top_item + descr->page_size) &&
1149 (descr->height > (descr->page_size * descr->item_height))) return;
1150 top = index - descr->page_size + 1;
1152 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1156 /***********************************************************************
1157 * LISTBOX_SelectItemRange
1159 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1161 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1162 INT last, BOOL on )
1164 INT i;
1166 /* A few sanity checks */
1168 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1169 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1170 if (last == -1) last = descr->nb_items - 1;
1171 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1172 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1173 /* selected_item reflects last selected/unselected item on multiple sel */
1174 descr->selected_item = last;
1176 if (on) /* Turn selection on */
1178 for (i = first; i <= last; i++)
1180 if (descr->items[i].selected) continue;
1181 descr->items[i].selected = TRUE;
1182 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1185 else /* Turn selection off */
1187 for (i = first; i <= last; i++)
1189 if (!descr->items[i].selected) continue;
1190 descr->items[i].selected = FALSE;
1191 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1194 return LB_OKAY;
1198 /***********************************************************************
1199 * LISTBOX_SetCaretIndex
1201 * NOTES
1202 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1205 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1206 BOOL fully_visible )
1208 INT oldfocus = descr->focus_item;
1210 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1211 if (index == oldfocus) return LB_OKAY;
1212 descr->focus_item = index;
1213 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1214 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1216 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1217 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1218 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1220 return LB_OKAY;
1224 /***********************************************************************
1225 * LISTBOX_SetSelection
1227 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1228 BOOL on, BOOL send_notify )
1230 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1231 if (descr->style & LBS_MULTIPLESEL)
1233 if (index == -1) /* Select all items */
1234 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1235 else /* Only one item */
1236 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1238 else
1240 INT oldsel = descr->selected_item;
1241 if (index == oldsel) return LB_OKAY;
1242 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1243 if (index != -1) descr->items[index].selected = TRUE;
1244 descr->selected_item = index;
1245 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1246 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1247 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1248 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1249 else
1250 if( descr->lphc ) /* set selection change flag for parent combo */
1251 descr->lphc->wState |= CBF_SELCHANGE;
1253 return LB_OKAY;
1257 /***********************************************************************
1258 * LISTBOX_MoveCaret
1260 * Change the caret position and extend the selection to the new caret.
1262 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1263 BOOL fully_visible )
1265 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1266 if (descr->style & LBS_EXTENDEDSEL)
1268 if (descr->anchor_item != -1)
1270 INT first = MIN( descr->focus_item, descr->anchor_item );
1271 INT last = MAX( descr->focus_item, descr->anchor_item );
1272 if (first > 0)
1273 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1274 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1275 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1278 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1280 /* Set selection to new caret item */
1281 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1286 /***********************************************************************
1287 * LISTBOX_InsertItem
1289 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1290 LPSTR str, DWORD data )
1292 LB_ITEMDATA *item;
1293 INT max_items;
1295 if (index == -1) index = descr->nb_items;
1296 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1297 if (!descr->items) max_items = 0;
1298 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1299 if (descr->nb_items == max_items)
1301 /* We need to grow the array */
1302 max_items += LB_ARRAY_GRANULARITY;
1303 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1304 max_items * sizeof(LB_ITEMDATA) )))
1306 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1307 return LB_ERRSPACE;
1309 descr->items = item;
1312 /* Insert the item structure */
1314 item = &descr->items[index];
1315 if (index < descr->nb_items)
1316 RtlMoveMemory( item + 1, item,
1317 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1318 item->str = str;
1319 item->data = data;
1320 item->height = 0;
1321 item->selected = FALSE;
1322 descr->nb_items++;
1324 /* Get item height */
1326 if (descr->style & LBS_OWNERDRAWVARIABLE)
1328 MEASUREITEMSTRUCT mis;
1329 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1331 mis.CtlType = ODT_LISTBOX;
1332 mis.CtlID = id;
1333 mis.itemID = index;
1334 mis.itemData = descr->items[index].data;
1335 mis.itemHeight = descr->item_height;
1336 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1337 item->height = mis.itemHeight ? mis.itemHeight : 1;
1338 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1339 wnd->hwndSelf, index, str ? str : "", item->height );
1342 /* Repaint the items */
1344 LISTBOX_UpdateScroll( wnd, descr );
1345 LISTBOX_InvalidateItems( wnd, descr, index );
1347 /* Move selection and focused item */
1349 if (index <= descr->selected_item) descr->selected_item++;
1350 if (index <= descr->focus_item)
1352 descr->focus_item++;
1353 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1356 /* If listbox was empty, set focus to the first item */
1358 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1359 return LB_OKAY;
1363 /***********************************************************************
1364 * LISTBOX_InsertString
1366 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1367 LPCSTR str )
1369 LPSTR new_str = NULL;
1370 DWORD data = 0;
1371 LRESULT ret;
1373 if (HAS_STRINGS(descr))
1375 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1377 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1378 return LB_ERRSPACE;
1381 else data = (DWORD)str;
1383 if (index == -1) index = descr->nb_items;
1384 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1386 if (new_str) HeapFree( descr->heap, 0, new_str );
1387 return ret;
1390 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1391 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1392 return index;
1396 /***********************************************************************
1397 * LISTBOX_DeleteItem
1399 * Delete the content of an item. 'index' must be a valid index.
1401 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1403 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1404 * while Win95 sends it for all items with user data.
1405 * It's probably better to send it too often than not
1406 * often enough, so this is what we do here.
1408 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1410 DELETEITEMSTRUCT dis;
1411 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1413 dis.CtlType = ODT_LISTBOX;
1414 dis.CtlID = id;
1415 dis.itemID = index;
1416 dis.hwndItem = wnd->hwndSelf;
1417 dis.itemData = descr->items[index].data;
1418 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1420 if (HAS_STRINGS(descr) && descr->items[index].str)
1421 HeapFree( descr->heap, 0, descr->items[index].str );
1425 /***********************************************************************
1426 * LISTBOX_RemoveItem
1428 * Remove an item from the listbox and delete its content.
1430 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1432 LB_ITEMDATA *item;
1433 INT max_items;
1435 if (index == -1) index = descr->nb_items - 1;
1436 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1437 LISTBOX_DeleteItem( wnd, descr, index );
1439 /* Remove the item */
1441 item = &descr->items[index];
1442 if (index < descr->nb_items-1)
1443 RtlMoveMemory( item, item + 1,
1444 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1445 descr->nb_items--;
1446 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1448 /* Shrink the item array if possible */
1450 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1451 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1453 max_items -= LB_ARRAY_GRANULARITY;
1454 item = HeapReAlloc( descr->heap, 0, descr->items,
1455 max_items * sizeof(LB_ITEMDATA) );
1456 if (item) descr->items = item;
1459 /* Repaint the items */
1461 LISTBOX_UpdateScroll( wnd, descr );
1462 LISTBOX_InvalidateItems( wnd, descr, index );
1464 /* Move selection and focused item */
1466 if (index <= descr->selected_item) descr->selected_item--;
1467 if (index <= descr->focus_item)
1469 descr->focus_item--;
1470 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1472 return LB_OKAY;
1476 /***********************************************************************
1477 * LISTBOX_ResetContent
1479 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1481 INT i;
1483 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1484 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1485 descr->nb_items = 0;
1486 descr->top_item = 0;
1487 descr->selected_item = -1;
1488 descr->focus_item = 0;
1489 descr->anchor_item = -1;
1490 descr->items = NULL;
1491 LISTBOX_UpdateScroll( wnd, descr );
1492 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1496 /***********************************************************************
1497 * LISTBOX_SetCount
1499 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1501 LRESULT ret;
1503 if (HAS_STRINGS(descr)) return LB_ERR;
1504 /* FIXME: this is far from optimal... */
1505 if (count > descr->nb_items)
1507 while (count > descr->nb_items)
1508 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1509 return ret;
1511 else if (count < descr->nb_items)
1513 while (count < descr->nb_items)
1514 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1515 return ret;
1517 return LB_OKAY;
1521 /***********************************************************************
1522 * LISTBOX_Directory
1524 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1525 LPCSTR filespec, BOOL long_names )
1527 HANDLE handle;
1528 LRESULT ret = LB_OKAY;
1529 WIN32_FIND_DATAA entry;
1530 int pos;
1532 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1534 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1536 else
1540 char buffer[270];
1541 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1543 if (!(attrib & DDL_DIRECTORY) ||
1544 !strcmp( entry.cAlternateFileName, "." )) continue;
1545 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1546 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1548 else /* not a directory */
1550 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1551 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1553 if ((attrib & DDL_EXCLUSIVE) &&
1554 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1555 continue;
1556 #undef ATTRIBS
1557 if (long_names) strcpy( buffer, entry.cFileName );
1558 else strcpy( buffer, entry.cAlternateFileName );
1560 if (!long_names) CharLowerA( buffer );
1561 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1562 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1563 break;
1564 } while (FindNextFileA( handle, &entry ));
1565 FindClose( handle );
1568 if ((ret >= 0) && (attrib & DDL_DRIVES))
1570 char buffer[] = "[-a-]";
1571 int drive;
1572 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1574 if (!DRIVE_IsValid(drive)) continue;
1575 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1576 break;
1579 return ret;
1583 /***********************************************************************
1584 * LISTBOX_HandleVScroll
1586 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1587 WPARAM wParam, LPARAM lParam )
1589 SCROLLINFO info;
1591 if (descr->style & LBS_MULTICOLUMN) return 0;
1592 switch(LOWORD(wParam))
1594 case SB_LINEUP:
1595 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1596 break;
1597 case SB_LINEDOWN:
1598 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1599 break;
1600 case SB_PAGEUP:
1601 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1602 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1603 break;
1604 case SB_PAGEDOWN:
1605 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1606 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1607 break;
1608 case SB_THUMBPOSITION:
1609 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1610 break;
1611 case SB_THUMBTRACK:
1612 info.cbSize = sizeof(info);
1613 info.fMask = SIF_TRACKPOS;
1614 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1615 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1616 break;
1617 case SB_TOP:
1618 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1619 break;
1620 case SB_BOTTOM:
1621 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1622 break;
1624 return 0;
1628 /***********************************************************************
1629 * LISTBOX_HandleHScroll
1631 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1632 WPARAM wParam, LPARAM lParam )
1634 SCROLLINFO info;
1635 INT page;
1637 if (descr->style & LBS_MULTICOLUMN)
1639 switch(LOWORD(wParam))
1641 case SB_LINELEFT:
1642 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1643 TRUE );
1644 break;
1645 case SB_LINERIGHT:
1646 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1647 TRUE );
1648 break;
1649 case SB_PAGELEFT:
1650 page = descr->width / descr->column_width;
1651 if (page < 1) page = 1;
1652 LISTBOX_SetTopItem( wnd, descr,
1653 descr->top_item - page * descr->page_size, TRUE );
1654 break;
1655 case SB_PAGERIGHT:
1656 page = descr->width / descr->column_width;
1657 if (page < 1) page = 1;
1658 LISTBOX_SetTopItem( wnd, descr,
1659 descr->top_item + page * descr->page_size, TRUE );
1660 break;
1661 case SB_THUMBPOSITION:
1662 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1663 TRUE );
1664 break;
1665 case SB_THUMBTRACK:
1666 info.cbSize = sizeof(info);
1667 info.fMask = SIF_TRACKPOS;
1668 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1669 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1670 TRUE );
1671 break;
1672 case SB_LEFT:
1673 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1674 break;
1675 case SB_RIGHT:
1676 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1677 break;
1680 else if (descr->horz_extent)
1682 switch(LOWORD(wParam))
1684 case SB_LINELEFT:
1685 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1686 break;
1687 case SB_LINERIGHT:
1688 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1689 break;
1690 case SB_PAGELEFT:
1691 LISTBOX_SetHorizontalPos( wnd, descr,
1692 descr->horz_pos - descr->width );
1693 break;
1694 case SB_PAGERIGHT:
1695 LISTBOX_SetHorizontalPos( wnd, descr,
1696 descr->horz_pos + descr->width );
1697 break;
1698 case SB_THUMBPOSITION:
1699 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1700 break;
1701 case SB_THUMBTRACK:
1702 info.cbSize = sizeof(info);
1703 info.fMask = SIF_TRACKPOS;
1704 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1705 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1706 break;
1707 case SB_LEFT:
1708 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1709 break;
1710 case SB_RIGHT:
1711 LISTBOX_SetHorizontalPos( wnd, descr,
1712 descr->horz_extent - descr->width );
1713 break;
1716 return 0;
1720 /***********************************************************************
1721 * LISTBOX_HandleLButtonDown
1723 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1724 WPARAM wParam, INT x, INT y )
1726 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1727 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1728 wnd->hwndSelf, x, y, index );
1729 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1730 if (index != -1)
1732 if (descr->style & LBS_EXTENDEDSEL)
1734 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1735 if (wParam & MK_CONTROL)
1737 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1738 LISTBOX_SetSelection( wnd, descr, index,
1739 !descr->items[index].selected, FALSE );
1741 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1743 else
1745 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1746 LISTBOX_SetSelection( wnd, descr, index,
1747 (!(descr->style & LBS_MULTIPLESEL) ||
1748 !descr->items[index].selected), FALSE );
1752 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1753 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1754 : descr->lphc->self->hwndSelf ) ;
1756 descr->captured = TRUE;
1757 SetCapture( wnd->hwndSelf );
1758 if (index != -1 && !descr->lphc)
1760 if (descr->style & LBS_NOTIFY )
1761 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1762 MAKELPARAM( x, y ) );
1763 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1765 POINT pt = { x, y };
1766 if (DragDetect( wnd->hwndSelf, pt ))
1767 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1770 return 0;
1774 /***********************************************************************
1775 * LISTBOX_HandleLButtonUp
1777 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1779 if (LISTBOX_Timer != LB_TIMER_NONE)
1780 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1781 LISTBOX_Timer = LB_TIMER_NONE;
1782 if (descr->captured)
1784 descr->captured = FALSE;
1785 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1786 if (descr->style & LBS_NOTIFY)
1787 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1789 return 0;
1793 /***********************************************************************
1794 * LISTBOX_HandleTimer
1796 * Handle scrolling upon a timer event.
1797 * Return TRUE if scrolling should continue.
1799 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1800 INT index, TIMER_DIRECTION dir )
1802 switch(dir)
1804 case LB_TIMER_UP:
1805 if (descr->top_item) index = descr->top_item - 1;
1806 else index = 0;
1807 break;
1808 case LB_TIMER_LEFT:
1809 if (descr->top_item) index -= descr->page_size;
1810 break;
1811 case LB_TIMER_DOWN:
1812 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1813 if (index == descr->focus_item) index++;
1814 if (index >= descr->nb_items) index = descr->nb_items - 1;
1815 break;
1816 case LB_TIMER_RIGHT:
1817 if (index + descr->page_size < descr->nb_items)
1818 index += descr->page_size;
1819 break;
1820 case LB_TIMER_NONE:
1821 break;
1823 if (index == descr->focus_item) return FALSE;
1824 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1825 return TRUE;
1829 /***********************************************************************
1830 * LISTBOX_HandleSystemTimer
1832 * WM_SYSTIMER handler.
1834 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1836 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1838 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1839 LISTBOX_Timer = LB_TIMER_NONE;
1841 return 0;
1845 /***********************************************************************
1846 * LISTBOX_HandleMouseMove
1848 * WM_MOUSEMOVE handler.
1850 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1851 INT x, INT y )
1853 INT index;
1854 TIMER_DIRECTION dir;
1856 if (!descr->captured) return;
1858 if (descr->style & LBS_MULTICOLUMN)
1860 if (y < 0) y = 0;
1861 else if (y >= descr->item_height * descr->page_size)
1862 y = descr->item_height * descr->page_size - 1;
1864 if (x < 0)
1866 dir = LB_TIMER_LEFT;
1867 x = 0;
1869 else if (x >= descr->width)
1871 dir = LB_TIMER_RIGHT;
1872 x = descr->width - 1;
1874 else dir = LB_TIMER_NONE; /* inside */
1876 else
1878 if (y < 0) dir = LB_TIMER_UP; /* above */
1879 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1880 else dir = LB_TIMER_NONE; /* inside */
1883 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1884 if (index == -1) index = descr->focus_item;
1885 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1887 /* Start/stop the system timer */
1889 if (dir != LB_TIMER_NONE)
1890 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1891 else if (LISTBOX_Timer != LB_TIMER_NONE)
1892 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1893 LISTBOX_Timer = dir;
1897 /***********************************************************************
1898 * LISTBOX_HandleKeyDown
1900 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1902 INT caret = -1;
1903 if (descr->style & LBS_WANTKEYBOARDINPUT)
1905 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1906 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1907 wnd->hwndSelf );
1908 if (caret == -2) return 0;
1910 if (caret == -1) switch(wParam)
1912 case VK_LEFT:
1913 if (descr->style & LBS_MULTICOLUMN)
1915 if (descr->focus_item >= descr->page_size)
1916 caret = descr->focus_item - descr->page_size;
1917 break;
1919 /* fall through */
1920 case VK_UP:
1921 caret = descr->focus_item - 1;
1922 if (caret < 0) caret = 0;
1923 break;
1924 case VK_RIGHT:
1925 if (descr->style & LBS_MULTICOLUMN)
1927 if (descr->focus_item + descr->page_size < descr->nb_items)
1928 caret = descr->focus_item + descr->page_size;
1929 break;
1931 /* fall through */
1932 case VK_DOWN:
1933 caret = descr->focus_item + 1;
1934 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1935 break;
1936 case VK_PRIOR:
1937 if (descr->style & LBS_MULTICOLUMN)
1939 INT page = descr->width / descr->column_width;
1940 if (page < 1) page = 1;
1941 caret = descr->focus_item - (page * descr->page_size) + 1;
1943 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1944 if (caret < 0) caret = 0;
1945 break;
1946 case VK_NEXT:
1947 if (descr->style & LBS_MULTICOLUMN)
1949 INT page = descr->width / descr->column_width;
1950 if (page < 1) page = 1;
1951 caret = descr->focus_item + (page * descr->page_size) - 1;
1953 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1954 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1955 break;
1956 case VK_HOME:
1957 caret = 0;
1958 break;
1959 case VK_END:
1960 caret = descr->nb_items - 1;
1961 break;
1962 case VK_SPACE:
1963 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1964 else if (descr->style & LBS_MULTIPLESEL)
1966 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1967 !descr->items[descr->focus_item].selected,
1968 (descr->style & LBS_NOTIFY) != 0 );
1970 else if (descr->selected_item == -1)
1972 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1973 (descr->style & LBS_NOTIFY) != 0 );
1975 break;
1977 if (caret >= 0)
1979 if ((descr->style & LBS_EXTENDEDSEL) &&
1980 !(GetKeyState( VK_SHIFT ) & 0x8000))
1981 descr->anchor_item = caret;
1982 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1983 if (descr->style & LBS_NOTIFY)
1985 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1987 /* make sure that combo parent doesn't hide us */
1988 descr->lphc->wState |= CBF_NOROLLUP;
1990 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1993 return 0;
1997 /***********************************************************************
1998 * LISTBOX_HandleChar
2000 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2001 WPARAM wParam )
2003 INT caret = -1;
2004 char str[2] = { wParam & 0xff, '\0' };
2006 if (descr->style & LBS_WANTKEYBOARDINPUT)
2008 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2009 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2010 wnd->hwndSelf );
2011 if (caret == -2) return 0;
2013 if (caret == -1)
2014 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2015 if (caret != -1)
2017 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2018 if (descr->style & LBS_NOTIFY)
2019 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2021 return 0;
2025 /***********************************************************************
2026 * LISTBOX_Create
2028 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2030 LB_DESCR *descr;
2031 MEASUREITEMSTRUCT mis;
2032 RECT rect;
2034 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2035 return FALSE;
2036 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2038 HeapFree( GetProcessHeap(), 0, descr );
2039 return FALSE;
2041 GetClientRect( wnd->hwndSelf, &rect );
2042 descr->owner = GetParent( wnd->hwndSelf );
2043 descr->style = wnd->dwStyle;
2044 descr->width = rect.right - rect.left;
2045 descr->height = rect.bottom - rect.top;
2046 descr->items = NULL;
2047 descr->nb_items = 0;
2048 descr->top_item = 0;
2049 descr->selected_item = -1;
2050 descr->focus_item = 0;
2051 descr->anchor_item = -1;
2052 descr->item_height = 1;
2053 descr->page_size = 1;
2054 descr->column_width = 150;
2055 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2056 descr->horz_pos = 0;
2057 descr->nb_tabs = 0;
2058 descr->tabs = NULL;
2059 descr->caret_on = TRUE;
2060 descr->captured = FALSE;
2061 descr->font = 0;
2062 descr->locale = 0; /* FIXME */
2063 descr->lphc = lphc;
2065 if( lphc )
2067 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2068 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2069 descr->owner = lphc->self->hwndSelf;
2072 *(LB_DESCR **)wnd->wExtra = descr;
2074 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2076 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2077 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2078 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2079 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2081 if (descr->style & LBS_OWNERDRAWFIXED)
2083 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2085 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2086 descr->item_height = lphc->fixedOwnerDrawHeight;
2088 else
2090 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2092 mis.CtlType = ODT_LISTBOX;
2093 mis.CtlID = id;
2094 mis.itemID = -1;
2095 mis.itemWidth = 0;
2096 mis.itemData = 0;
2097 mis.itemHeight = descr->item_height;
2098 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2099 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2103 return TRUE;
2107 /***********************************************************************
2108 * LISTBOX_Destroy
2110 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2112 LISTBOX_ResetContent( wnd, descr );
2113 HeapDestroy( descr->heap );
2114 HeapFree( GetProcessHeap(), 0, descr );
2115 wnd->wExtra[0] = 0;
2116 return TRUE;
2120 /***********************************************************************
2121 * ListBoxWndProc
2123 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2124 WPARAM wParam, LPARAM lParam )
2126 LRESULT ret;
2127 LB_DESCR *descr;
2128 WND *wnd = WIN_FindWndPtr( hwnd );
2129 LRESULT retvalue;
2131 if (!wnd) return 0;
2132 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2134 if (msg == WM_CREATE)
2136 if (!LISTBOX_Create( wnd, NULL ))
2138 retvalue = -1;
2139 goto END;
2141 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2142 hwnd, *(LB_DESCR **)wnd->wExtra );
2143 retvalue = 0;
2144 goto END;
2146 /* Ignore all other messages before we get a WM_CREATE */
2147 retvalue = DefWindowProcA( hwnd, msg, wParam, lParam );
2148 goto END;
2151 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2152 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2153 switch(msg)
2155 case LB_RESETCONTENT16:
2156 case LB_RESETCONTENT:
2157 LISTBOX_ResetContent( wnd, descr );
2158 retvalue = 0;
2159 goto END;
2161 case LB_ADDSTRING16:
2162 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2163 /* fall through */
2164 case LB_ADDSTRING:
2165 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2166 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2167 goto END;
2169 case LB_INSERTSTRING16:
2170 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2171 wParam = (INT)(INT16)wParam;
2172 /* fall through */
2173 case LB_INSERTSTRING:
2174 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2175 goto END;
2177 case LB_ADDFILE16:
2178 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2179 /* fall through */
2180 case LB_ADDFILE:
2181 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2182 retvalue = LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2183 goto END;
2185 case LB_DELETESTRING16:
2186 case LB_DELETESTRING:
2187 retvalue = LISTBOX_RemoveItem( wnd, descr, wParam );
2188 goto END;
2190 case LB_GETITEMDATA16:
2191 case LB_GETITEMDATA:
2192 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2194 retvalue = LB_ERR;
2195 goto END;
2197 retvalue = descr->items[wParam].data;
2198 goto END;
2200 case LB_SETITEMDATA16:
2201 case LB_SETITEMDATA:
2202 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2204 retvalue = LB_ERR;
2205 goto END;
2207 descr->items[wParam].data = (DWORD)lParam;
2208 retvalue = LB_OKAY;
2209 goto END;
2211 case LB_GETCOUNT16:
2212 case LB_GETCOUNT:
2213 retvalue = descr->nb_items;
2214 goto END;
2216 case LB_GETTEXT16:
2217 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2218 /* fall through */
2219 case LB_GETTEXT:
2220 retvalue = LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2221 goto END;
2223 case LB_GETTEXTLEN16:
2224 /* fall through */
2225 case LB_GETTEXTLEN:
2226 if (wParam >= descr->nb_items)
2228 retvalue = LB_ERR;
2229 goto END;
2231 retvalue = (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2232 : sizeof(DWORD));
2233 goto END;
2235 case LB_GETCURSEL16:
2236 case LB_GETCURSEL:
2237 if (descr->nb_items==0)
2238 retvalue = LB_ERR;
2239 else
2241 retvalue = descr->selected_item;
2242 if (retvalue == -1) retvalue = descr->focus_item;
2244 /* otherwise, if the user tries to move the selection with the */
2245 /* arrow keys, we will give the application something to choke on */
2246 goto END;
2248 case LB_GETTOPINDEX16:
2249 case LB_GETTOPINDEX:
2250 retvalue = descr->top_item;
2251 goto END;
2253 case LB_GETITEMHEIGHT16:
2254 case LB_GETITEMHEIGHT:
2255 retvalue = LISTBOX_GetItemHeight( wnd, descr, wParam );
2256 goto END;
2258 case LB_SETITEMHEIGHT16:
2259 lParam = LOWORD(lParam);
2260 /* fall through */
2261 case LB_SETITEMHEIGHT:
2262 retvalue = LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2263 goto END;
2265 case LB_ITEMFROMPOINT:
2267 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
2268 RECT rect = { 0, 0, descr->width, descr->height };
2269 retvalue = MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2270 PtInRect( &rect, pt ) );
2271 goto END;
2274 case LB_SETCARETINDEX16:
2275 case LB_SETCARETINDEX:
2276 retvalue = LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2277 goto END;
2279 case LB_GETCARETINDEX16:
2280 case LB_GETCARETINDEX:
2281 retvalue = descr->focus_item;
2282 goto END;
2284 case LB_SETTOPINDEX16:
2285 case LB_SETTOPINDEX:
2286 retvalue = LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2287 goto END;
2289 case LB_SETCOLUMNWIDTH16:
2290 case LB_SETCOLUMNWIDTH:
2291 retvalue = LISTBOX_SetColumnWidth( wnd, descr, wParam );
2292 goto END;
2294 case LB_GETITEMRECT16:
2296 RECT rect;
2297 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2298 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2300 retvalue = ret;
2301 goto END;
2303 case LB_GETITEMRECT:
2304 retvalue = LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2305 goto END;
2307 case LB_FINDSTRING16:
2308 wParam = (INT)(INT16)wParam;
2309 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2310 /* fall through */
2311 case LB_FINDSTRING:
2312 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2313 goto END;
2315 case LB_FINDSTRINGEXACT16:
2316 wParam = (INT)(INT16)wParam;
2317 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2318 /* fall through */
2319 case LB_FINDSTRINGEXACT:
2320 retvalue = LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2321 goto END;
2323 case LB_SELECTSTRING16:
2324 wParam = (INT)(INT16)wParam;
2325 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2326 /* fall through */
2327 case LB_SELECTSTRING:
2329 INT index = LISTBOX_FindString( wnd, descr, wParam,
2330 (LPCSTR)lParam, FALSE );
2331 if (index == LB_ERR)
2333 retvalue = LB_ERR;
2334 goto END;
2336 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2337 retvalue = index;
2338 goto END;
2341 case LB_GETSEL16:
2342 wParam = (INT)(INT16)wParam;
2343 /* fall through */
2344 case LB_GETSEL:
2345 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2347 retvalue = LB_ERR;
2348 goto END;
2350 retvalue = descr->items[wParam].selected;
2351 goto END;
2353 case LB_SETSEL16:
2354 lParam = (INT)(INT16)lParam;
2355 /* fall through */
2356 case LB_SETSEL:
2357 retvalue = LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2358 goto END;
2360 case LB_SETCURSEL16:
2361 wParam = (INT)(INT16)wParam;
2362 /* fall through */
2363 case LB_SETCURSEL:
2364 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2365 retvalue = LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2366 goto END;
2368 case LB_GETSELCOUNT16:
2369 case LB_GETSELCOUNT:
2370 retvalue = LISTBOX_GetSelCount( wnd, descr );
2371 goto END;
2373 case LB_GETSELITEMS16:
2374 retvalue = LISTBOX_GetSelItems16( wnd, descr, wParam,
2375 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2376 goto END;
2378 case LB_GETSELITEMS:
2379 retvalue = LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2380 goto END;
2382 case LB_SELITEMRANGE16:
2383 case LB_SELITEMRANGE:
2384 if (LOWORD(lParam) <= HIWORD(lParam))
2386 retvalue = LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2387 HIWORD(lParam), wParam );
2389 else
2391 retvalue = LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2392 LOWORD(lParam), wParam );
2394 goto END;
2396 case LB_SELITEMRANGEEX16:
2397 case LB_SELITEMRANGEEX:
2398 if ((INT)lParam >= (INT)wParam)
2399 retvalue = LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2400 else
2401 retvalue = LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2402 goto END;
2404 case LB_GETHORIZONTALEXTENT16:
2405 case LB_GETHORIZONTALEXTENT:
2406 retvalue = descr->horz_extent;
2407 goto END;
2409 case LB_SETHORIZONTALEXTENT16:
2410 case LB_SETHORIZONTALEXTENT:
2411 retvalue = LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2412 goto END;
2414 case LB_GETANCHORINDEX16:
2415 case LB_GETANCHORINDEX:
2416 retvalue = descr->anchor_item;
2417 goto END;
2419 case LB_SETANCHORINDEX16:
2420 wParam = (INT)(INT16)wParam;
2421 /* fall through */
2422 case LB_SETANCHORINDEX:
2423 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2425 retvalue = LB_ERR;
2426 goto END;
2428 descr->anchor_item = (INT)wParam;
2429 retvalue = LB_OKAY;
2430 goto END;
2432 case LB_DIR16:
2433 retvalue = LISTBOX_Directory( wnd, descr, wParam,
2434 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2435 goto END;
2437 case LB_DIR:
2438 retvalue = LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2439 goto END;
2441 case LB_GETLOCALE:
2442 retvalue = descr->locale;
2443 goto END;
2445 case LB_SETLOCALE:
2446 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2447 retvalue = LB_OKAY;
2448 goto END;
2450 case LB_INITSTORAGE:
2451 retvalue = LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2452 goto END;
2454 case LB_SETCOUNT:
2455 retvalue = LISTBOX_SetCount( wnd, descr, (INT)wParam );
2456 goto END;
2458 case LB_SETTABSTOPS16:
2459 retvalue = LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2460 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2461 goto END;
2463 case LB_SETTABSTOPS:
2464 retvalue = LISTBOX_SetTabStops( wnd, descr, wParam,
2465 (LPINT)lParam, FALSE );
2466 goto END;
2468 case LB_CARETON16:
2469 case LB_CARETON:
2470 if (descr->caret_on)
2472 retvalue = LB_OKAY;
2473 goto END;
2475 descr->caret_on = TRUE;
2476 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2477 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2478 retvalue = LB_OKAY;
2479 goto END;
2481 case LB_CARETOFF16:
2482 case LB_CARETOFF:
2483 if (!descr->caret_on)
2485 retvalue = LB_OKAY;
2486 goto END;
2488 descr->caret_on = FALSE;
2489 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2490 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2491 retvalue = LB_OKAY;
2492 goto END;
2494 case WM_DESTROY:
2495 retvalue = LISTBOX_Destroy( wnd, descr );
2496 goto END;
2498 case WM_ENABLE:
2499 InvalidateRect( hwnd, NULL, TRUE );
2500 retvalue = 0;
2501 goto END;
2503 case WM_SETREDRAW:
2504 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2505 retvalue = 0;
2506 goto END;
2508 case WM_GETDLGCODE:
2509 retvalue =DLGC_WANTARROWS | DLGC_WANTCHARS;
2510 goto END;
2511 case WM_PAINT:
2513 PAINTSTRUCT ps;
2514 HDC hdc = ( wParam ) ? ((HDC)wParam)
2515 : BeginPaint( hwnd, &ps );
2516 ret = LISTBOX_Paint( wnd, descr, hdc );
2517 if( !wParam ) EndPaint( hwnd, &ps );
2519 retvalue =ret;
2520 goto END;
2521 case WM_SIZE:
2522 LISTBOX_UpdateSize( wnd, descr );
2523 retvalue =0;
2524 goto END;
2525 case WM_GETFONT:
2526 retvalue =descr->font;
2527 goto END;
2528 case WM_SETFONT:
2529 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2530 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2531 retvalue =0;
2532 goto END;
2533 case WM_SETFOCUS:
2534 descr->caret_on = TRUE;
2535 if (descr->focus_item != -1)
2536 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2537 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2538 retvalue =0;
2539 goto END;
2540 case WM_KILLFOCUS:
2541 if ((descr->focus_item != -1) && descr->caret_on)
2542 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2543 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2544 retvalue =0;
2545 goto END;
2546 case WM_HSCROLL:
2547 retvalue =LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2548 goto END;
2549 case WM_VSCROLL:
2550 retvalue =LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2551 goto END;
2552 case WM_LBUTTONDOWN:
2553 retvalue =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2554 (INT16)LOWORD(lParam),
2555 (INT16)HIWORD(lParam) );
2556 goto END;
2557 case WM_LBUTTONDBLCLK:
2558 if (descr->style & LBS_NOTIFY)
2559 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2560 retvalue =0;
2561 goto END;
2562 case WM_MOUSEMOVE:
2563 if (GetCapture() == hwnd)
2564 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2565 (INT16)HIWORD(lParam) );
2566 retvalue =0;
2567 goto END;
2568 case WM_LBUTTONUP:
2569 retvalue =LISTBOX_HandleLButtonUp( wnd, descr );
2570 goto END;
2571 case WM_KEYDOWN:
2572 retvalue =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2573 goto END;
2574 case WM_CHAR:
2575 retvalue =LISTBOX_HandleChar( wnd, descr, wParam );
2576 goto END;
2577 case WM_SYSTIMER:
2578 retvalue =LISTBOX_HandleSystemTimer( wnd, descr );
2579 goto END;
2580 case WM_ERASEBKGND:
2581 if (IS_OWNERDRAW(descr))
2583 RECT rect;
2584 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2585 wParam, (LPARAM)wnd->hwndSelf );
2586 GetClientRect(hwnd, &rect);
2587 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2589 retvalue =1;
2590 goto END;
2591 case WM_DROPFILES:
2592 if( !descr->lphc )
2594 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2595 goto END;
2597 break;
2599 case WM_DROPOBJECT:
2600 case WM_QUERYDROPOBJECT:
2601 case WM_DRAGSELECT:
2602 case WM_DRAGMOVE:
2603 if( !descr->lphc )
2605 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2606 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2607 dragInfo->pt.y );
2608 retvalue =SendMessageA( descr->owner, msg, wParam, lParam );
2609 goto END;
2611 break;
2613 case WM_NCCREATE:
2614 if (TWEAK_WineLook > WIN31_LOOK)
2615 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2616 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2617 goto END;
2618 default:
2619 if ((msg >= WM_USER) && (msg < 0xc000))
2620 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2621 hwnd, msg, wParam, lParam );
2622 retvalue =DefWindowProcA( hwnd, msg, wParam, lParam );
2623 goto END;
2625 retvalue =0;
2626 END:
2627 WIN_ReleaseWndPtr(wnd);
2628 return retvalue;
2631 /***********************************************************************
2632 * COMBO_Directory
2634 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2636 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2638 if( wnd )
2640 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2641 if( descr )
2643 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2645 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2646 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2647 WIN_ReleaseWndPtr(wnd);
2648 return lRet;
2650 WIN_ReleaseWndPtr(wnd);
2652 return CB_ERR;
2655 /***********************************************************************
2656 * ComboLBWndProc
2658 * NOTE: in Windows, winproc address of the ComboLBox is the same
2659 * as that of the Listbox.
2661 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2662 WPARAM wParam, LPARAM lParam )
2664 LRESULT lRet = 0;
2665 WND *wnd = WIN_FindWndPtr( hwnd );
2667 if (wnd)
2669 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2671 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2672 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2674 if( descr || msg == WM_CREATE )
2676 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2678 switch( msg )
2680 case WM_CREATE:
2681 #define lpcs ((LPCREATESTRUCTA)lParam)
2682 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2683 (UINT)lpcs->lpCreateParams);
2685 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2686 #undef lpcs
2687 lRet =LISTBOX_Create( wnd, lphc );
2688 goto END;
2689 case WM_LBUTTONDOWN:
2690 lRet =LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2691 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2692 goto END;
2693 /* avoid activation at all costs */
2695 case WM_MOUSEACTIVATE:
2696 lRet =MA_NOACTIVATE;
2697 goto END;
2698 case WM_NCACTIVATE:
2699 lRet =FALSE;
2700 goto END;
2701 case WM_KEYDOWN:
2702 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2704 /* for some reason(?) Windows makes it possible to
2705 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2707 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2708 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2709 && (wParam == VK_DOWN || wParam == VK_UP)) )
2711 COMBO_FlipListbox( lphc, FALSE );
2712 lRet =0;
2713 goto END;
2716 lRet =LISTBOX_HandleKeyDown( wnd, descr, wParam );
2717 goto END;
2719 case LB_SETCURSEL16:
2720 case LB_SETCURSEL:
2721 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2722 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2723 goto END;
2724 case WM_NCDESTROY:
2725 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2726 lphc->hWndLBox = 0;
2727 /* fall through */
2729 default:
2730 lRet =ListBoxWndProc( hwnd, msg, wParam, lParam );
2731 goto END;
2734 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2736 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2738 END:
2739 WIN_ReleaseWndPtr(wnd);
2740 return lRet;