user32: Reimplement 16-bit clipboard functions on top of the 32-bit ones.
[wine] / dlls / user32 / listbox.c
1 /*
2  * Listbox controls
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTES
21  *
22  * This code was audited for completeness against the documented features
23  * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
24  * 
25  * Unless otherwise noted, we believe this code to be complete, as per
26  * the specification mentioned above.
27  * If you discover missing features, or bugs, please note them below.
28  *
29  * TODO:
30  *    - GetListBoxInfo()
31  *    - LB_GETLISTBOXINFO
32  *    - LBS_NODATA
33  */
34
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "wine/unicode.h"
43 #include "user_private.h"
44 #include "controls.h"
45 #include "wine/exception.h"
46 #include "wine/debug.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
49
50 /* Items array granularity */
51 #define LB_ARRAY_GRANULARITY 16
52
53 /* Scrolling timeout in ms */
54 #define LB_SCROLL_TIMEOUT 50
55
56 /* Listbox system timer id */
57 #define LB_TIMER_ID  2
58
59 /* flag listbox changed while setredraw false - internal style */
60 #define LBS_DISPLAYCHANGED 0x80000000
61
62 /* Item structure */
63 typedef struct
64 {
65     LPWSTR    str;       /* Item text */
66     BOOL      selected;  /* Is item selected? */
67     UINT      height;    /* Item height (only for OWNERDRAWVARIABLE) */
68     ULONG_PTR data;      /* User data */
69 } LB_ITEMDATA;
70
71 /* Listbox structure */
72 typedef struct
73 {
74     HWND        self;           /* Our own window handle */
75     HWND        owner;          /* Owner window to send notifications to */
76     UINT        style;          /* Window style */
77     INT         width;          /* Window width */
78     INT         height;         /* Window height */
79     LB_ITEMDATA  *items;        /* Array of items */
80     INT         nb_items;       /* Number of items */
81     INT         top_item;       /* Top visible item */
82     INT         selected_item;  /* Selected item */
83     INT         focus_item;     /* Item that has the focus */
84     INT         anchor_item;    /* Anchor item for extended selection */
85     INT         item_height;    /* Default item height */
86     INT         page_size;      /* Items per listbox page */
87     INT         column_width;   /* Column width for multi-column listboxes */
88     INT         horz_extent;    /* Horizontal extent (0 if no hscroll) */
89     INT         horz_pos;       /* Horizontal position */
90     INT         nb_tabs;        /* Number of tabs in array */
91     INT        *tabs;           /* Array of tabs */
92     INT         avg_char_width; /* Average width of characters */
93     BOOL        caret_on;       /* Is caret on? */
94     BOOL        captured;       /* Is mouse captured? */
95     BOOL        in_focus;
96     HFONT       font;           /* Current font */
97     LCID          locale;       /* Current locale for string comparisons */
98     LPHEADCOMBO   lphc;         /* ComboLBox */
99 } LB_DESCR;
100
101
102 #define IS_OWNERDRAW(descr) \
103     ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104
105 #define HAS_STRINGS(descr) \
106     (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
107
108
109 #define IS_MULTISELECT(descr) \
110     ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
111      !((descr)->style & LBS_NOSEL))
112
113 #define SEND_NOTIFICATION(descr,code) \
114     (SendMessageW( (descr)->owner, WM_COMMAND, \
115      MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
116
117 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
118
119 /* Current timer status */
120 typedef enum
121 {
122     LB_TIMER_NONE,
123     LB_TIMER_UP,
124     LB_TIMER_LEFT,
125     LB_TIMER_DOWN,
126     LB_TIMER_RIGHT
127 } TIMER_DIRECTION;
128
129 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
130
131 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
132
133 /*********************************************************************
134  * listbox class descriptor
135  */
136 static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
137 const struct builtin_class_descr LISTBOX_builtin_class =
138 {
139     listboxW,             /* name */
140     CS_DBLCLKS /*| CS_PARENTDC*/,  /* style */
141     WINPROC_LISTBOX,      /* proc */
142     sizeof(LB_DESCR *),   /* extra */
143     IDC_ARROW,            /* cursor */
144     0                     /* brush */
145 };
146
147
148 /*********************************************************************
149  * combolbox class descriptor
150  */
151 static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
152 const struct builtin_class_descr COMBOLBOX_builtin_class =
153 {
154     combolboxW,           /* name */
155     CS_DBLCLKS | CS_SAVEBITS,  /* style */
156     WINPROC_LISTBOX,      /* proc */
157     sizeof(LB_DESCR *),   /* extra */
158     IDC_ARROW,            /* cursor */
159     0                     /* brush */
160 };
161
162
163 /* check whether app is a Win 3.1 app */
164 static inline BOOL is_old_app( LB_DESCR *descr )
165 {
166     return (GetExpWinVer16( GetWindowLongPtrW(descr->self, GWLP_HINSTANCE) ) & 0xFF00 ) == 0x0300;
167 }
168
169
170 /***********************************************************************
171  *           LISTBOX_GetCurrentPageSize
172  *
173  * Return the current page size
174  */
175 static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
176 {
177     INT i, height;
178     if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
179     for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
180     {
181         if ((height += descr->items[i].height) > descr->height) break;
182     }
183     if (i == descr->top_item) return 1;
184     else return i - descr->top_item;
185 }
186
187
188 /***********************************************************************
189  *           LISTBOX_GetMaxTopIndex
190  *
191  * Return the maximum possible index for the top of the listbox.
192  */
193 static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
194 {
195     INT max, page;
196
197     if (descr->style & LBS_OWNERDRAWVARIABLE)
198     {
199         page = descr->height;
200         for (max = descr->nb_items - 1; max >= 0; max--)
201             if ((page -= descr->items[max].height) < 0) break;
202         if (max < descr->nb_items - 1) max++;
203     }
204     else if (descr->style & LBS_MULTICOLUMN)
205     {
206         if ((page = descr->width / descr->column_width) < 1) page = 1;
207         max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
208         max = (max - page) * descr->page_size;
209     }
210     else
211     {
212         max = descr->nb_items - descr->page_size;
213     }
214     if (max < 0) max = 0;
215     return max;
216 }
217
218
219 /***********************************************************************
220  *           LISTBOX_UpdateScroll
221  *
222  * Update the scrollbars. Should be called whenever the content
223  * of the listbox changes.
224  */
225 static void LISTBOX_UpdateScroll( LB_DESCR *descr )
226 {
227     SCROLLINFO info;
228
229     /* Check the listbox scroll bar flags individually before we call
230        SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
231        no WS_VSCROLL, we end up with an uninitialized, visible horizontal
232        scroll bar when we do not need one.
233     if (!(descr->style & WS_VSCROLL)) return;
234     */
235
236     /*   It is important that we check descr->style, and not wnd->dwStyle,
237        for WS_VSCROLL, as the former is exactly the one passed in
238        argument to CreateWindow.
239          In Windows (and from now on in Wine :) a listbox created
240        with such a style (no WS_SCROLL) does not update
241        the scrollbar with listbox-related data, thus letting
242        the programmer use it for his/her own purposes. */
243
244     if (descr->style & LBS_NOREDRAW) return;
245     info.cbSize = sizeof(info);
246
247     if (descr->style & LBS_MULTICOLUMN)
248     {
249         info.nMin  = 0;
250         info.nMax  = (descr->nb_items - 1) / descr->page_size;
251         info.nPos  = descr->top_item / descr->page_size;
252         info.nPage = descr->width / descr->column_width;
253         if (info.nPage < 1) info.nPage = 1;
254         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
255         if (descr->style & LBS_DISABLENOSCROLL)
256             info.fMask |= SIF_DISABLENOSCROLL;
257         if (descr->style & WS_HSCROLL)
258             SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
259         info.nMax = 0;
260         info.fMask = SIF_RANGE;
261         if (descr->style & WS_VSCROLL)
262             SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
263     }
264     else
265     {
266         info.nMin  = 0;
267         info.nMax  = descr->nb_items - 1;
268         info.nPos  = descr->top_item;
269         info.nPage = LISTBOX_GetCurrentPageSize( descr );
270         info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
271         if (descr->style & LBS_DISABLENOSCROLL)
272             info.fMask |= SIF_DISABLENOSCROLL;
273         if (descr->style & WS_VSCROLL)
274             SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
275
276         if (descr->horz_extent)
277         {
278             info.nMin  = 0;
279             info.nMax  = descr->horz_extent - 1;
280             info.nPos  = descr->horz_pos;
281             info.nPage = descr->width;
282             info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
283             if (descr->style & LBS_DISABLENOSCROLL)
284                 info.fMask |= SIF_DISABLENOSCROLL;
285             if (descr->style & WS_HSCROLL)
286                 SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
287         }
288     }
289 }
290
291
292 /***********************************************************************
293  *           LISTBOX_SetTopItem
294  *
295  * Set the top item of the listbox, scrolling up or down if necessary.
296  */
297 static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
298 {
299     INT max = LISTBOX_GetMaxTopIndex( descr );
300
301     TRACE("setting top item %d, scroll %d\n", index, scroll);
302
303     if (index > max) index = max;
304     if (index < 0) index = 0;
305     if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
306     if (descr->top_item == index) return LB_OKAY;
307     if (descr->style & LBS_MULTICOLUMN)
308     {
309         INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
310         if (scroll && (abs(diff) < descr->width))
311             ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
312                               SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
313
314         else
315             scroll = FALSE;
316     }
317     else if (scroll)
318     {
319         INT diff;
320         if (descr->style & LBS_OWNERDRAWVARIABLE)
321         {
322             INT i;
323             diff = 0;
324             if (index > descr->top_item)
325             {
326                 for (i = index - 1; i >= descr->top_item; i--)
327                     diff -= descr->items[i].height;
328             }
329             else
330             {
331                 for (i = index; i < descr->top_item; i++)
332                     diff += descr->items[i].height;
333             }
334         }
335         else
336             diff = (descr->top_item - index) * descr->item_height;
337
338         if (abs(diff) < descr->height)
339             ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
340                             SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
341         else
342             scroll = FALSE;
343     }
344     if (!scroll) InvalidateRect( descr->self, NULL, TRUE );
345     descr->top_item = index;
346     LISTBOX_UpdateScroll( descr );
347     return LB_OKAY;
348 }
349
350
351 /***********************************************************************
352  *           LISTBOX_UpdatePage
353  *
354  * Update the page size. Should be called when the size of
355  * the client area or the item height changes.
356  */
357 static void LISTBOX_UpdatePage( LB_DESCR *descr )
358 {
359     INT page_size;
360
361     if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
362                        page_size = 1;
363     if (page_size == descr->page_size) return;
364     descr->page_size = page_size;
365     if (descr->style & LBS_MULTICOLUMN)
366         InvalidateRect( descr->self, NULL, TRUE );
367     LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
368 }
369
370
371 /***********************************************************************
372  *           LISTBOX_UpdateSize
373  *
374  * Update the size of the listbox. Should be called when the size of
375  * the client area changes.
376  */
377 static void LISTBOX_UpdateSize( LB_DESCR *descr )
378 {
379     RECT rect;
380
381     GetClientRect( descr->self, &rect );
382     descr->width  = rect.right - rect.left;
383     descr->height = rect.bottom - rect.top;
384     if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
385     {
386         INT remaining;
387         RECT rect;
388
389         GetWindowRect( descr->self, &rect );
390         if(descr->item_height != 0)
391             remaining = descr->height % descr->item_height;
392         else
393             remaining = 0;
394         if ((descr->height > descr->item_height) && remaining)
395         {
396             if (is_old_app(descr))
397             { /* give a margin for error to 16 bits programs - if we need
398                  less than the height of the nonclient area, round to the
399                  *next* number of items */
400                 int ncheight = rect.bottom - rect.top - descr->height;
401                 if ((descr->item_height - remaining) <= ncheight)
402                     remaining = remaining - descr->item_height;
403             }
404             TRACE("[%p]: changing height %d -> %d\n",
405                   descr->self, descr->height, descr->height - remaining );
406             SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
407                           rect.bottom - rect.top - remaining,
408                           SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
409             return;
410         }
411     }
412     TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
413     LISTBOX_UpdatePage( descr );
414     LISTBOX_UpdateScroll( descr );
415
416     /* Invalidate the focused item so it will be repainted correctly */
417     if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
418     {
419         InvalidateRect( descr->self, &rect, FALSE );
420     }
421 }
422
423
424 /***********************************************************************
425  *           LISTBOX_GetItemRect
426  *
427  * Get the rectangle enclosing an item, in listbox client coordinates.
428  * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
429  */
430 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect )
431 {
432     /* Index <= 0 is legal even on empty listboxes */
433     if (index && (index >= descr->nb_items))
434     {
435         memset(rect, 0, sizeof(*rect));
436         SetLastError(ERROR_INVALID_INDEX);
437         return LB_ERR;
438     }
439     SetRect( rect, 0, 0, descr->width, descr->height );
440     if (descr->style & LBS_MULTICOLUMN)
441     {
442         INT col = (index / descr->page_size) -
443                         (descr->top_item / descr->page_size);
444         rect->left += col * descr->column_width;
445         rect->right = rect->left + descr->column_width;
446         rect->top += (index % descr->page_size) * descr->item_height;
447         rect->bottom = rect->top + descr->item_height;
448     }
449     else if (descr->style & LBS_OWNERDRAWVARIABLE)
450     {
451         INT i;
452         rect->right += descr->horz_pos;
453         if ((index >= 0) && (index < descr->nb_items))
454         {
455             if (index < descr->top_item)
456             {
457                 for (i = descr->top_item-1; i >= index; i--)
458                     rect->top -= descr->items[i].height;
459             }
460             else
461             {
462                 for (i = descr->top_item; i < index; i++)
463                     rect->top += descr->items[i].height;
464             }
465             rect->bottom = rect->top + descr->items[index].height;
466
467         }
468     }
469     else
470     {
471         rect->top += (index - descr->top_item) * descr->item_height;
472         rect->bottom = rect->top + descr->item_height;
473         rect->right += descr->horz_pos;
474     }
475
476     TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));
477
478     return ((rect->left < descr->width) && (rect->right > 0) &&
479             (rect->top < descr->height) && (rect->bottom > 0));
480 }
481
482
483 /***********************************************************************
484  *           LISTBOX_GetItemFromPoint
485  *
486  * Return the item nearest from point (x,y) (in client coordinates).
487  */
488 static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
489 {
490     INT index = descr->top_item;
491
492     if (!descr->nb_items) return -1;  /* No items */
493     if (descr->style & LBS_OWNERDRAWVARIABLE)
494     {
495         INT pos = 0;
496         if (y >= 0)
497         {
498             while (index < descr->nb_items)
499             {
500                 if ((pos += descr->items[index].height) > y) break;
501                 index++;
502             }
503         }
504         else
505         {
506             while (index > 0)
507             {
508                 index--;
509                 if ((pos -= descr->items[index].height) <= y) break;
510             }
511         }
512     }
513     else if (descr->style & LBS_MULTICOLUMN)
514     {
515         if (y >= descr->item_height * descr->page_size) return -1;
516         if (y >= 0) index += y / descr->item_height;
517         if (x >= 0) index += (x / descr->column_width) * descr->page_size;
518         else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
519     }
520     else
521     {
522         index += (y / descr->item_height);
523     }
524     if (index < 0) return 0;
525     if (index >= descr->nb_items) return -1;
526     return index;
527 }
528
529
530 /***********************************************************************
531  *           LISTBOX_PaintItem
532  *
533  * Paint an item.
534  */
535 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, 
536                                INT index, UINT action, BOOL ignoreFocus )
537 {
538     LB_ITEMDATA *item = NULL;
539     if (index < descr->nb_items) item = &descr->items[index];
540
541     if (IS_OWNERDRAW(descr))
542     {
543         DRAWITEMSTRUCT dis;
544         RECT r;
545         HRGN hrgn;
546
547         if (!item)
548         {
549             if (action == ODA_FOCUS)
550                 DrawFocusRect( hdc, rect );
551             else
552                 ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
553             return;
554         }
555
556         /* some programs mess with the clipping region when
557         drawing the item, *and* restore the previous region
558         after they are done, so a region has better to exist
559         else everything ends clipped */
560         GetClientRect(descr->self, &r);
561         hrgn = CreateRectRgnIndirect(&r);
562         SelectClipRgn( hdc, hrgn);
563         DeleteObject( hrgn );
564
565         dis.CtlType      = ODT_LISTBOX;
566         dis.CtlID        = GetWindowLongPtrW( descr->self, GWLP_ID );
567         dis.hwndItem     = descr->self;
568         dis.itemAction   = action;
569         dis.hDC          = hdc;
570         dis.itemID       = index;
571         dis.itemState    = 0;
572         if (item->selected) dis.itemState |= ODS_SELECTED;
573         if (!ignoreFocus && (descr->focus_item == index) &&
574             (descr->caret_on) &&
575             (descr->in_focus)) dis.itemState |= ODS_FOCUS;
576         if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
577         dis.itemData     = item->data;
578         dis.rcItem       = *rect;
579         TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
580               descr->self, index, item ? debugstr_w(item->str) : "", action,
581               dis.itemState, wine_dbgstr_rect(rect) );
582         SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
583     }
584     else
585     {
586         COLORREF oldText = 0, oldBk = 0;
587
588         if (action == ODA_FOCUS)
589         {
590             DrawFocusRect( hdc, rect );
591             return;
592         }
593         if (item && item->selected)
594         {
595             oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
596             oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
597         }
598
599         TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
600               descr->self, index, item ? debugstr_w(item->str) : "", action,
601               wine_dbgstr_rect(rect) );
602         if (!item)
603             ExtTextOutW( hdc, rect->left + 1, rect->top,
604                            ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
605         else if (!(descr->style & LBS_USETABSTOPS))
606             ExtTextOutW( hdc, rect->left + 1, rect->top,
607                          ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
608                          strlenW(item->str), NULL );
609         else
610         {
611             /* Output empty string to paint background in the full width. */
612             ExtTextOutW( hdc, rect->left + 1, rect->top,
613                          ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
614             TabbedTextOutW( hdc, rect->left + 1 , rect->top,
615                             item->str, strlenW(item->str),
616                             descr->nb_tabs, descr->tabs, 0);
617         }
618         if (item && item->selected)
619         {
620             SetBkColor( hdc, oldBk );
621             SetTextColor( hdc, oldText );
622         }
623         if (!ignoreFocus && (descr->focus_item == index) &&
624             (descr->caret_on) &&
625             (descr->in_focus)) DrawFocusRect( hdc, rect );
626     }
627 }
628
629
630 /***********************************************************************
631  *           LISTBOX_SetRedraw
632  *
633  * Change the redraw flag.
634  */
635 static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
636 {
637     if (on)
638     {
639         if (!(descr->style & LBS_NOREDRAW)) return;
640         descr->style &= ~LBS_NOREDRAW;
641         if (descr->style & LBS_DISPLAYCHANGED)
642         {     /* page was changed while setredraw false, refresh automatically */
643             InvalidateRect(descr->self, NULL, TRUE);
644             if ((descr->top_item + descr->page_size) > descr->nb_items)
645             {      /* reset top of page if less than number of items/page */
646                 descr->top_item = descr->nb_items - descr->page_size;
647                 if (descr->top_item < 0) descr->top_item = 0;
648             }
649             descr->style &= ~LBS_DISPLAYCHANGED;
650         }
651         LISTBOX_UpdateScroll( descr );
652     }
653     else descr->style |= LBS_NOREDRAW;
654 }
655
656
657 /***********************************************************************
658  *           LISTBOX_RepaintItem
659  *
660  * Repaint a single item synchronously.
661  */
662 static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
663 {
664     HDC hdc;
665     RECT rect;
666     HFONT oldFont = 0;
667     HBRUSH hbrush, oldBrush = 0;
668
669     /* Do not repaint the item if the item is not visible */
670     if (!IsWindowVisible(descr->self)) return;
671     if (descr->style & LBS_NOREDRAW)
672     {
673         descr->style |= LBS_DISPLAYCHANGED;
674         return;
675     }
676     if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
677     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
678     if (descr->font) oldFont = SelectObject( hdc, descr->font );
679     hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
680                                    (WPARAM)hdc, (LPARAM)descr->self );
681     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
682     if (!IsWindowEnabled(descr->self))
683         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
684     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
685     LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
686     if (oldFont) SelectObject( hdc, oldFont );
687     if (oldBrush) SelectObject( hdc, oldBrush );
688     ReleaseDC( descr->self, hdc );
689 }
690
691
692 /***********************************************************************
693  *           LISTBOX_DrawFocusRect
694  */
695 static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
696 {
697     HDC hdc;
698     RECT rect;
699     HFONT oldFont = 0;
700
701     /* Do not repaint the item if the item is not visible */
702     if (!IsWindowVisible(descr->self)) return;
703
704     if (descr->focus_item == -1) return;
705     if (!descr->caret_on || !descr->in_focus) return;
706
707     if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
708     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
709     if (descr->font) oldFont = SelectObject( hdc, descr->font );
710     if (!IsWindowEnabled(descr->self))
711         SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
712     SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
713     LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, on ? FALSE : TRUE );
714     if (oldFont) SelectObject( hdc, oldFont );
715     ReleaseDC( descr->self, hdc );
716 }
717
718
719 /***********************************************************************
720  *           LISTBOX_InitStorage
721  */
722 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
723 {
724     LB_ITEMDATA *item;
725
726     nb_items += LB_ARRAY_GRANULARITY - 1;
727     nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
728     if (descr->items) {
729         nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
730         item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
731                               nb_items * sizeof(LB_ITEMDATA));
732     }
733     else {
734         item = HeapAlloc( GetProcessHeap(), 0,
735                               nb_items * sizeof(LB_ITEMDATA));
736     }
737
738     if (!item)
739     {
740         SEND_NOTIFICATION( descr, LBN_ERRSPACE );
741         return LB_ERRSPACE;
742     }
743     descr->items = item;
744     return LB_OKAY;
745 }
746
747
748 /***********************************************************************
749  *           LISTBOX_SetTabStops
750  */
751 static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
752 {
753     INT i;
754
755     if (!(descr->style & LBS_USETABSTOPS))
756     {
757         SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
758         return FALSE;
759     }
760
761     HeapFree( GetProcessHeap(), 0, descr->tabs );
762     if (!(descr->nb_tabs = count))
763     {
764         descr->tabs = NULL;
765         return TRUE;
766     }
767     if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
768                                             descr->nb_tabs * sizeof(INT) )))
769         return FALSE;
770     memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
771
772     /* convert into "dialog units"*/
773     for (i = 0; i < descr->nb_tabs; i++)
774         descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);
775
776     return TRUE;
777 }
778
779
780 /***********************************************************************
781  *           LISTBOX_GetText
782  */
783 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
784 {
785     DWORD len;
786
787     if ((index < 0) || (index >= descr->nb_items))
788     {
789         SetLastError(ERROR_INVALID_INDEX);
790         return LB_ERR;
791     }
792     if (HAS_STRINGS(descr))
793     {
794         if (!buffer)
795         {
796             len = strlenW(descr->items[index].str);
797             if( unicode )
798                 return len;
799             return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
800                                         NULL, 0, NULL, NULL );
801         }
802
803         TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
804
805         __TRY  /* hide a Delphi bug that passes a read-only buffer */
806         {
807             if(unicode)
808             {
809                 strcpyW( buffer, descr->items[index].str );
810                 len = strlenW(buffer);
811             }
812             else
813             {
814                 len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
815                                           (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
816             }
817         }
818         __EXCEPT_PAGE_FAULT
819         {
820             WARN( "got an invalid buffer (Delphi bug?)\n" );
821             SetLastError( ERROR_INVALID_PARAMETER );
822             return LB_ERR;
823         }
824         __ENDTRY
825     } else {
826         if (buffer)
827             *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
828         len = sizeof(DWORD);
829     }
830     return len;
831 }
832
833 static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
834 {
835     INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
836     if (ret == CSTR_LESS_THAN)
837         return -1;
838     if (ret == CSTR_EQUAL)
839         return 0;
840     if (ret == CSTR_GREATER_THAN)
841         return 1;
842     return -1;
843 }
844
845 /***********************************************************************
846  *           LISTBOX_FindStringPos
847  *
848  * Find the nearest string located before a given string in sort order.
849  * If 'exact' is TRUE, return an error if we don't get an exact match.
850  */
851 static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
852 {
853     INT index, min, max, res = -1;
854
855     if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
856     min = 0;
857     max = descr->nb_items;
858     while (min != max)
859     {
860         index = (min + max) / 2;
861         if (HAS_STRINGS(descr))
862             res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
863         else
864         {
865             COMPAREITEMSTRUCT cis;
866             UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
867
868             cis.CtlType    = ODT_LISTBOX;
869             cis.CtlID      = id;
870             cis.hwndItem   = descr->self;
871             /* note that some application (MetaStock) expects the second item
872              * to be in the listbox */
873             cis.itemID1    = -1;
874             cis.itemData1  = (ULONG_PTR)str;
875             cis.itemID2    = index;
876             cis.itemData2  = descr->items[index].data;
877             cis.dwLocaleId = descr->locale;
878             res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
879         }
880         if (!res) return index;
881         if (res < 0) max = index;
882         else min = index + 1;
883     }
884     return exact ? -1 : max;
885 }
886
887
888 /***********************************************************************
889  *           LISTBOX_FindFileStrPos
890  *
891  * Find the nearest string located before a given string in directory
892  * sort order (i.e. first files, then directories, then drives).
893  */
894 static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
895 {
896     INT min, max, res = -1;
897
898     if (!HAS_STRINGS(descr))
899         return LISTBOX_FindStringPos( descr, str, FALSE );
900     min = 0;
901     max = descr->nb_items;
902     while (min != max)
903     {
904         INT index = (min + max) / 2;
905         LPCWSTR p = descr->items[index].str;
906         if (*p == '[')  /* drive or directory */
907         {
908             if (*str != '[') res = -1;
909             else if (p[1] == '-')  /* drive */
910             {
911                 if (str[1] == '-') res = str[2] - p[2];
912                 else res = -1;
913             }
914             else  /* directory */
915             {
916                 if (str[1] == '-') res = 1;
917                 else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
918             }
919         }
920         else  /* filename */
921         {
922             if (*str == '[') res = 1;
923             else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
924         }
925         if (!res) return index;
926         if (res < 0) max = index;
927         else min = index + 1;
928     }
929     return max;
930 }
931
932
933 /***********************************************************************
934  *           LISTBOX_FindString
935  *
936  * Find the item beginning with a given string.
937  */
938 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
939 {
940     INT i;
941     LB_ITEMDATA *item;
942
943     if (start >= descr->nb_items) start = -1;
944     item = descr->items + start + 1;
945     if (HAS_STRINGS(descr))
946     {
947         if (!str || ! str[0] ) return LB_ERR;
948         if (exact)
949         {
950             for (i = start + 1; i < descr->nb_items; i++, item++)
951                 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
952             for (i = 0, item = descr->items; i <= start; i++, item++)
953                 if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
954         }
955         else
956         {
957  /* Special case for drives and directories: ignore prefix */
958 #define CHECK_DRIVE(item) \
959     if ((item)->str[0] == '[') \
960     { \
961         if (!strncmpiW( str, (item)->str+1, len )) return i; \
962         if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
963         return i; \
964     }
965
966             INT len = strlenW(str);
967             for (i = start + 1; i < descr->nb_items; i++, item++)
968             {
969                if (!strncmpiW( str, item->str, len )) return i;
970                CHECK_DRIVE(item);
971             }
972             for (i = 0, item = descr->items; i <= start; i++, item++)
973             {
974                if (!strncmpiW( str, item->str, len )) return i;
975                CHECK_DRIVE(item);
976             }
977 #undef CHECK_DRIVE
978         }
979     }
980     else
981     {
982         if (exact && (descr->style & LBS_SORT))
983             /* If sorted, use a WM_COMPAREITEM binary search */
984             return LISTBOX_FindStringPos( descr, str, TRUE );
985
986         /* Otherwise use a linear search */
987         for (i = start + 1; i < descr->nb_items; i++, item++)
988             if (item->data == (ULONG_PTR)str) return i;
989         for (i = 0, item = descr->items; i <= start; i++, item++)
990             if (item->data == (ULONG_PTR)str) return i;
991     }
992     return LB_ERR;
993 }
994
995
996 /***********************************************************************
997  *           LISTBOX_GetSelCount
998  */
999 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
1000 {
1001     INT i, count;
1002     const LB_ITEMDATA *item = descr->items;
1003
1004     if (!(descr->style & LBS_MULTIPLESEL) ||
1005         (descr->style & LBS_NOSEL))
1006       return LB_ERR;
1007     for (i = count = 0; i < descr->nb_items; i++, item++)
1008         if (item->selected) count++;
1009     return count;
1010 }
1011
1012
1013 /***********************************************************************
1014  *           LISTBOX_GetSelItems
1015  */
1016 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
1017 {
1018     INT i, count;
1019     const LB_ITEMDATA *item = descr->items;
1020
1021     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1022     for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
1023         if (item->selected) array[count++] = i;
1024     return count;
1025 }
1026
1027
1028 /***********************************************************************
1029  *           LISTBOX_Paint
1030  */
1031 static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
1032 {
1033     INT i, col_pos = descr->page_size - 1;
1034     RECT rect;
1035     RECT focusRect = {-1, -1, -1, -1};
1036     HFONT oldFont = 0;
1037     HBRUSH hbrush, oldBrush = 0;
1038
1039     if (descr->style & LBS_NOREDRAW) return 0;
1040
1041     SetRect( &rect, 0, 0, descr->width, descr->height );
1042     if (descr->style & LBS_MULTICOLUMN)
1043         rect.right = rect.left + descr->column_width;
1044     else if (descr->horz_pos)
1045     {
1046         SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1047         rect.right += descr->horz_pos;
1048     }
1049
1050     if (descr->font) oldFont = SelectObject( hdc, descr->font );
1051     hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1052                                    (WPARAM)hdc, (LPARAM)descr->self );
1053     if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1054     if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1055
1056     if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1057         (descr->in_focus))
1058     {
1059         /* Special case for empty listbox: paint focus rect */
1060         rect.bottom = rect.top + descr->item_height;
1061         ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1062                      &rect, NULL, 0, NULL );
1063         LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
1064         rect.top = rect.bottom;
1065     }
1066
1067     /* Paint all the item, regarding the selection
1068        Focus state will be painted after  */
1069
1070     for (i = descr->top_item; i < descr->nb_items; i++)
1071     {
1072         if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1073             rect.bottom = rect.top + descr->item_height;
1074         else
1075             rect.bottom = rect.top + descr->items[i].height;
1076
1077         if (i == descr->focus_item)
1078         {
1079             /* keep the focus rect, to paint the focus item after */
1080             focusRect.left = rect.left;
1081             focusRect.right = rect.right;
1082             focusRect.top = rect.top;
1083             focusRect.bottom = rect.bottom;
1084         }
1085         LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1086         rect.top = rect.bottom;
1087
1088         if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1089         {
1090             if (!IS_OWNERDRAW(descr))
1091             {
1092                 /* Clear the bottom of the column */
1093                 if (rect.top < descr->height)
1094                 {
1095                     rect.bottom = descr->height;
1096                     ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1097                                    &rect, NULL, 0, NULL );
1098                 }
1099             }
1100
1101             /* Go to the next column */
1102             rect.left += descr->column_width;
1103             rect.right += descr->column_width;
1104             rect.top = 0;
1105             col_pos = descr->page_size - 1;
1106         }
1107         else
1108         {
1109             col_pos--;
1110             if (rect.top >= descr->height) break;
1111         }
1112     }
1113
1114     /* Paint the focus item now */
1115     if (focusRect.top != focusRect.bottom &&
1116         descr->caret_on && descr->in_focus)
1117         LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1118
1119     if (!IS_OWNERDRAW(descr))
1120     {
1121         /* Clear the remainder of the client area */
1122         if (rect.top < descr->height)
1123         {
1124             rect.bottom = descr->height;
1125             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1126                            &rect, NULL, 0, NULL );
1127         }
1128         if (rect.right < descr->width)
1129         {
1130             rect.left   = rect.right;
1131             rect.right  = descr->width;
1132             rect.top    = 0;
1133             rect.bottom = descr->height;
1134             ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1135                            &rect, NULL, 0, NULL );
1136         }
1137     }
1138     if (oldFont) SelectObject( hdc, oldFont );
1139     if (oldBrush) SelectObject( hdc, oldBrush );
1140     return 0;
1141 }
1142
1143
1144 /***********************************************************************
1145  *           LISTBOX_InvalidateItems
1146  *
1147  * Invalidate all items from a given item. If the specified item is not
1148  * visible, nothing happens.
1149  */
1150 static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
1151 {
1152     RECT rect;
1153
1154     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1155     {
1156         if (descr->style & LBS_NOREDRAW)
1157         {
1158             descr->style |= LBS_DISPLAYCHANGED;
1159             return;
1160         }
1161         rect.bottom = descr->height;
1162         InvalidateRect( descr->self, &rect, TRUE );
1163         if (descr->style & LBS_MULTICOLUMN)
1164         {
1165             /* Repaint the other columns */
1166             rect.left  = rect.right;
1167             rect.right = descr->width;
1168             rect.top   = 0;
1169             InvalidateRect( descr->self, &rect, TRUE );
1170         }
1171     }
1172 }
1173
1174 static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1175 {
1176     RECT rect;
1177
1178     if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1179         InvalidateRect( descr->self, &rect, TRUE );
1180 }
1181
1182 /***********************************************************************
1183  *           LISTBOX_GetItemHeight
1184  */
1185 static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
1186 {
1187     if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
1188     {
1189         if ((index < 0) || (index >= descr->nb_items))
1190         {
1191             SetLastError(ERROR_INVALID_INDEX);
1192             return LB_ERR;
1193         }
1194         return descr->items[index].height;
1195     }
1196     else return descr->item_height;
1197 }
1198
1199
1200 /***********************************************************************
1201  *           LISTBOX_SetItemHeight
1202  */
1203 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
1204 {
1205     if (height > MAXBYTE)
1206         return -1;
1207
1208     if (!height) height = 1;
1209
1210     if (descr->style & LBS_OWNERDRAWVARIABLE)
1211     {
1212         if ((index < 0) || (index >= descr->nb_items))
1213         {
1214             SetLastError(ERROR_INVALID_INDEX);
1215             return LB_ERR;
1216         }
1217         TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
1218         descr->items[index].height = height;
1219         LISTBOX_UpdateScroll( descr );
1220         if (repaint)
1221             LISTBOX_InvalidateItems( descr, index );
1222     }
1223     else if (height != descr->item_height)
1224     {
1225         TRACE("[%p]: new height = %d\n", descr->self, height );
1226         descr->item_height = height;
1227         LISTBOX_UpdatePage( descr );
1228         LISTBOX_UpdateScroll( descr );
1229         if (repaint)
1230             InvalidateRect( descr->self, 0, TRUE );
1231     }
1232     return LB_OKAY;
1233 }
1234
1235
1236 /***********************************************************************
1237  *           LISTBOX_SetHorizontalPos
1238  */
1239 static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
1240 {
1241     INT diff;
1242
1243     if (pos > descr->horz_extent - descr->width)
1244         pos = descr->horz_extent - descr->width;
1245     if (pos < 0) pos = 0;
1246     if (!(diff = descr->horz_pos - pos)) return;
1247     TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
1248     descr->horz_pos = pos;
1249     LISTBOX_UpdateScroll( descr );
1250     if (abs(diff) < descr->width)
1251     {
1252         RECT rect;
1253         /* Invalidate the focused item so it will be repainted correctly */
1254         if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1255             InvalidateRect( descr->self, &rect, TRUE );
1256         ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1257                           SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1258     }
1259     else
1260         InvalidateRect( descr->self, NULL, TRUE );
1261 }
1262
1263
1264 /***********************************************************************
1265  *           LISTBOX_SetHorizontalExtent
1266  */
1267 static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
1268 {
1269     if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1270         return LB_OKAY;
1271     if (extent <= 0) extent = 1;
1272     if (extent == descr->horz_extent) return LB_OKAY;
1273     TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
1274     descr->horz_extent = extent;
1275     if (descr->horz_pos > extent - descr->width)
1276         LISTBOX_SetHorizontalPos( descr, extent - descr->width );
1277     else
1278         LISTBOX_UpdateScroll( descr );
1279     return LB_OKAY;
1280 }
1281
1282
1283 /***********************************************************************
1284  *           LISTBOX_SetColumnWidth
1285  */
1286 static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
1287 {
1288     if (width == descr->column_width) return LB_OKAY;
1289     TRACE("[%p]: new column width = %d\n", descr->self, width );
1290     descr->column_width = width;
1291     LISTBOX_UpdatePage( descr );
1292     return LB_OKAY;
1293 }
1294
1295
1296 /***********************************************************************
1297  *           LISTBOX_SetFont
1298  *
1299  * Returns the item height.
1300  */
1301 static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
1302 {
1303     HDC hdc;
1304     HFONT oldFont = 0;
1305     const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1306     SIZE sz;
1307
1308     descr->font = font;
1309
1310     if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
1311     {
1312         ERR("unable to get DC.\n" );
1313         return 16;
1314     }
1315     if (font) oldFont = SelectObject( hdc, font );
1316     GetTextExtentPointA( hdc, alphabet, 52, &sz);
1317     if (oldFont) SelectObject( hdc, oldFont );
1318     ReleaseDC( descr->self, hdc );
1319
1320     descr->avg_char_width = (sz.cx / 26 + 1) / 2;
1321     if (!IS_OWNERDRAW(descr))
1322         LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
1323     return sz.cy;
1324 }
1325
1326
1327 /***********************************************************************
1328  *           LISTBOX_MakeItemVisible
1329  *
1330  * Make sure that a given item is partially or fully visible.
1331  */
1332 static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
1333 {
1334     INT top;
1335
1336     TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);
1337
1338     if (index <= descr->top_item) top = index;
1339     else if (descr->style & LBS_MULTICOLUMN)
1340     {
1341         INT cols = descr->width;
1342         if (!fully) cols += descr->column_width - 1;
1343         if (cols >= descr->column_width) cols /= descr->column_width;
1344         else cols = 1;
1345         if (index < descr->top_item + (descr->page_size * cols)) return;
1346         top = index - descr->page_size * (cols - 1);
1347     }
1348     else if (descr->style & LBS_OWNERDRAWVARIABLE)
1349     {
1350         INT height = fully ? descr->items[index].height : 1;
1351         for (top = index; top > descr->top_item; top--)
1352             if ((height += descr->items[top-1].height) > descr->height) break;
1353     }
1354     else
1355     {
1356         if (index < descr->top_item + descr->page_size) return;
1357         if (!fully && (index == descr->top_item + descr->page_size) &&
1358             (descr->height > (descr->page_size * descr->item_height))) return;
1359         top = index - descr->page_size + 1;
1360     }
1361     LISTBOX_SetTopItem( descr, top, TRUE );
1362 }
1363
1364 /***********************************************************************
1365  *           LISTBOX_SetCaretIndex
1366  *
1367  * NOTES
1368  *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1369  *
1370  */
1371 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1372 {
1373     INT oldfocus = descr->focus_item;
1374
1375     TRACE("old focus %d, index %d\n", oldfocus, index);
1376
1377     if (descr->style & LBS_NOSEL) return LB_ERR;
1378     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1379     if (index == oldfocus) return LB_OKAY;
1380
1381     LISTBOX_DrawFocusRect( descr, FALSE );
1382     descr->focus_item = index;
1383
1384     LISTBOX_MakeItemVisible( descr, index, fully_visible );
1385     LISTBOX_DrawFocusRect( descr, TRUE );
1386
1387     return LB_OKAY;
1388 }
1389
1390
1391 /***********************************************************************
1392  *           LISTBOX_SelectItemRange
1393  *
1394  * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1395  */
1396 static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1397                                         INT last, BOOL on )
1398 {
1399     INT i;
1400
1401     /* A few sanity checks */
1402
1403     if (descr->style & LBS_NOSEL) return LB_ERR;
1404     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1405
1406     if (!descr->nb_items) return LB_OKAY;
1407
1408     if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1409     if (first < 0) first = 0;
1410     if (last < first) return LB_OKAY;
1411
1412     if (on)  /* Turn selection on */
1413     {
1414         for (i = first; i <= last; i++)
1415         {
1416             if (descr->items[i].selected) continue;
1417             descr->items[i].selected = TRUE;
1418             LISTBOX_InvalidateItemRect(descr, i);
1419         }
1420     }
1421     else  /* Turn selection off */
1422     {
1423         for (i = first; i <= last; i++)
1424         {
1425             if (!descr->items[i].selected) continue;
1426             descr->items[i].selected = FALSE;
1427             LISTBOX_InvalidateItemRect(descr, i);
1428         }
1429     }
1430     return LB_OKAY;
1431 }
1432
1433 /***********************************************************************
1434  *           LISTBOX_SetSelection
1435  */
1436 static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1437                                      BOOL on, BOOL send_notify )
1438 {
1439     TRACE( "cur_sel=%d index=%d notify=%s\n",
1440            descr->selected_item, index, send_notify ? "YES" : "NO" );
1441
1442     if (descr->style & LBS_NOSEL)
1443     {
1444         descr->selected_item = index;
1445         return LB_ERR;
1446     }
1447     if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1448     if (descr->style & LBS_MULTIPLESEL)
1449     {
1450         if (index == -1)  /* Select all items */
1451             return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
1452         else  /* Only one item */
1453             return LISTBOX_SelectItemRange( descr, index, index, on );
1454     }
1455     else
1456     {
1457         INT oldsel = descr->selected_item;
1458         if (index == oldsel) return LB_OKAY;
1459         if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1460         if (index != -1) descr->items[index].selected = TRUE;
1461         if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1462         descr->selected_item = index;
1463         if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
1464         if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
1465                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1466         else
1467             if( descr->lphc ) /* set selection change flag for parent combo */
1468                 descr->lphc->wState |= CBF_SELCHANGE;
1469     }
1470     return LB_OKAY;
1471 }
1472
1473
1474 /***********************************************************************
1475  *           LISTBOX_MoveCaret
1476  *
1477  * Change the caret position and extend the selection to the new caret.
1478  */
1479 static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
1480 {
1481     TRACE("old focus %d, index %d\n", descr->focus_item, index);
1482
1483     if ((index <  0) || (index >= descr->nb_items))
1484         return;
1485
1486     /* Important, repaint needs to be done in this order if
1487        you want to mimic Windows behavior:
1488        1. Remove the focus and paint the item
1489        2. Remove the selection and paint the item(s)
1490        3. Set the selection and repaint the item(s)
1491        4. Set the focus to 'index' and repaint the item */
1492
1493     /* 1. remove the focus and repaint the item */
1494     LISTBOX_DrawFocusRect( descr, FALSE );
1495
1496     /* 2. then turn off the previous selection */
1497     /* 3. repaint the new selected item */
1498     if (descr->style & LBS_EXTENDEDSEL)
1499     {
1500         if (descr->anchor_item != -1)
1501         {
1502             INT first = min( index, descr->anchor_item );
1503             INT last  = max( index, descr->anchor_item );
1504             if (first > 0)
1505                 LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
1506             LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
1507             LISTBOX_SelectItemRange( descr, first, last, TRUE );
1508         }
1509     }
1510     else if (!(descr->style & LBS_MULTIPLESEL))
1511     {
1512         /* Set selection to new caret item */
1513         LISTBOX_SetSelection( descr, index, TRUE, FALSE );
1514     }
1515
1516     /* 4. repaint the new item with the focus */
1517     descr->focus_item = index;
1518     LISTBOX_MakeItemVisible( descr, index, fully_visible );
1519     LISTBOX_DrawFocusRect( descr, TRUE );
1520 }
1521
1522
1523 /***********************************************************************
1524  *           LISTBOX_InsertItem
1525  */
1526 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1527                                    LPWSTR str, ULONG_PTR data )
1528 {
1529     LB_ITEMDATA *item;
1530     INT max_items;
1531     INT oldfocus = descr->focus_item;
1532
1533     if (index == -1) index = descr->nb_items;
1534     else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1535     if (!descr->items) max_items = 0;
1536     else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1537     if (descr->nb_items == max_items)
1538     {
1539         /* We need to grow the array */
1540         max_items += LB_ARRAY_GRANULARITY;
1541         if (descr->items)
1542             item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1543                                   max_items * sizeof(LB_ITEMDATA) );
1544         else
1545             item = HeapAlloc( GetProcessHeap(), 0,
1546                                   max_items * sizeof(LB_ITEMDATA) );
1547         if (!item)
1548         {
1549             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1550             return LB_ERRSPACE;
1551         }
1552         descr->items = item;
1553     }
1554
1555     /* Insert the item structure */
1556
1557     item = &descr->items[index];
1558     if (index < descr->nb_items)
1559         RtlMoveMemory( item + 1, item,
1560                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1561     item->str      = str;
1562     item->data     = data;
1563     item->height   = 0;
1564     item->selected = FALSE;
1565     descr->nb_items++;
1566
1567     /* Get item height */
1568
1569     if (descr->style & LBS_OWNERDRAWVARIABLE)
1570     {
1571         MEASUREITEMSTRUCT mis;
1572         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1573
1574         mis.CtlType    = ODT_LISTBOX;
1575         mis.CtlID      = id;
1576         mis.itemID     = index;
1577         mis.itemData   = descr->items[index].data;
1578         mis.itemHeight = descr->item_height;
1579         SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1580         item->height = mis.itemHeight ? mis.itemHeight : 1;
1581         TRACE("[%p]: measure item %d (%s) = %d\n",
1582               descr->self, index, str ? debugstr_w(str) : "", item->height );
1583     }
1584
1585     /* Repaint the items */
1586
1587     LISTBOX_UpdateScroll( descr );
1588     LISTBOX_InvalidateItems( descr, index );
1589
1590     /* Move selection and focused item */
1591     /* If listbox was empty, set focus to the first item */
1592     if (descr->nb_items == 1)
1593          LISTBOX_SetCaretIndex( descr, 0, FALSE );
1594     /* single select don't change selection index in win31 */
1595     else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1596     {
1597         descr->selected_item++;
1598         LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1599     }
1600     else
1601     {
1602         if (index <= descr->selected_item)
1603         {
1604             descr->selected_item++;
1605             descr->focus_item = oldfocus; /* focus not changed */
1606         }
1607     }
1608     return LB_OKAY;
1609 }
1610
1611
1612 /***********************************************************************
1613  *           LISTBOX_InsertString
1614  */
1615 static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
1616 {
1617     LPWSTR new_str = NULL;
1618     ULONG_PTR data = 0;
1619     LRESULT ret;
1620
1621     if (HAS_STRINGS(descr))
1622     {
1623         static const WCHAR empty_stringW[] = { 0 };
1624         if (!str) str = empty_stringW;
1625         if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1626         {
1627             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
1628             return LB_ERRSPACE;
1629         }
1630         strcpyW(new_str, str);
1631     }
1632     else data = (ULONG_PTR)str;
1633
1634     if (index == -1) index = descr->nb_items;
1635     if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
1636     {
1637         HeapFree( GetProcessHeap(), 0, new_str );
1638         return ret;
1639     }
1640
1641     TRACE("[%p]: added item %d %s\n",
1642           descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1643     return index;
1644 }
1645
1646
1647 /***********************************************************************
1648  *           LISTBOX_DeleteItem
1649  *
1650  * Delete the content of an item. 'index' must be a valid index.
1651  */
1652 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
1653 {
1654     /* save the item data before it gets freed by LB_RESETCONTENT */
1655     ULONG_PTR item_data = descr->items[index].data;
1656     LPWSTR item_str = descr->items[index].str;
1657
1658     if (!descr->nb_items)
1659         SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
1660
1661     /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1662      *       while Win95 sends it for all items with user data.
1663      *       It's probably better to send it too often than not
1664      *       often enough, so this is what we do here.
1665      */
1666     if (IS_OWNERDRAW(descr) || item_data)
1667     {
1668         DELETEITEMSTRUCT dis;
1669         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
1670
1671         dis.CtlType  = ODT_LISTBOX;
1672         dis.CtlID    = id;
1673         dis.itemID   = index;
1674         dis.hwndItem = descr->self;
1675         dis.itemData = item_data;
1676         SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1677     }
1678     if (HAS_STRINGS(descr))
1679         HeapFree( GetProcessHeap(), 0, item_str );
1680 }
1681
1682
1683 /***********************************************************************
1684  *           LISTBOX_RemoveItem
1685  *
1686  * Remove an item from the listbox and delete its content.
1687  */
1688 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
1689 {
1690     LB_ITEMDATA *item;
1691     INT max_items;
1692
1693     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1694
1695     /* We need to invalidate the original rect instead of the updated one. */
1696     LISTBOX_InvalidateItems( descr, index );
1697
1698     descr->nb_items--;
1699     LISTBOX_DeleteItem( descr, index );
1700
1701     if (!descr->nb_items) return LB_OKAY;
1702
1703     /* Remove the item */
1704
1705     item = &descr->items[index];
1706     if (index < descr->nb_items)
1707         RtlMoveMemory( item, item + 1,
1708                        (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1709     if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1710
1711     /* Shrink the item array if possible */
1712
1713     max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1714     if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1715     {
1716         max_items -= LB_ARRAY_GRANULARITY;
1717         item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1718                             max_items * sizeof(LB_ITEMDATA) );
1719         if (item) descr->items = item;
1720     }
1721     /* Repaint the items */
1722
1723     LISTBOX_UpdateScroll( descr );
1724     /* if we removed the scrollbar, reset the top of the list
1725       (correct for owner-drawn ???) */
1726     if (descr->nb_items == descr->page_size)
1727         LISTBOX_SetTopItem( descr, 0, TRUE );
1728
1729     /* Move selection and focused item */
1730     if (!IS_MULTISELECT(descr))
1731     {
1732         if (index == descr->selected_item)
1733             descr->selected_item = -1;
1734         else if (index < descr->selected_item)
1735         {
1736             descr->selected_item--;
1737             if (ISWIN31) /* win 31 do not change the selected item number */
1738                LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
1739         }
1740     }
1741
1742     if (descr->focus_item >= descr->nb_items)
1743     {
1744           descr->focus_item = descr->nb_items - 1;
1745           if (descr->focus_item < 0) descr->focus_item = 0;
1746     }
1747     return LB_OKAY;
1748 }
1749
1750
1751 /***********************************************************************
1752  *           LISTBOX_ResetContent
1753  */
1754 static void LISTBOX_ResetContent( LB_DESCR *descr )
1755 {
1756     INT i;
1757
1758     for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1759     HeapFree( GetProcessHeap(), 0, descr->items );
1760     descr->nb_items      = 0;
1761     descr->top_item      = 0;
1762     descr->selected_item = -1;
1763     descr->focus_item    = 0;
1764     descr->anchor_item   = -1;
1765     descr->items         = NULL;
1766 }
1767
1768
1769 /***********************************************************************
1770  *           LISTBOX_SetCount
1771  */
1772 static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
1773 {
1774     LRESULT ret;
1775
1776     if (HAS_STRINGS(descr))
1777     {
1778         SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
1779         return LB_ERR;
1780     }
1781
1782     /* FIXME: this is far from optimal... */
1783     if (count > descr->nb_items)
1784     {
1785         while (count > descr->nb_items)
1786             if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
1787                 return ret;
1788     }
1789     else if (count < descr->nb_items)
1790     {
1791         while (count < descr->nb_items)
1792             if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
1793                 return ret;
1794     }
1795     return LB_OKAY;
1796 }
1797
1798
1799 /***********************************************************************
1800  *           LISTBOX_Directory
1801  */
1802 static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1803                                   LPCWSTR filespec, BOOL long_names )
1804 {
1805     HANDLE handle;
1806     LRESULT ret = LB_OKAY;
1807     WIN32_FIND_DATAW entry;
1808     int pos;
1809     LRESULT maxinsert = LB_ERR;
1810
1811     /* don't scan directory if we just want drives exclusively */
1812     if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1813         /* scan directory */
1814         if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1815         {
1816              int le = GetLastError();
1817             if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1818         }
1819         else
1820         {
1821             do
1822             {
1823                 WCHAR buffer[270];
1824                 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1825                 {
1826                     static const WCHAR bracketW[]  = { ']',0 };
1827                     static const WCHAR dotW[] = { '.',0 };
1828                     if (!(attrib & DDL_DIRECTORY) ||
1829                         !strcmpW( entry.cFileName, dotW )) continue;
1830                     buffer[0] = '[';
1831                     if (!long_names && entry.cAlternateFileName[0])
1832                         strcpyW( buffer + 1, entry.cAlternateFileName );
1833                     else
1834                         strcpyW( buffer + 1, entry.cFileName );
1835                     strcatW(buffer, bracketW);
1836                 }
1837                 else  /* not a directory */
1838                 {
1839 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1840                  FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1841
1842                     if ((attrib & DDL_EXCLUSIVE) &&
1843                         ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1844                         continue;
1845 #undef ATTRIBS
1846                     if (!long_names && entry.cAlternateFileName[0])
1847                         strcpyW( buffer, entry.cAlternateFileName );
1848                     else
1849                         strcpyW( buffer, entry.cFileName );
1850                 }
1851                 if (!long_names) CharLowerW( buffer );
1852                 pos = LISTBOX_FindFileStrPos( descr, buffer );
1853                 if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1854                     break;
1855                 if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1856             } while (FindNextFileW( handle, &entry ));
1857             FindClose( handle );
1858         }
1859     }
1860     if (ret >= 0)
1861     {
1862         ret = maxinsert;
1863
1864         /* scan drives */
1865         if (attrib & DDL_DRIVES)
1866         {
1867             WCHAR buffer[] = {'[','-','a','-',']',0};
1868             WCHAR root[] = {'A',':','\\',0};
1869             int drive;
1870             for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1871             {
1872                 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1873                 if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
1874                     break;
1875             }
1876         }
1877     }
1878     return ret;
1879 }
1880
1881
1882 /***********************************************************************
1883  *           LISTBOX_HandleVScroll
1884  */
1885 static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1886 {
1887     SCROLLINFO info;
1888
1889     if (descr->style & LBS_MULTICOLUMN) return 0;
1890     switch(scrollReq)
1891     {
1892     case SB_LINEUP:
1893         LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
1894         break;
1895     case SB_LINEDOWN:
1896         LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
1897         break;
1898     case SB_PAGEUP:
1899         LISTBOX_SetTopItem( descr, descr->top_item -
1900                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
1901         break;
1902     case SB_PAGEDOWN:
1903         LISTBOX_SetTopItem( descr, descr->top_item +
1904                             LISTBOX_GetCurrentPageSize( descr ), TRUE );
1905         break;
1906     case SB_THUMBPOSITION:
1907         LISTBOX_SetTopItem( descr, pos, TRUE );
1908         break;
1909     case SB_THUMBTRACK:
1910         info.cbSize = sizeof(info);
1911         info.fMask = SIF_TRACKPOS;
1912         GetScrollInfo( descr->self, SB_VERT, &info );
1913         LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
1914         break;
1915     case SB_TOP:
1916         LISTBOX_SetTopItem( descr, 0, TRUE );
1917         break;
1918     case SB_BOTTOM:
1919         LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1920         break;
1921     }
1922     return 0;
1923 }
1924
1925
1926 /***********************************************************************
1927  *           LISTBOX_HandleHScroll
1928  */
1929 static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
1930 {
1931     SCROLLINFO info;
1932     INT page;
1933
1934     if (descr->style & LBS_MULTICOLUMN)
1935     {
1936         switch(scrollReq)
1937         {
1938         case SB_LINELEFT:
1939             LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
1940                                 TRUE );
1941             break;
1942         case SB_LINERIGHT:
1943             LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
1944                                 TRUE );
1945             break;
1946         case SB_PAGELEFT:
1947             page = descr->width / descr->column_width;
1948             if (page < 1) page = 1;
1949             LISTBOX_SetTopItem( descr,
1950                              descr->top_item - page * descr->page_size, TRUE );
1951             break;
1952         case SB_PAGERIGHT:
1953             page = descr->width / descr->column_width;
1954             if (page < 1) page = 1;
1955             LISTBOX_SetTopItem( descr,
1956                              descr->top_item + page * descr->page_size, TRUE );
1957             break;
1958         case SB_THUMBPOSITION:
1959             LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
1960             break;
1961         case SB_THUMBTRACK:
1962             info.cbSize = sizeof(info);
1963             info.fMask  = SIF_TRACKPOS;
1964             GetScrollInfo( descr->self, SB_VERT, &info );
1965             LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
1966                                 TRUE );
1967             break;
1968         case SB_LEFT:
1969             LISTBOX_SetTopItem( descr, 0, TRUE );
1970             break;
1971         case SB_RIGHT:
1972             LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
1973             break;
1974         }
1975     }
1976     else if (descr->horz_extent)
1977     {
1978         switch(scrollReq)
1979         {
1980         case SB_LINELEFT:
1981             LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
1982             break;
1983         case SB_LINERIGHT:
1984             LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
1985             break;
1986         case SB_PAGELEFT:
1987             LISTBOX_SetHorizontalPos( descr,
1988                                       descr->horz_pos - descr->width );
1989             break;
1990         case SB_PAGERIGHT:
1991             LISTBOX_SetHorizontalPos( descr,
1992                                       descr->horz_pos + descr->width );
1993             break;
1994         case SB_THUMBPOSITION:
1995             LISTBOX_SetHorizontalPos( descr, pos );
1996             break;
1997         case SB_THUMBTRACK:
1998             info.cbSize = sizeof(info);
1999             info.fMask = SIF_TRACKPOS;
2000             GetScrollInfo( descr->self, SB_HORZ, &info );
2001             LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
2002             break;
2003         case SB_LEFT:
2004             LISTBOX_SetHorizontalPos( descr, 0 );
2005             break;
2006         case SB_RIGHT:
2007             LISTBOX_SetHorizontalPos( descr,
2008                                       descr->horz_extent - descr->width );
2009             break;
2010         }
2011     }
2012     return 0;
2013 }
2014
2015 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2016 {
2017     short gcWheelDelta = 0;
2018     UINT pulScrollLines = 3;
2019
2020     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
2021
2022     gcWheelDelta -= delta;
2023
2024     if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
2025     {
2026         int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
2027         cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
2028         LISTBOX_SetTopItem( descr, descr->top_item + cLineScroll, TRUE );
2029     }
2030     return 0;
2031 }
2032
2033 /***********************************************************************
2034  *           LISTBOX_HandleLButtonDown
2035  */
2036 static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
2037 {
2038     INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2039
2040     TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
2041           descr->self, x, y, index, descr->focus_item);
2042
2043     if (!descr->caret_on && (descr->in_focus)) return 0;
2044
2045     if (!descr->in_focus)
2046     {
2047         if( !descr->lphc ) SetFocus( descr->self );
2048         else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2049     }
2050
2051     if (index == -1) return 0;
2052
2053     if (!descr->lphc)
2054     {
2055         if (descr->style & LBS_NOTIFY )
2056             SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2057                             MAKELPARAM( x, y ) );
2058     }
2059
2060     descr->captured = TRUE;
2061     SetCapture( descr->self );
2062
2063     if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
2064     {
2065         /* we should perhaps make sure that all items are deselected
2066            FIXME: needed for !LBS_EXTENDEDSEL, too ?
2067            if (!(keys & (MK_SHIFT|MK_CONTROL)))
2068            LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2069         */
2070
2071         if (!(keys & MK_SHIFT)) descr->anchor_item = index;
2072         if (keys & MK_CONTROL)
2073         {
2074             LISTBOX_SetCaretIndex( descr, index, FALSE );
2075             LISTBOX_SetSelection( descr, index,
2076                                   !descr->items[index].selected,
2077                                   (descr->style & LBS_NOTIFY) != 0);
2078         }
2079         else
2080         {
2081             LISTBOX_MoveCaret( descr, index, FALSE );
2082
2083             if (descr->style & LBS_EXTENDEDSEL)
2084             {
2085                 LISTBOX_SetSelection( descr, index,
2086                                descr->items[index].selected,
2087                               (descr->style & LBS_NOTIFY) != 0 );
2088             }
2089             else
2090             {
2091                 LISTBOX_SetSelection( descr, index,
2092                                !descr->items[index].selected,
2093                               (descr->style & LBS_NOTIFY) != 0 );
2094             }
2095         }
2096     }
2097     else
2098     {
2099         descr->anchor_item = index;
2100         LISTBOX_MoveCaret( descr, index, FALSE );
2101         LISTBOX_SetSelection( descr, index,
2102                               TRUE, (descr->style & LBS_NOTIFY) != 0 );
2103     }
2104
2105     if (!descr->lphc)
2106     {
2107         if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2108         {
2109             POINT pt;
2110
2111             pt.x = x;
2112             pt.y = y;
2113
2114             if (DragDetect( descr->self, pt ))
2115                 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2116         }
2117     }
2118     return 0;
2119 }
2120
2121
2122 /*************************************************************************
2123  * LISTBOX_HandleLButtonDownCombo [Internal]
2124  *
2125  * Process LButtonDown message for the ComboListBox
2126  *
2127  * PARAMS
2128  *     pWnd       [I] The windows internal structure
2129  *     pDescr     [I] The ListBox internal structure
2130  *     keys       [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2131  *     x          [I] X Mouse Coordinate
2132  *     y          [I] Y Mouse Coordinate
2133  *
2134  * RETURNS
2135  *     0 since we are processing the WM_LBUTTONDOWN Message
2136  *
2137  * NOTES
2138  *  This function is only to be used when a ListBox is a ComboListBox
2139  */
2140
2141 static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2142 {
2143     RECT clientRect, screenRect;
2144     POINT mousePos;
2145
2146     mousePos.x = x;
2147     mousePos.y = y;
2148
2149     GetClientRect(descr->self, &clientRect);
2150
2151     if(PtInRect(&clientRect, mousePos))
2152     {
2153        /* MousePos is in client, resume normal processing */
2154         if (msg == WM_LBUTTONDOWN)
2155         {
2156            descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
2157            return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2158         }
2159         else if (descr->style & LBS_NOTIFY)
2160             SEND_NOTIFICATION( descr, LBN_DBLCLK );
2161     }
2162     else
2163     {
2164         POINT screenMousePos;
2165         HWND hWndOldCapture;
2166
2167         /* Check the Non-Client Area */
2168         screenMousePos = mousePos;
2169         hWndOldCapture = GetCapture();
2170         ReleaseCapture();
2171         GetWindowRect(descr->self, &screenRect);
2172         ClientToScreen(descr->self, &screenMousePos);
2173
2174         if(!PtInRect(&screenRect, screenMousePos))
2175         {
2176             LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
2177             LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
2178             COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2179         }
2180         else
2181         {
2182             /* Check to see the NC is a scrollbar */
2183             INT nHitTestType=0;
2184             LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2185             /* Check Vertical scroll bar */
2186             if (style & WS_VSCROLL)
2187             {
2188                 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2189                 if (PtInRect( &clientRect, mousePos ))
2190                     nHitTestType = HTVSCROLL;
2191             }
2192               /* Check horizontal scroll bar */
2193             if (style & WS_HSCROLL)
2194             {
2195                 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2196                 if (PtInRect( &clientRect, mousePos ))
2197                     nHitTestType = HTHSCROLL;
2198             }
2199             /* Windows sends this message when a scrollbar is clicked
2200              */
2201
2202             if(nHitTestType != 0)
2203             {
2204                 SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
2205                              MAKELONG(screenMousePos.x, screenMousePos.y));
2206             }
2207             /* Resume the Capture after scrolling is complete
2208              */
2209             if(hWndOldCapture != 0)
2210                 SetCapture(hWndOldCapture);
2211         }
2212     }
2213     return 0;
2214 }
2215
2216 /***********************************************************************
2217  *           LISTBOX_HandleLButtonUp
2218  */
2219 static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
2220 {
2221     if (LISTBOX_Timer != LB_TIMER_NONE)
2222         KillSystemTimer( descr->self, LB_TIMER_ID );
2223     LISTBOX_Timer = LB_TIMER_NONE;
2224     if (descr->captured)
2225     {
2226         descr->captured = FALSE;
2227         if (GetCapture() == descr->self) ReleaseCapture();
2228         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2229             SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2230     }
2231     return 0;
2232 }
2233
2234
2235 /***********************************************************************
2236  *           LISTBOX_HandleTimer
2237  *
2238  * Handle scrolling upon a timer event.
2239  * Return TRUE if scrolling should continue.
2240  */
2241 static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
2242 {
2243     switch(dir)
2244     {
2245     case LB_TIMER_UP:
2246         if (descr->top_item) index = descr->top_item - 1;
2247         else index = 0;
2248         break;
2249     case LB_TIMER_LEFT:
2250         if (descr->top_item) index -= descr->page_size;
2251         break;
2252     case LB_TIMER_DOWN:
2253         index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2254         if (index == descr->focus_item) index++;
2255         if (index >= descr->nb_items) index = descr->nb_items - 1;
2256         break;
2257     case LB_TIMER_RIGHT:
2258         if (index + descr->page_size < descr->nb_items)
2259             index += descr->page_size;
2260         break;
2261     case LB_TIMER_NONE:
2262         break;
2263     }
2264     if (index == descr->focus_item) return FALSE;
2265     LISTBOX_MoveCaret( descr, index, FALSE );
2266     return TRUE;
2267 }
2268
2269
2270 /***********************************************************************
2271  *           LISTBOX_HandleSystemTimer
2272  *
2273  * WM_SYSTIMER handler.
2274  */
2275 static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
2276 {
2277     if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
2278     {
2279         KillSystemTimer( descr->self, LB_TIMER_ID );
2280         LISTBOX_Timer = LB_TIMER_NONE;
2281     }
2282     return 0;
2283 }
2284
2285
2286 /***********************************************************************
2287  *           LISTBOX_HandleMouseMove
2288  *
2289  * WM_MOUSEMOVE handler.
2290  */
2291 static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2292                                      INT x, INT y )
2293 {
2294     INT index;
2295     TIMER_DIRECTION dir = LB_TIMER_NONE;
2296
2297     if (!descr->captured) return;
2298
2299     if (descr->style & LBS_MULTICOLUMN)
2300     {
2301         if (y < 0) y = 0;
2302         else if (y >= descr->item_height * descr->page_size)
2303             y = descr->item_height * descr->page_size - 1;
2304
2305         if (x < 0)
2306         {
2307             dir = LB_TIMER_LEFT;
2308             x = 0;
2309         }
2310         else if (x >= descr->width)
2311         {
2312             dir = LB_TIMER_RIGHT;
2313             x = descr->width - 1;
2314         }
2315     }
2316     else
2317     {
2318         if (y < 0) dir = LB_TIMER_UP;  /* above */
2319         else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
2320     }
2321
2322     index = LISTBOX_GetItemFromPoint( descr, x, y );
2323     if (index == -1) index = descr->focus_item;
2324     if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
2325
2326     /* Start/stop the system timer */
2327
2328     if (dir != LB_TIMER_NONE)
2329         SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2330     else if (LISTBOX_Timer != LB_TIMER_NONE)
2331         KillSystemTimer( descr->self, LB_TIMER_ID );
2332     LISTBOX_Timer = dir;
2333 }
2334
2335
2336 /***********************************************************************
2337  *           LISTBOX_HandleKeyDown
2338  */
2339 static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
2340 {
2341     INT caret = -1;
2342     BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2343     if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2344         bForceSelection = FALSE; /* only for single select list */
2345
2346     if (descr->style & LBS_WANTKEYBOARDINPUT)
2347     {
2348         caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2349                                 MAKEWPARAM(LOWORD(key), descr->focus_item),
2350                                 (LPARAM)descr->self );
2351         if (caret == -2) return 0;
2352     }
2353     if (caret == -1) switch(key)
2354     {
2355     case VK_LEFT:
2356         if (descr->style & LBS_MULTICOLUMN)
2357         {
2358             bForceSelection = FALSE;
2359             if (descr->focus_item >= descr->page_size)
2360                 caret = descr->focus_item - descr->page_size;
2361             break;
2362         }
2363         /* fall through */
2364     case VK_UP:
2365         caret = descr->focus_item - 1;
2366         if (caret < 0) caret = 0;
2367         break;
2368     case VK_RIGHT:
2369         if (descr->style & LBS_MULTICOLUMN)
2370         {
2371             bForceSelection = FALSE;
2372             if (descr->focus_item + descr->page_size < descr->nb_items)
2373                 caret = descr->focus_item + descr->page_size;
2374             break;
2375         }
2376         /* fall through */
2377     case VK_DOWN:
2378         caret = descr->focus_item + 1;
2379         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2380         break;
2381
2382     case VK_PRIOR:
2383         if (descr->style & LBS_MULTICOLUMN)
2384         {
2385             INT page = descr->width / descr->column_width;
2386             if (page < 1) page = 1;
2387             caret = descr->focus_item - (page * descr->page_size) + 1;
2388         }
2389         else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2390         if (caret < 0) caret = 0;
2391         break;
2392     case VK_NEXT:
2393         if (descr->style & LBS_MULTICOLUMN)
2394         {
2395             INT page = descr->width / descr->column_width;
2396             if (page < 1) page = 1;
2397             caret = descr->focus_item + (page * descr->page_size) - 1;
2398         }
2399         else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2400         if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2401         break;
2402     case VK_HOME:
2403         caret = 0;
2404         break;
2405     case VK_END:
2406         caret = descr->nb_items - 1;
2407         break;
2408     case VK_SPACE:
2409         if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2410         else if (descr->style & LBS_MULTIPLESEL)
2411         {
2412             LISTBOX_SetSelection( descr, descr->focus_item,
2413                                   !descr->items[descr->focus_item].selected,
2414                                   (descr->style & LBS_NOTIFY) != 0 );
2415         }
2416         break;
2417     default:
2418         bForceSelection = FALSE;
2419     }
2420     if (bForceSelection) /* focused item is used instead of key */
2421         caret = descr->focus_item;
2422     if (caret >= 0)
2423     {
2424         if (((descr->style & LBS_EXTENDEDSEL) &&
2425             !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
2426             !IS_MULTISELECT(descr))
2427             descr->anchor_item = caret;
2428         LISTBOX_MoveCaret( descr, caret, TRUE );
2429
2430         if (descr->style & LBS_MULTIPLESEL)
2431             descr->selected_item = caret;
2432         else
2433             LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2434         if (descr->style & LBS_NOTIFY)
2435         {
2436             if (descr->lphc && IsWindowVisible( descr->self ))
2437             {
2438                 /* make sure that combo parent doesn't hide us */
2439                 descr->lphc->wState |= CBF_NOROLLUP;
2440             }
2441             if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2442         }
2443     }
2444     return 0;
2445 }
2446
2447
2448 /***********************************************************************
2449  *           LISTBOX_HandleChar
2450  */
2451 static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
2452 {
2453     INT caret = -1;
2454     WCHAR str[2];
2455
2456     str[0] = charW;
2457     str[1] = '\0';
2458
2459     if (descr->style & LBS_WANTKEYBOARDINPUT)
2460     {
2461         caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2462                                 MAKEWPARAM(charW, descr->focus_item),
2463                                 (LPARAM)descr->self );
2464         if (caret == -2) return 0;
2465     }
2466     if (caret == -1)
2467         caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
2468     if (caret != -1)
2469     {
2470         if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2471            LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
2472         LISTBOX_MoveCaret( descr, caret, TRUE );
2473         if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2474             SEND_NOTIFICATION( descr, LBN_SELCHANGE );
2475     }
2476     return 0;
2477 }
2478
2479
2480 /***********************************************************************
2481  *           LISTBOX_Create
2482  */
2483 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2484 {
2485     LB_DESCR *descr;
2486     MEASUREITEMSTRUCT mis;
2487     RECT rect;
2488
2489     if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2490         return FALSE;
2491
2492     GetClientRect( hwnd, &rect );
2493     descr->self          = hwnd;
2494     descr->owner         = GetParent( descr->self );
2495     descr->style         = GetWindowLongW( descr->self, GWL_STYLE );
2496     descr->width         = rect.right - rect.left;
2497     descr->height        = rect.bottom - rect.top;
2498     descr->items         = NULL;
2499     descr->nb_items      = 0;
2500     descr->top_item      = 0;
2501     descr->selected_item = -1;
2502     descr->focus_item    = 0;
2503     descr->anchor_item   = -1;
2504     descr->item_height   = 1;
2505     descr->page_size     = 1;
2506     descr->column_width  = 150;
2507     descr->horz_extent   = (descr->style & WS_HSCROLL) ? 1 : 0;
2508     descr->horz_pos      = 0;
2509     descr->nb_tabs       = 0;
2510     descr->tabs          = NULL;
2511     descr->caret_on      = lphc ? FALSE : TRUE;
2512     if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2513     descr->in_focus      = FALSE;
2514     descr->captured      = FALSE;
2515     descr->font          = 0;
2516     descr->locale        = GetUserDefaultLCID();
2517     descr->lphc          = lphc;
2518
2519     if (is_old_app(descr) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2520     {
2521         /* Win95 document "List Box Differences" from MSDN:
2522            If a list box in a version 3.x application has either the
2523            WS_HSCROLL or WS_VSCROLL style, the list box receives both
2524            horizontal and vertical scroll bars.
2525         */
2526         descr->style |= WS_VSCROLL | WS_HSCROLL;
2527     }
2528
2529     if( lphc )
2530     {
2531         TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2532         descr->owner = lphc->self;
2533     }
2534
2535     SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
2536
2537 /*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2538  */
2539     if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2540     if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2541     if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2542     descr->item_height = LISTBOX_SetFont( descr, 0 );
2543
2544     if (descr->style & LBS_OWNERDRAWFIXED)
2545     {
2546         if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2547         {
2548             /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2549           descr->item_height = lphc->fixedOwnerDrawHeight;
2550         }
2551         else
2552         {
2553             UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
2554             mis.CtlType    = ODT_LISTBOX;
2555             mis.CtlID      = id;
2556             mis.itemID     = -1;
2557             mis.itemWidth  =  0;
2558             mis.itemData   =  0;
2559             mis.itemHeight = descr->item_height;
2560             SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2561             descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2562         }
2563     }
2564
2565     TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2566     return TRUE;
2567 }
2568
2569
2570 /***********************************************************************
2571  *           LISTBOX_Destroy
2572  */
2573 static BOOL LISTBOX_Destroy( LB_DESCR *descr )
2574 {
2575     LISTBOX_ResetContent( descr );
2576     SetWindowLongPtrW( descr->self, 0, 0 );
2577     HeapFree( GetProcessHeap(), 0, descr );
2578     return TRUE;
2579 }
2580
2581
2582 /***********************************************************************
2583  *           ListBoxWndProc_common
2584  */
2585 LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
2586 {
2587     LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2588     LPHEADCOMBO lphc = 0;
2589     LRESULT ret;
2590
2591     if (!descr)
2592     {
2593         if (!IsWindow(hwnd)) return 0;
2594
2595         if (msg == WM_CREATE)
2596         {
2597             CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2598             if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2599             if (!LISTBOX_Create( hwnd, lphc )) return -1;
2600             TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2601             return 0;
2602         }
2603         /* Ignore all other messages before we get a WM_CREATE */
2604         return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2605                          DefWindowProcA( hwnd, msg, wParam, lParam );
2606     }
2607     if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
2608
2609     TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2610           descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2611
2612     switch(msg)
2613     {
2614     case LB_RESETCONTENT:
2615         LISTBOX_ResetContent( descr );
2616         LISTBOX_UpdateScroll( descr );
2617         InvalidateRect( descr->self, NULL, TRUE );
2618         return 0;
2619
2620     case LB_ADDSTRING:
2621     {
2622         INT ret;
2623         LPWSTR textW;
2624         if(unicode || !HAS_STRINGS(descr))
2625             textW = (LPWSTR)lParam;
2626         else
2627         {
2628             LPSTR textA = (LPSTR)lParam;
2629             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2630             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2631                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2632             else
2633                 return LB_ERRSPACE;
2634         }
2635         wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
2636         ret = LISTBOX_InsertString( descr, wParam, textW );
2637         if (!unicode && HAS_STRINGS(descr))
2638             HeapFree(GetProcessHeap(), 0, textW);
2639         return ret;
2640     }
2641
2642     case LB_INSERTSTRING:
2643     {
2644         INT ret;
2645         LPWSTR textW;
2646         if(unicode || !HAS_STRINGS(descr))
2647             textW = (LPWSTR)lParam;
2648         else
2649         {
2650             LPSTR textA = (LPSTR)lParam;
2651             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2652             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2653                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2654             else
2655                 return LB_ERRSPACE;
2656         }
2657         ret = LISTBOX_InsertString( descr, wParam, textW );
2658         if(!unicode && HAS_STRINGS(descr))
2659             HeapFree(GetProcessHeap(), 0, textW);
2660         return ret;
2661     }
2662
2663     case LB_ADDFILE:
2664     {
2665         INT ret;
2666         LPWSTR textW;
2667         if(unicode || !HAS_STRINGS(descr))
2668             textW = (LPWSTR)lParam;
2669         else
2670         {
2671             LPSTR textA = (LPSTR)lParam;
2672             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2673             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2674                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2675             else
2676                 return LB_ERRSPACE;
2677         }
2678         wParam = LISTBOX_FindFileStrPos( descr, textW );
2679         ret = LISTBOX_InsertString( descr, wParam, textW );
2680         if(!unicode && HAS_STRINGS(descr))
2681             HeapFree(GetProcessHeap(), 0, textW);
2682         return ret;
2683     }
2684
2685     case LB_DELETESTRING:
2686         if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2687             return descr->nb_items;
2688         else
2689         {
2690             SetLastError(ERROR_INVALID_INDEX);
2691             return LB_ERR;
2692         }
2693
2694     case LB_GETITEMDATA:
2695         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2696         {
2697             SetLastError(ERROR_INVALID_INDEX);
2698             return LB_ERR;
2699         }
2700         return descr->items[wParam].data;
2701
2702     case LB_SETITEMDATA:
2703         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2704         {
2705             SetLastError(ERROR_INVALID_INDEX);
2706             return LB_ERR;
2707         }
2708         descr->items[wParam].data = lParam;
2709         /* undocumented: returns TRUE, not LB_OKAY (0) */
2710         return TRUE;
2711
2712     case LB_GETCOUNT:
2713         return descr->nb_items;
2714
2715     case LB_GETTEXT:
2716         return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
2717
2718     case LB_GETTEXTLEN:
2719         if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2720         {
2721             SetLastError(ERROR_INVALID_INDEX);
2722             return LB_ERR;
2723         }
2724         if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2725         if (unicode) return strlenW( descr->items[wParam].str );
2726         return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2727                                     strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2728
2729     case LB_GETCURSEL:
2730         if (descr->nb_items == 0)
2731             return LB_ERR;
2732         if (!IS_MULTISELECT(descr))
2733             return descr->selected_item;
2734         if (descr->selected_item != -1)
2735             return descr->selected_item;
2736         return descr->focus_item;
2737         /* otherwise, if the user tries to move the selection with the    */
2738         /* arrow keys, we will give the application something to choke on */
2739     case LB_GETTOPINDEX:
2740         return descr->top_item;
2741
2742     case LB_GETITEMHEIGHT:
2743         return LISTBOX_GetItemHeight( descr, wParam );
2744
2745     case LB_SETITEMHEIGHT:
2746         return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
2747
2748     case LB_ITEMFROMPOINT:
2749         {
2750             POINT pt;
2751             RECT rect;
2752             int index;
2753             BOOL hit = TRUE;
2754
2755             /* The hiword of the return value is not a client area
2756                hittest as suggested by MSDN, but rather a hittest on
2757                the returned listbox item. */
2758
2759             if(descr->nb_items == 0)
2760                 return 0x1ffff;      /* win9x returns 0x10000, we copy winnt */
2761
2762             pt.x = (short)LOWORD(lParam);
2763             pt.y = (short)HIWORD(lParam);
2764
2765             SetRect(&rect, 0, 0, descr->width, descr->height);
2766
2767             if(!PtInRect(&rect, pt))
2768             {
2769                 pt.x = min(pt.x, rect.right - 1);
2770                 pt.x = max(pt.x, 0);
2771                 pt.y = min(pt.y, rect.bottom - 1);
2772                 pt.y = max(pt.y, 0);
2773                 hit = FALSE;
2774             }
2775
2776             index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);
2777
2778             if(index == -1)
2779             {
2780                 index = descr->nb_items - 1;
2781                 hit = FALSE;
2782             }
2783             return MAKELONG(index, hit ? 0 : 1);
2784         }
2785
2786     case LB_SETCARETINDEX:
2787         if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2788         if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2789             return LB_ERR;
2790         else if (ISWIN31)
2791              return wParam;
2792         else
2793              return LB_OKAY;
2794
2795     case LB_GETCARETINDEX:
2796         return descr->focus_item;
2797
2798     case LB_SETTOPINDEX:
2799         return LISTBOX_SetTopItem( descr, wParam, TRUE );
2800
2801     case LB_SETCOLUMNWIDTH:
2802         return LISTBOX_SetColumnWidth( descr, wParam );
2803
2804     case LB_GETITEMRECT:
2805         return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2806
2807     case LB_FINDSTRING:
2808     {
2809         INT ret;
2810         LPWSTR textW;
2811         if(unicode || !HAS_STRINGS(descr))
2812             textW = (LPWSTR)lParam;
2813         else
2814         {
2815             LPSTR textA = (LPSTR)lParam;
2816             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2817             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2818                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2819         }
2820         ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2821         if(!unicode && HAS_STRINGS(descr))
2822             HeapFree(GetProcessHeap(), 0, textW);
2823         return ret;
2824     }
2825
2826     case LB_FINDSTRINGEXACT:
2827     {
2828         INT ret;
2829         LPWSTR textW;
2830         if(unicode || !HAS_STRINGS(descr))
2831             textW = (LPWSTR)lParam;
2832         else
2833         {
2834             LPSTR textA = (LPSTR)lParam;
2835             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2836             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2837                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2838         }
2839         ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2840         if(!unicode && HAS_STRINGS(descr))
2841             HeapFree(GetProcessHeap(), 0, textW);
2842         return ret;
2843     }
2844
2845     case LB_SELECTSTRING:
2846     {
2847         INT index;
2848         LPWSTR textW;
2849
2850         if(HAS_STRINGS(descr))
2851             TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2852                                                      debugstr_a((LPSTR)lParam));
2853         if(unicode || !HAS_STRINGS(descr))
2854             textW = (LPWSTR)lParam;
2855         else
2856         {
2857             LPSTR textA = (LPSTR)lParam;
2858             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2859             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2860                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2861         }
2862         index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2863         if(!unicode && HAS_STRINGS(descr))
2864             HeapFree(GetProcessHeap(), 0, textW);
2865         if (index != LB_ERR)
2866         {
2867             LISTBOX_MoveCaret( descr, index, TRUE );
2868             LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2869         }
2870         return index;
2871     }
2872
2873     case LB_GETSEL:
2874         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2875             return LB_ERR;
2876         return descr->items[wParam].selected;
2877
2878     case LB_SETSEL:
2879         return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
2880
2881     case LB_SETCURSEL:
2882         if (IS_MULTISELECT(descr)) return LB_ERR;
2883         LISTBOX_SetCaretIndex( descr, wParam, FALSE );
2884         ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2885         if (ret != LB_ERR) ret = descr->selected_item;
2886         return ret;
2887
2888     case LB_GETSELCOUNT:
2889         return LISTBOX_GetSelCount( descr );
2890
2891     case LB_GETSELITEMS:
2892         return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2893
2894     case LB_SELITEMRANGE:
2895         if (LOWORD(lParam) <= HIWORD(lParam))
2896             return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
2897                                             HIWORD(lParam), wParam );
2898         else
2899             return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
2900                                             LOWORD(lParam), wParam );
2901
2902     case LB_SELITEMRANGEEX:
2903         if ((INT)lParam >= (INT)wParam)
2904             return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
2905         else
2906             return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
2907
2908     case LB_GETHORIZONTALEXTENT:
2909         return descr->horz_extent;
2910
2911     case LB_SETHORIZONTALEXTENT:
2912         return LISTBOX_SetHorizontalExtent( descr, wParam );
2913
2914     case LB_GETANCHORINDEX:
2915         return descr->anchor_item;
2916
2917     case LB_SETANCHORINDEX:
2918         if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2919         {
2920             SetLastError(ERROR_INVALID_INDEX);
2921             return LB_ERR;
2922         }
2923         descr->anchor_item = (INT)wParam;
2924         return LB_OKAY;
2925
2926     case LB_DIR:
2927     {
2928         INT ret;
2929         LPWSTR textW;
2930         if(unicode)
2931             textW = (LPWSTR)lParam;
2932         else
2933         {
2934             LPSTR textA = (LPSTR)lParam;
2935             INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2936             if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2937                 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2938         }
2939         ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2940         if(!unicode)
2941             HeapFree(GetProcessHeap(), 0, textW);
2942         return ret;
2943     }
2944
2945     case LB_GETLOCALE:
2946         return descr->locale;
2947
2948     case LB_SETLOCALE:
2949     {
2950         LCID ret;
2951         if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
2952             return LB_ERR;
2953         ret = descr->locale;
2954         descr->locale = (LCID)wParam;
2955         return ret;
2956     }
2957
2958     case LB_INITSTORAGE:
2959         return LISTBOX_InitStorage( descr, wParam );
2960
2961     case LB_SETCOUNT:
2962         return LISTBOX_SetCount( descr, (INT)wParam );
2963
2964     case LB_SETTABSTOPS:
2965         return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
2966
2967     case LB_CARETON:
2968         if (descr->caret_on)
2969             return LB_OKAY;
2970         descr->caret_on = TRUE;
2971         if ((descr->focus_item != -1) && (descr->in_focus))
2972             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2973         return LB_OKAY;
2974
2975     case LB_CARETOFF:
2976         if (!descr->caret_on)
2977             return LB_OKAY;
2978         descr->caret_on = FALSE;
2979         if ((descr->focus_item != -1) && (descr->in_focus))
2980             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2981         return LB_OKAY;
2982
2983     case LB_GETLISTBOXINFO:
2984         FIXME("LB_GETLISTBOXINFO: stub!\n");
2985         return 0;
2986
2987     case WM_DESTROY:
2988         return LISTBOX_Destroy( descr );
2989
2990     case WM_ENABLE:
2991         InvalidateRect( descr->self, NULL, TRUE );
2992         return 0;
2993
2994     case WM_SETREDRAW:
2995         LISTBOX_SetRedraw( descr, wParam != 0 );
2996         return 0;
2997
2998     case WM_GETDLGCODE:
2999         return DLGC_WANTARROWS | DLGC_WANTCHARS;
3000
3001     case WM_PRINTCLIENT:
3002     case WM_PAINT:
3003         {
3004             PAINTSTRUCT ps;
3005             HDC hdc = ( wParam ) ? ((HDC)wParam) :  BeginPaint( descr->self, &ps );
3006             ret = LISTBOX_Paint( descr, hdc );
3007             if( !wParam ) EndPaint( descr->self, &ps );
3008         }
3009         return ret;
3010     case WM_SIZE:
3011         LISTBOX_UpdateSize( descr );
3012         return 0;
3013     case WM_GETFONT:
3014         return (LRESULT)descr->font;
3015     case WM_SETFONT:
3016         LISTBOX_SetFont( descr, (HFONT)wParam );
3017         if (lParam) InvalidateRect( descr->self, 0, TRUE );
3018         return 0;
3019     case WM_SETFOCUS:
3020         descr->in_focus = TRUE;
3021         descr->caret_on = TRUE;
3022         if (descr->focus_item != -1)
3023             LISTBOX_DrawFocusRect( descr, TRUE );
3024         SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3025         return 0;
3026     case WM_KILLFOCUS:
3027         descr->in_focus = FALSE;
3028         if ((descr->focus_item != -1) && descr->caret_on)
3029             LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
3030         SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3031         return 0;
3032     case WM_HSCROLL:
3033         return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3034     case WM_VSCROLL:
3035         return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3036     case WM_MOUSEWHEEL:
3037         if (wParam & (MK_SHIFT | MK_CONTROL))
3038             return DefWindowProcW( descr->self, msg, wParam, lParam );
3039         return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
3040     case WM_LBUTTONDOWN:
3041         if (lphc)
3042             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3043                                                   (INT16)LOWORD(lParam),
3044                                                   (INT16)HIWORD(lParam) );
3045         return LISTBOX_HandleLButtonDown( descr, wParam,
3046                                           (INT16)LOWORD(lParam),
3047                                           (INT16)HIWORD(lParam) );
3048     case WM_LBUTTONDBLCLK:
3049         if (lphc)
3050             return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3051                                                   (INT16)LOWORD(lParam),
3052                                                   (INT16)HIWORD(lParam) );
3053         if (descr->style & LBS_NOTIFY)
3054             SEND_NOTIFICATION( descr, LBN_DBLCLK );
3055         return 0;
3056     case WM_MOUSEMOVE:
3057         if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
3058         {
3059             BOOL    captured = descr->captured;
3060             POINT   mousePos;
3061             RECT    clientRect;
3062
3063             mousePos.x = (INT16)LOWORD(lParam);
3064             mousePos.y = (INT16)HIWORD(lParam);
3065
3066             /*
3067              * If we are in a dropdown combobox, we simulate that
3068              * the mouse is captured to show the tracking of the item.
3069              */
3070             if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3071                 descr->captured = TRUE;
3072
3073             LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3074
3075             descr->captured = captured;
3076         } 
3077         else if (GetCapture() == descr->self)
3078         {
3079             LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
3080                                      (INT16)HIWORD(lParam) );
3081         }
3082         return 0;
3083     case WM_LBUTTONUP:
3084         if (lphc)
3085         {
3086             POINT mousePos;
3087             RECT  clientRect;
3088
3089             /*
3090              * If the mouse button "up" is not in the listbox,
3091              * we make sure there is no selection by re-selecting the
3092              * item that was selected when the listbox was made visible.
3093              */
3094             mousePos.x = (INT16)LOWORD(lParam);
3095             mousePos.y = (INT16)HIWORD(lParam);
3096
3097             GetClientRect(descr->self, &clientRect);
3098
3099             /*
3100              * When the user clicks outside the combobox and the focus
3101              * is lost, the owning combobox will send a fake buttonup with
3102              * 0xFFFFFFF as the mouse location, we must also revert the
3103              * selection to the original selection.
3104              */
3105             if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3106                 LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3107         }
3108         return LISTBOX_HandleLButtonUp( descr );
3109     case WM_KEYDOWN:
3110         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3111         {
3112             /* for some reason Windows makes it possible to
3113              * show/hide ComboLBox by sending it WM_KEYDOWNs */
3114
3115             if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3116                 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3117                   && (wParam == VK_DOWN || wParam == VK_UP)) )
3118             {
3119                 COMBO_FlipListbox( lphc, FALSE, FALSE );
3120                 return 0;
3121             }
3122         }
3123         return LISTBOX_HandleKeyDown( descr, wParam );
3124     case WM_CHAR:
3125     {
3126         WCHAR charW;
3127         if(unicode)
3128             charW = (WCHAR)wParam;
3129         else
3130         {
3131             CHAR charA = (CHAR)wParam;
3132             MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3133         }
3134         return LISTBOX_HandleChar( descr, charW );
3135     }
3136     case WM_SYSTIMER:
3137         return LISTBOX_HandleSystemTimer( descr );
3138     case WM_ERASEBKGND:
3139         if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3140         {
3141             RECT rect;
3142             HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3143                                               wParam, (LPARAM)descr->self );
3144             TRACE("hbrush = %p\n", hbrush);
3145             if(!hbrush)
3146                 hbrush = GetSysColorBrush(COLOR_WINDOW);
3147             if(hbrush)
3148             {
3149                 GetClientRect(descr->self, &rect);
3150                 FillRect((HDC)wParam, &rect, hbrush);
3151             }
3152         }
3153         return 1;
3154     case WM_DROPFILES:
3155         if( lphc ) return 0;
3156         return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3157                          SendMessageA( descr->owner, msg, wParam, lParam );
3158
3159     case WM_NCDESTROY:
3160         if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
3161             lphc->hWndLBox = 0;
3162         break;
3163
3164     case WM_NCACTIVATE:
3165         if (lphc) return 0;
3166         break;
3167
3168     default:
3169         if ((msg >= WM_USER) && (msg < 0xc000))
3170             WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3171                  hwnd, msg, wParam, lParam );
3172     }
3173
3174     return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3175                      DefWindowProcA( hwnd, msg, wParam, lParam );
3176 }