listbox.c 100 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 * Listbox controls
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 *
 * Copyright 1996 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 21 22 23 24 25 26 27 28 29 30
 *
 * NOTES
 *
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Oct. 9, 2004, by Dimitrie O. Paun.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features, or bugs, please note them below.
 *
 * TODO:
 *    - LBS_NODATA
Alexandre Julliard's avatar
Alexandre Julliard committed
31 32
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
33
#include <string.h>
34
#include <stdlib.h>
35
#include <stdarg.h>
36 37
#include <stdio.h>
#include "windef.h"
38
#include "winbase.h"
39
#include "wingdi.h"
40
#include "wine/unicode.h"
41
#include "user_private.h"
42
#include "controls.h"
43
#include "wine/exception.h"
44
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
45

46
WINE_DEFAULT_DEBUG_CHANNEL(listbox);
47

Alexandre Julliard's avatar
Alexandre Julliard committed
48 49 50 51 52 53 54 55 56
/* Items array granularity */
#define LB_ARRAY_GRANULARITY 16

/* Scrolling timeout in ms */
#define LB_SCROLL_TIMEOUT 50

/* Listbox system timer id */
#define LB_TIMER_ID  2

57
/* flag listbox changed while setredraw false - internal style */
58
#define LBS_DISPLAYCHANGED 0x80000000
59

Alexandre Julliard's avatar
Alexandre Julliard committed
60 61 62
/* Item structure */
typedef struct
{
63 64 65 66
    LPWSTR    str;       /* Item text */
    BOOL      selected;  /* Is item selected? */
    UINT      height;    /* Item height (only for OWNERDRAWVARIABLE) */
    ULONG_PTR data;      /* User data */
Alexandre Julliard's avatar
Alexandre Julliard committed
67 68 69 70 71
} LB_ITEMDATA;

/* Listbox structure */
typedef struct
{
72
    HWND        self;           /* Our own window handle */
73 74 75 76
    HWND        owner;          /* Owner window to send notifications to */
    UINT        style;          /* Window style */
    INT         width;          /* Window width */
    INT         height;         /* Window height */
77
    LB_ITEMDATA  *items;        /* Array of items */
78 79 80 81 82 83 84 85
    INT         nb_items;       /* Number of items */
    INT         top_item;       /* Top visible item */
    INT         selected_item;  /* Selected item */
    INT         focus_item;     /* Item that has the focus */
    INT         anchor_item;    /* Anchor item for extended selection */
    INT         item_height;    /* Default item height */
    INT         page_size;      /* Items per listbox page */
    INT         column_width;   /* Column width for multi-column listboxes */
86
    INT         horz_extent;    /* Horizontal extent */
87 88 89
    INT         horz_pos;       /* Horizontal position */
    INT         nb_tabs;        /* Number of tabs in array */
    INT        *tabs;           /* Array of tabs */
90
    INT         avg_char_width; /* Average width of characters */
91
    INT         wheel_remain;   /* Left over scroll amount */
92
    BOOL        caret_on;       /* Is caret on? */
93
    BOOL        captured;       /* Is mouse captured? */
Alexandre Julliard's avatar
Alexandre Julliard committed
94
    BOOL	in_focus;
95
    HFONT       font;           /* Current font */
96 97
    LCID          locale;       /* Current locale for string comparisons */
    LPHEADCOMBO   lphc;		/* ComboLBox */
Alexandre Julliard's avatar
Alexandre Julliard committed
98 99 100 101 102 103 104 105 106
} LB_DESCR;


#define IS_OWNERDRAW(descr) \
    ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))

#define HAS_STRINGS(descr) \
    (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))

107 108

#define IS_MULTISELECT(descr) \
109 110
    ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \
     !((descr)->style & LBS_NOSEL))
111

112
#define SEND_NOTIFICATION(descr,code) \
113
    (SendMessageW( (descr)->owner, WM_COMMAND, \
114
     MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
115

116 117
#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)

Alexandre Julliard's avatar
Alexandre Julliard committed
118 119 120 121 122 123 124 125 126 127 128
/* Current timer status */
typedef enum
{
    LB_TIMER_NONE,
    LB_TIMER_UP,
    LB_TIMER_LEFT,
    LB_TIMER_DOWN,
    LB_TIMER_RIGHT
} TIMER_DIRECTION;

static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
Alexandre Julliard's avatar
Alexandre Julliard committed
129

130
static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
131 132 133 134

/*********************************************************************
 * listbox class descriptor
 */
135
static const WCHAR listboxW[] = {'L','i','s','t','B','o','x',0};
136 137
const struct builtin_class_descr LISTBOX_builtin_class =
{
138
    listboxW,             /* name */
139
    CS_DBLCLKS /*| CS_PARENTDC*/,  /* style */
140
    WINPROC_LISTBOX,      /* proc */
141
    sizeof(LB_DESCR *),   /* extra */
142
    IDC_ARROW,            /* cursor */
143 144 145 146 147 148 149
    0                     /* brush */
};


/*********************************************************************
 * combolbox class descriptor
 */
150
static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0};
151 152
const struct builtin_class_descr COMBOLBOX_builtin_class =
{
153
    combolboxW,           /* name */
154
    CS_DBLCLKS | CS_SAVEBITS,  /* style */
155
    WINPROC_LISTBOX,      /* proc */
156
    sizeof(LB_DESCR *),   /* extra */
157
    IDC_ARROW,            /* cursor */
158 159 160
    0                     /* brush */
};

Alexandre Julliard's avatar
Alexandre Julliard committed
161

Alexandre Julliard's avatar
Alexandre Julliard committed
162 163 164 165 166
/***********************************************************************
 *           LISTBOX_GetCurrentPageSize
 *
 * Return the current page size
 */
167
static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
168
{
169
    INT i, height;
Alexandre Julliard's avatar
Alexandre Julliard committed
170 171
    if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
    for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
172
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
173
        if ((height += descr->items[i].height) > descr->height) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
174
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
175 176
    if (i == descr->top_item) return 1;
    else return i - descr->top_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
177 178 179
}


Alexandre Julliard's avatar
Alexandre Julliard committed
180 181 182 183 184
/***********************************************************************
 *           LISTBOX_GetMaxTopIndex
 *
 * Return the maximum possible index for the top of the listbox.
 */
185
static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
186
{
187
    INT max, page;
Alexandre Julliard's avatar
Alexandre Julliard committed
188

Alexandre Julliard's avatar
Alexandre Julliard committed
189
    if (descr->style & LBS_OWNERDRAWVARIABLE)
Alexandre Julliard's avatar
Alexandre Julliard committed
190
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
191 192 193 194
        page = descr->height;
        for (max = descr->nb_items - 1; max >= 0; max--)
            if ((page -= descr->items[max].height) < 0) break;
        if (max < descr->nb_items - 1) max++;
Alexandre Julliard's avatar
Alexandre Julliard committed
195
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
196
    else if (descr->style & LBS_MULTICOLUMN)
Alexandre Julliard's avatar
Alexandre Julliard committed
197
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
198 199 200
        if ((page = descr->width / descr->column_width) < 1) page = 1;
        max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
        max = (max - page) * descr->page_size;
Alexandre Julliard's avatar
Alexandre Julliard committed
201 202
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
203
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
204
        max = descr->nb_items - descr->page_size;
Alexandre Julliard's avatar
Alexandre Julliard committed
205
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
206 207
    if (max < 0) max = 0;
    return max;
Alexandre Julliard's avatar
Alexandre Julliard committed
208 209
}

Alexandre Julliard's avatar
Alexandre Julliard committed
210

Alexandre Julliard's avatar
Alexandre Julliard committed
211 212 213 214 215 216
/***********************************************************************
 *           LISTBOX_UpdateScroll
 *
 * Update the scrollbars. Should be called whenever the content
 * of the listbox changes.
 */
217
static void LISTBOX_UpdateScroll( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
218
{
Alexandre Julliard's avatar
Alexandre Julliard committed
219
    SCROLLINFO info;
Alexandre Julliard's avatar
Alexandre Julliard committed
220

221 222 223 224 225
    /* Check the listbox scroll bar flags individually before we call
       SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
       no WS_VSCROLL, we end up with an uninitialized, visible horizontal
       scroll bar when we do not need one.
    if (!(descr->style & WS_VSCROLL)) return;
226
    */
227

228 229 230 231 232 233
    /*   It is important that we check descr->style, and not wnd->dwStyle,
       for WS_VSCROLL, as the former is exactly the one passed in
       argument to CreateWindow.
         In Windows (and from now on in Wine :) a listbox created
       with such a style (no WS_SCROLL) does not update
       the scrollbar with listbox-related data, thus letting
Alexandre Julliard's avatar
Alexandre Julliard committed
234 235
       the programmer use it for his/her own purposes. */

Alexandre Julliard's avatar
Alexandre Julliard committed
236 237
    if (descr->style & LBS_NOREDRAW) return;
    info.cbSize = sizeof(info);
Alexandre Julliard's avatar
Alexandre Julliard committed
238

Alexandre Julliard's avatar
Alexandre Julliard committed
239 240 241 242 243 244 245 246 247 248
    if (descr->style & LBS_MULTICOLUMN)
    {
        info.nMin  = 0;
        info.nMax  = (descr->nb_items - 1) / descr->page_size;
        info.nPos  = descr->top_item / descr->page_size;
        info.nPage = descr->width / descr->column_width;
        if (info.nPage < 1) info.nPage = 1;
        info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
        if (descr->style & LBS_DISABLENOSCROLL)
            info.fMask |= SIF_DISABLENOSCROLL;
249
        if (descr->style & WS_HSCROLL)
250
            SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
251 252
        info.nMax = 0;
        info.fMask = SIF_RANGE;
253
        if (descr->style & WS_VSCROLL)
254
            SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
255 256 257 258 259 260
    }
    else
    {
        info.nMin  = 0;
        info.nMax  = descr->nb_items - 1;
        info.nPos  = descr->top_item;
261
        info.nPage = LISTBOX_GetCurrentPageSize( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
262 263 264
        info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
        if (descr->style & LBS_DISABLENOSCROLL)
            info.fMask |= SIF_DISABLENOSCROLL;
265
        if (descr->style & WS_VSCROLL)
266
            SetScrollInfo( descr->self, SB_VERT, &info, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
267

268
        if ((descr->style & WS_HSCROLL) && descr->horz_extent)
Alexandre Julliard's avatar
Alexandre Julliard committed
269 270 271
        {
            info.nPos  = descr->horz_pos;
            info.nPage = descr->width;
272
            info.fMask = SIF_POS | SIF_PAGE;
Alexandre Julliard's avatar
Alexandre Julliard committed
273 274
            if (descr->style & LBS_DISABLENOSCROLL)
                info.fMask |= SIF_DISABLENOSCROLL;
275
            SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
276
        }
277 278 279 280 281 282 283 284 285 286 287 288 289 290
        else
        {
            if (descr->style & LBS_DISABLENOSCROLL)
            {
                info.nMin  = 0;
                info.nMax  = 0;
                info.fMask = SIF_RANGE | SIF_DISABLENOSCROLL;
                SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
            }
            else
            {
                ShowScrollBar( descr->self, SB_HORZ, FALSE );
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
291 292
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
293 294


Alexandre Julliard's avatar
Alexandre Julliard committed
295 296 297 298 299
/***********************************************************************
 *           LISTBOX_SetTopItem
 *
 * Set the top item of the listbox, scrolling up or down if necessary.
 */
300
static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
301
{
302
    INT max = LISTBOX_GetMaxTopIndex( descr );
303 304 305

    TRACE("setting top item %d, scroll %d\n", index, scroll);

Alexandre Julliard's avatar
Alexandre Julliard committed
306 307 308 309
    if (index > max) index = max;
    if (index < 0) index = 0;
    if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
    if (descr->top_item == index) return LB_OKAY;
310
    if (scroll)
Alexandre Julliard's avatar
Alexandre Julliard committed
311
    {
312
        INT diff;
313 314 315
        if (descr->style & LBS_MULTICOLUMN)
            diff = (descr->top_item - index) / descr->page_size * descr->column_width;
        else if (descr->style & LBS_OWNERDRAWVARIABLE)
Alexandre Julliard's avatar
Alexandre Julliard committed
316
        {
317
            INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
318 319 320 321 322 323 324 325 326 327 328 329
            diff = 0;
            if (index > descr->top_item)
            {
                for (i = index - 1; i >= descr->top_item; i--)
                    diff -= descr->items[i].height;
            }
            else
            {
                for (i = index; i < descr->top_item; i++)
                    diff += descr->items[i].height;
            }
        }
330
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
331
            diff = (descr->top_item - index) * descr->item_height;
Alexandre Julliard's avatar
Alexandre Julliard committed
332

333 334
        ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
                        SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
335
    }
336 337
    else
        InvalidateRect( descr->self, NULL, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
338
    descr->top_item = index;
339
    LISTBOX_UpdateScroll( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
340
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
341 342 343
}


Alexandre Julliard's avatar
Alexandre Julliard committed
344 345 346 347 348 349
/***********************************************************************
 *           LISTBOX_UpdatePage
 *
 * Update the page size. Should be called when the size of
 * the client area or the item height changes.
 */
350
static void LISTBOX_UpdatePage( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
351
{
352
    INT page_size;
Alexandre Julliard's avatar
Alexandre Julliard committed
353

354
    if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
355
                       page_size = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
356 357 358
    if (page_size == descr->page_size) return;
    descr->page_size = page_size;
    if (descr->style & LBS_MULTICOLUMN)
359 360
        InvalidateRect( descr->self, NULL, TRUE );
    LISTBOX_SetTopItem( descr, descr->top_item, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
361 362 363
}


Alexandre Julliard's avatar
Alexandre Julliard committed
364 365 366 367 368 369
/***********************************************************************
 *           LISTBOX_UpdateSize
 *
 * Update the size of the listbox. Should be called when the size of
 * the client area changes.
 */
370
static void LISTBOX_UpdateSize( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
371
{
372
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
373

374
    GetClientRect( descr->self, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
375 376
    descr->width  = rect.right - rect.left;
    descr->height = rect.bottom - rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
377
    if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
Alexandre Julliard's avatar
Alexandre Julliard committed
378
    {
379
        INT remaining;
380
        RECT rect;
381

382
        GetWindowRect( descr->self, &rect );
383 384 385 386
        if(descr->item_height != 0)
            remaining = descr->height % descr->item_height;
        else
            remaining = 0;
387
        if ((descr->height > descr->item_height) && remaining)
Alexandre Julliard's avatar
Alexandre Julliard committed
388
        {
389
            TRACE("[%p]: changing height %d -> %d\n",
390 391 392 393
                  descr->self, descr->height, descr->height - remaining );
            SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left,
                          rect.bottom - rect.top - remaining,
                          SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
Alexandre Julliard's avatar
Alexandre Julliard committed
394 395
            return;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
396
    }
397 398 399
    TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height );
    LISTBOX_UpdatePage( descr );
    LISTBOX_UpdateScroll( descr );
400 401

    /* Invalidate the focused item so it will be repainted correctly */
402
    if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
403
    {
404
        InvalidateRect( descr->self, &rect, FALSE );
405
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
406 407 408
}


Alexandre Julliard's avatar
Alexandre Julliard committed
409 410 411 412 413 414
/***********************************************************************
 *           LISTBOX_GetItemRect
 *
 * Get the rectangle enclosing an item, in listbox client coordinates.
 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
 */
415
static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect )
Alexandre Julliard's avatar
Alexandre Julliard committed
416
{
Alexandre Julliard's avatar
Alexandre Julliard committed
417
    /* Index <= 0 is legal even on empty listboxes */
418 419
    if (index && (index >= descr->nb_items))
    {
420
        SetRectEmpty(rect);
421 422 423
        SetLastError(ERROR_INVALID_INDEX);
        return LB_ERR;
    }
424
    SetRect( rect, 0, 0, descr->width, descr->height );
Alexandre Julliard's avatar
Alexandre Julliard committed
425 426
    if (descr->style & LBS_MULTICOLUMN)
    {
427
        INT col = (index / descr->page_size) -
Alexandre Julliard's avatar
Alexandre Julliard committed
428 429 430 431 432 433 434 435
                        (descr->top_item / descr->page_size);
        rect->left += col * descr->column_width;
        rect->right = rect->left + descr->column_width;
        rect->top += (index % descr->page_size) * descr->item_height;
        rect->bottom = rect->top + descr->item_height;
    }
    else if (descr->style & LBS_OWNERDRAWVARIABLE)
    {
436
        INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
437 438
        rect->right += descr->horz_pos;
        if ((index >= 0) && (index < descr->nb_items))
Alexandre Julliard's avatar
Alexandre Julliard committed
439
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
440 441 442 443 444 445 446 447 448 449 450 451
            if (index < descr->top_item)
            {
                for (i = descr->top_item-1; i >= index; i--)
                    rect->top -= descr->items[i].height;
            }
            else
            {
                for (i = descr->top_item; i < index; i++)
                    rect->top += descr->items[i].height;
            }
            rect->bottom = rect->top + descr->items[index].height;

Alexandre Julliard's avatar
Alexandre Julliard committed
452 453 454 455 456 457 458 459
        }
    }
    else
    {
        rect->top += (index - descr->top_item) * descr->item_height;
        rect->bottom = rect->top + descr->item_height;
        rect->right += descr->horz_pos;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
460

461 462
    TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect));

Alexandre Julliard's avatar
Alexandre Julliard committed
463 464
    return ((rect->left < descr->width) && (rect->right > 0) &&
            (rect->top < descr->height) && (rect->bottom > 0));
Alexandre Julliard's avatar
Alexandre Julliard committed
465 466 467
}


Alexandre Julliard's avatar
Alexandre Julliard committed
468 469 470 471 472
/***********************************************************************
 *           LISTBOX_GetItemFromPoint
 *
 * Return the item nearest from point (x,y) (in client coordinates).
 */
473
static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
474
{
475
    INT index = descr->top_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
476

Alexandre Julliard's avatar
Alexandre Julliard committed
477 478
    if (!descr->nb_items) return -1;  /* No items */
    if (descr->style & LBS_OWNERDRAWVARIABLE)
Alexandre Julliard's avatar
Alexandre Julliard committed
479
    {
480
        INT pos = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
481
        if (y >= 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
482
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
483
            while (index < descr->nb_items)
Alexandre Julliard's avatar
Alexandre Julliard committed
484
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
485 486
                if ((pos += descr->items[index].height) > y) break;
                index++;
Alexandre Julliard's avatar
Alexandre Julliard committed
487 488
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
489
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
490
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
491
            while (index > 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
492
            {
Alexandre Julliard's avatar
Alexandre Julliard committed
493 494
                index--;
                if ((pos -= descr->items[index].height) <= y) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
495 496
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
497
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
498
    else if (descr->style & LBS_MULTICOLUMN)
Alexandre Julliard's avatar
Alexandre Julliard committed
499
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
500 501 502 503
        if (y >= descr->item_height * descr->page_size) return -1;
        if (y >= 0) index += y / descr->item_height;
        if (x >= 0) index += (x / descr->column_width) * descr->page_size;
        else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
Alexandre Julliard's avatar
Alexandre Julliard committed
504
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
505 506 507 508 509 510 511
    else
    {
        index += (y / descr->item_height);
    }
    if (index < 0) return 0;
    if (index >= descr->nb_items) return -1;
    return index;
Alexandre Julliard's avatar
Alexandre Julliard committed
512 513 514
}


Alexandre Julliard's avatar
Alexandre Julliard committed
515 516 517 518 519
/***********************************************************************
 *           LISTBOX_PaintItem
 *
 * Paint an item.
 */
520 521
static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect, 
			       INT index, UINT action, BOOL ignoreFocus )
Alexandre Julliard's avatar
Alexandre Julliard committed
522
{
Alexandre Julliard's avatar
Alexandre Julliard committed
523 524
    LB_ITEMDATA *item = NULL;
    if (index < descr->nb_items) item = &descr->items[index];
Alexandre Julliard's avatar
Alexandre Julliard committed
525

Alexandre Julliard's avatar
Alexandre Julliard committed
526 527
    if (IS_OWNERDRAW(descr))
    {
528
        DRAWITEMSTRUCT dis;
529 530
        RECT r;
        HRGN hrgn;
Alexandre Julliard's avatar
Alexandre Julliard committed
531

532 533
	if (!item)
	{
534
	    if (action == ODA_FOCUS)
535
		DrawFocusRect( hdc, rect );
536
	    else
537
	        ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
538 539
	    return;
	}
540 541 542 543 544

        /* some programs mess with the clipping region when
        drawing the item, *and* restore the previous region
        after they are done, so a region has better to exist
        else everything ends clipped */
545
        GetClientRect(descr->self, &r);
546
        hrgn = set_control_clipping( hdc, &r );
547

Alexandre Julliard's avatar
Alexandre Julliard committed
548
        dis.CtlType      = ODT_LISTBOX;
549
        dis.CtlID        = GetWindowLongPtrW( descr->self, GWLP_ID );
550
        dis.hwndItem     = descr->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
551 552 553 554
        dis.itemAction   = action;
        dis.hDC          = hdc;
        dis.itemID       = index;
        dis.itemState    = 0;
555
        if (item->selected) dis.itemState |= ODS_SELECTED;
556
        if (!ignoreFocus && (descr->focus_item == index) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
557
            (descr->caret_on) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
558
            (descr->in_focus)) dis.itemState |= ODS_FOCUS;
559
        if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
560
        dis.itemData     = item->data;
Alexandre Julliard's avatar
Alexandre Julliard committed
561
        dis.rcItem       = *rect;
562
        TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
563
              descr->self, index, debugstr_w(item->str), action,
564
              dis.itemState, wine_dbgstr_rect(rect) );
565
        SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
566 567
        SelectClipRgn( hdc, hrgn );
        if (hrgn) DeleteObject( hrgn );
Alexandre Julliard's avatar
Alexandre Julliard committed
568 569 570
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
571 572
        COLORREF oldText = 0, oldBk = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
573 574
        if (action == ODA_FOCUS)
        {
575
            DrawFocusRect( hdc, rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
576 577 578 579
            return;
        }
        if (item && item->selected)
        {
580 581
            oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
            oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
Alexandre Julliard's avatar
Alexandre Julliard committed
582
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
583

584
        TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
585
              descr->self, index, item ? debugstr_w(item->str) : "", action,
586
              wine_dbgstr_rect(rect) );
Alexandre Julliard's avatar
Alexandre Julliard committed
587
        if (!item)
588
            ExtTextOutW( hdc, rect->left + 1, rect->top,
Alexandre Julliard's avatar
Alexandre Julliard committed
589
                           ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
590 591 592 593
        else if (!(descr->style & LBS_USETABSTOPS))
            ExtTextOutW( hdc, rect->left + 1, rect->top,
                         ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
                         strlenW(item->str), NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
594
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
595 596
	{
	    /* Output empty string to paint background in the full width. */
597 598 599 600 601
            ExtTextOutW( hdc, rect->left + 1, rect->top,
                         ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
            TabbedTextOutW( hdc, rect->left + 1 , rect->top,
                            item->str, strlenW(item->str),
                            descr->nb_tabs, descr->tabs, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
602
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604
        if (item && item->selected)
        {
605 606
            SetBkColor( hdc, oldBk );
            SetTextColor( hdc, oldText );
Alexandre Julliard's avatar
Alexandre Julliard committed
607
        }
608
        if (!ignoreFocus && (descr->focus_item == index) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
609
            (descr->caret_on) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
610
            (descr->in_focus)) DrawFocusRect( hdc, rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
611
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
612 613 614 615
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
616 617 618
 *           LISTBOX_SetRedraw
 *
 * Change the redraw flag.
Alexandre Julliard's avatar
Alexandre Julliard committed
619
 */
620
static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on )
Alexandre Julliard's avatar
Alexandre Julliard committed
621
{
Alexandre Julliard's avatar
Alexandre Julliard committed
622 623 624 625
    if (on)
    {
        if (!(descr->style & LBS_NOREDRAW)) return;
        descr->style &= ~LBS_NOREDRAW;
626 627
        if (descr->style & LBS_DISPLAYCHANGED)
        {     /* page was changed while setredraw false, refresh automatically */
628
            InvalidateRect(descr->self, NULL, TRUE);
629
            if ((descr->top_item + descr->page_size) > descr->nb_items)
630
            {      /* reset top of page if less than number of items/page */
631 632 633 634 635
                descr->top_item = descr->nb_items - descr->page_size;
                if (descr->top_item < 0) descr->top_item = 0;
            }
            descr->style &= ~LBS_DISPLAYCHANGED;
        }
636
        LISTBOX_UpdateScroll( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
637 638
    }
    else descr->style |= LBS_NOREDRAW;
Alexandre Julliard's avatar
Alexandre Julliard committed
639
}
Alexandre Julliard's avatar
Alexandre Julliard committed
640

Alexandre Julliard's avatar
Alexandre Julliard committed
641

Alexandre Julliard's avatar
Alexandre Julliard committed
642
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
643 644 645
 *           LISTBOX_RepaintItem
 *
 * Repaint a single item synchronously.
Alexandre Julliard's avatar
Alexandre Julliard committed
646
 */
647
static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action )
Alexandre Julliard's avatar
Alexandre Julliard committed
648
{
649 650 651 652
    HDC hdc;
    RECT rect;
    HFONT oldFont = 0;
    HBRUSH hbrush, oldBrush = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
653

654
    /* Do not repaint the item if the item is not visible */
655
    if (!IsWindowVisible(descr->self)) return;
656
    if (descr->style & LBS_NOREDRAW)
657
    {
658 659
        descr->style |= LBS_DISPLAYCHANGED;
        return;
660 661
    }
    if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
662
    if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
663
    if (descr->font) oldFont = SelectObject( hdc, descr->font );
664
    hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
665
				   (WPARAM)hdc, (LPARAM)descr->self );
666
    if (hbrush) oldBrush = SelectObject( hdc, hbrush );
667
    if (!IsWindowEnabled(descr->self))
668 669
        SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
    SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
670
    LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE );
671 672
    if (oldFont) SelectObject( hdc, oldFont );
    if (oldBrush) SelectObject( hdc, oldBrush );
673
    ReleaseDC( descr->self, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
674 675
}

Alexandre Julliard's avatar
Alexandre Julliard committed
676

677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
/***********************************************************************
 *           LISTBOX_DrawFocusRect
 */
static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
{
    HDC hdc;
    RECT rect;
    HFONT oldFont = 0;

    /* Do not repaint the item if the item is not visible */
    if (!IsWindowVisible(descr->self)) return;

    if (descr->focus_item == -1) return;
    if (!descr->caret_on || !descr->in_focus) return;

    if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return;
    if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return;
    if (descr->font) oldFont = SelectObject( hdc, descr->font );
    if (!IsWindowEnabled(descr->self))
        SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
    SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
698
    LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, !on );
699 700 701 702 703
    if (oldFont) SelectObject( hdc, oldFont );
    ReleaseDC( descr->self, hdc );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
704
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
705
 *           LISTBOX_InitStorage
Alexandre Julliard's avatar
Alexandre Julliard committed
706
 */
707
static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
Alexandre Julliard's avatar
Alexandre Julliard committed
708
{
Alexandre Julliard's avatar
Alexandre Julliard committed
709
    LB_ITEMDATA *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
710

Alexandre Julliard's avatar
Alexandre Julliard committed
711 712
    nb_items += LB_ARRAY_GRANULARITY - 1;
    nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
713
    if (descr->items) {
714
        nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
715 716 717 718 719 720 721 722 723
	item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
                              nb_items * sizeof(LB_ITEMDATA));
    }
    else {
	item = HeapAlloc( GetProcessHeap(), 0,
                              nb_items * sizeof(LB_ITEMDATA));
    }

    if (!item)
Alexandre Julliard's avatar
Alexandre Julliard committed
724
    {
725
        SEND_NOTIFICATION( descr, LBN_ERRSPACE );
Alexandre Julliard's avatar
Alexandre Julliard committed
726 727 728 729
        return LB_ERRSPACE;
    }
    descr->items = item;
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
730 731 732
}


Alexandre Julliard's avatar
Alexandre Julliard committed
733
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
734
 *           LISTBOX_SetTabStops
Alexandre Julliard's avatar
Alexandre Julliard committed
735
 */
736
static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs )
Alexandre Julliard's avatar
Alexandre Julliard committed
737
{
738 739
    INT i;

740 741 742 743 744 745
    if (!(descr->style & LBS_USETABSTOPS))
    {
        SetLastError(ERROR_LB_WITHOUT_TABSTOPS);
        return FALSE;
    }

746
    HeapFree( GetProcessHeap(), 0, descr->tabs );
Alexandre Julliard's avatar
Alexandre Julliard committed
747 748 749 750 751
    if (!(descr->nb_tabs = count))
    {
        descr->tabs = NULL;
        return TRUE;
    }
752
    if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0,
753
                                            descr->nb_tabs * sizeof(INT) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
754
        return FALSE;
755
    memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
756 757 758 759 760

    /* convert into "dialog units"*/
    for (i = 0; i < descr->nb_tabs; i++)
        descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4);

Alexandre Julliard's avatar
Alexandre Julliard committed
761
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
762 763 764 765
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
766
 *           LISTBOX_GetText
Alexandre Julliard's avatar
Alexandre Julliard committed
767
 */
768
static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
769
{
770 771
    DWORD len;

772 773 774 775 776
    if ((index < 0) || (index >= descr->nb_items))
    {
        SetLastError(ERROR_INVALID_INDEX);
        return LB_ERR;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
777 778
    if (HAS_STRINGS(descr))
    {
779
        if (!buffer)
780
        {
781
            len = strlenW(descr->items[index].str);
782 783 784 785 786
            if( unicode )
                return len;
            return WideCharToMultiByte( CP_ACP, 0, descr->items[index].str, len,
                                        NULL, 0, NULL, NULL );
        }
787

788 789
	TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));

790
        __TRY  /* hide a Delphi bug that passes a read-only buffer */
791
        {
792 793 794 795 796 797 798 799 800 801
            if(unicode)
            {
                strcpyW( buffer, descr->items[index].str );
                len = strlenW(buffer);
            }
            else
            {
                len = WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1,
                                          (LPSTR)buffer, 0x7FFFFFFF, NULL, NULL) - 1;
            }
802
        }
803
        __EXCEPT_PAGE_FAULT
804
        {
805 806 807
            WARN( "got an invalid buffer (Delphi bug?)\n" );
            SetLastError( ERROR_INVALID_PARAMETER );
            return LB_ERR;
808
        }
809
        __ENDTRY
810
    } else {
811 812
        if (buffer)
            *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
813
        len = sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
814
    }
815
    return len;
Alexandre Julliard's avatar
Alexandre Julliard committed
816 817
}

818 819 820 821 822 823 824 825 826 827 828
static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 )
{
    INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 );
    if (ret == CSTR_LESS_THAN)
        return -1;
    if (ret == CSTR_EQUAL)
        return 0;
    if (ret == CSTR_GREATER_THAN)
        return 1;
    return -1;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
829

Alexandre Julliard's avatar
Alexandre Julliard committed
830
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
831 832 833 834
 *           LISTBOX_FindStringPos
 *
 * Find the nearest string located before a given string in sort order.
 * If 'exact' is TRUE, return an error if we don't get an exact match.
Alexandre Julliard's avatar
Alexandre Julliard committed
835
 */
836
static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
Alexandre Julliard's avatar
Alexandre Julliard committed
837
{
838
    INT index, min, max, res;
Alexandre Julliard's avatar
Alexandre Julliard committed
839

Alexandre Julliard's avatar
Alexandre Julliard committed
840 841 842 843 844 845 846
    if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
    min = 0;
    max = descr->nb_items;
    while (min != max)
    {
        index = (min + max) / 2;
        if (HAS_STRINGS(descr))
847
            res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
Alexandre Julliard's avatar
Alexandre Julliard committed
848 849
        else
        {
850
            COMPAREITEMSTRUCT cis;
851
            UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
852

Alexandre Julliard's avatar
Alexandre Julliard committed
853
            cis.CtlType    = ODT_LISTBOX;
854
            cis.CtlID      = id;
855
            cis.hwndItem   = descr->self;
856 857 858
            /* note that some application (MetaStock) expects the second item
             * to be in the listbox */
            cis.itemID1    = -1;
859
            cis.itemData1  = (ULONG_PTR)str;
860 861
            cis.itemID2    = index;
            cis.itemData2  = descr->items[index].data;
Alexandre Julliard's avatar
Alexandre Julliard committed
862
            cis.dwLocaleId = descr->locale;
863
            res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
Alexandre Julliard's avatar
Alexandre Julliard committed
864 865
        }
        if (!res) return index;
866
        if (res < 0) max = index;
Alexandre Julliard's avatar
Alexandre Julliard committed
867 868 869
        else min = index + 1;
    }
    return exact ? -1 : max;
Alexandre Julliard's avatar
Alexandre Julliard committed
870 871
}

Alexandre Julliard's avatar
Alexandre Julliard committed
872

Alexandre Julliard's avatar
Alexandre Julliard committed
873
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
874 875 876 877
 *           LISTBOX_FindFileStrPos
 *
 * Find the nearest string located before a given string in directory
 * sort order (i.e. first files, then directories, then drives).
Alexandre Julliard's avatar
Alexandre Julliard committed
878
 */
879
static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
880
{
881
    INT min, max, res;
Alexandre Julliard's avatar
Alexandre Julliard committed
882

Alexandre Julliard's avatar
Alexandre Julliard committed
883
    if (!HAS_STRINGS(descr))
884
        return LISTBOX_FindStringPos( descr, str, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
885 886 887 888
    min = 0;
    max = descr->nb_items;
    while (min != max)
    {
889
        INT index = (min + max) / 2;
890
        LPCWSTR p = descr->items[index].str;
Alexandre Julliard's avatar
Alexandre Julliard committed
891 892 893 894 895 896 897 898 899 900 901
        if (*p == '[')  /* drive or directory */
        {
            if (*str != '[') res = -1;
            else if (p[1] == '-')  /* drive */
            {
                if (str[1] == '-') res = str[2] - p[2];
                else res = -1;
            }
            else  /* directory */
            {
                if (str[1] == '-') res = 1;
902
                else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
Alexandre Julliard's avatar
Alexandre Julliard committed
903 904 905 906 907
            }
        }
        else  /* filename */
        {
            if (*str == '[') res = 1;
908
            else res = LISTBOX_lstrcmpiW( descr->locale, str, p );
Alexandre Julliard's avatar
Alexandre Julliard committed
909 910 911 912 913 914
        }
        if (!res) return index;
        if (res < 0) max = index;
        else min = index + 1;
    }
    return max;
Alexandre Julliard's avatar
Alexandre Julliard committed
915 916 917 918
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
919
 *           LISTBOX_FindString
Alexandre Julliard's avatar
Alexandre Julliard committed
920
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
921
 * Find the item beginning with a given string.
Alexandre Julliard's avatar
Alexandre Julliard committed
922
 */
923
static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
Alexandre Julliard's avatar
Alexandre Julliard committed
924
{
925
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
926
    LB_ITEMDATA *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
927

Alexandre Julliard's avatar
Alexandre Julliard committed
928 929 930 931
    if (start >= descr->nb_items) start = -1;
    item = descr->items + start + 1;
    if (HAS_STRINGS(descr))
    {
932
        if (!str || ! str[0] ) return LB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
933 934 935
        if (exact)
        {
            for (i = start + 1; i < descr->nb_items; i++, item++)
936
                if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
937
            for (i = 0, item = descr->items; i <= start; i++, item++)
938
                if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
939 940 941 942 943 944 945
        }
        else
        {
 /* Special case for drives and directories: ignore prefix */
#define CHECK_DRIVE(item) \
    if ((item)->str[0] == '[') \
    { \
946 947
        if (!strncmpiW( str, (item)->str+1, len )) return i; \
        if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
Alexandre Julliard's avatar
Alexandre Julliard committed
948
        return i; \
Alexandre Julliard's avatar
Alexandre Julliard committed
949 950
    }

951
            INT len = strlenW(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
952 953
            for (i = start + 1; i < descr->nb_items; i++, item++)
            {
954
               if (!strncmpiW( str, item->str, len )) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
955 956 957 958
               CHECK_DRIVE(item);
            }
            for (i = 0, item = descr->items; i <= start; i++, item++)
            {
959
               if (!strncmpiW( str, item->str, len )) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
960 961 962 963 964 965
               CHECK_DRIVE(item);
            }
#undef CHECK_DRIVE
        }
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
966
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
967 968
        if (exact && (descr->style & LBS_SORT))
            /* If sorted, use a WM_COMPAREITEM binary search */
969
            return LISTBOX_FindStringPos( descr, str, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
970 971 972

        /* Otherwise use a linear search */
        for (i = start + 1; i < descr->nb_items; i++, item++)
973
            if (item->data == (ULONG_PTR)str) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
974
        for (i = 0, item = descr->items; i <= start; i++, item++)
975
            if (item->data == (ULONG_PTR)str) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
976
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
977
    return LB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
978 979
}

Alexandre Julliard's avatar
Alexandre Julliard committed
980

Alexandre Julliard's avatar
Alexandre Julliard committed
981
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
982
 *           LISTBOX_GetSelCount
Alexandre Julliard's avatar
Alexandre Julliard committed
983
 */
984
static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
985
{
986
    INT i, count;
987
    const LB_ITEMDATA *item = descr->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
988

989 990 991
    if (!(descr->style & LBS_MULTIPLESEL) ||
        (descr->style & LBS_NOSEL))
      return LB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
992 993 994
    for (i = count = 0; i < descr->nb_items; i++, item++)
        if (item->selected) count++;
    return count;
Alexandre Julliard's avatar
Alexandre Julliard committed
995 996
}

Alexandre Julliard's avatar
Alexandre Julliard committed
997

Alexandre Julliard's avatar
Alexandre Julliard committed
998
/***********************************************************************
999
 *           LISTBOX_GetSelItems
Alexandre Julliard's avatar
Alexandre Julliard committed
1000
 */
1001
static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
Alexandre Julliard's avatar
Alexandre Julliard committed
1002
{
1003
    INT i, count;
1004
    const LB_ITEMDATA *item = descr->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
1005 1006 1007 1008 1009

    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
    for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
        if (item->selected) array[count++] = i;
    return count;
Alexandre Julliard's avatar
Alexandre Julliard committed
1010 1011
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1012

Alexandre Julliard's avatar
Alexandre Julliard committed
1013
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1014
 *           LISTBOX_Paint
Alexandre Julliard's avatar
Alexandre Julliard committed
1015
 */
1016
static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
Alexandre Julliard's avatar
Alexandre Julliard committed
1017
{
1018 1019
    INT i, col_pos = descr->page_size - 1;
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1020
    RECT focusRect = {-1, -1, -1, -1};
1021 1022
    HFONT oldFont = 0;
    HBRUSH hbrush, oldBrush = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1023

Alexandre Julliard's avatar
Alexandre Julliard committed
1024
    if (descr->style & LBS_NOREDRAW) return 0;
1025 1026

    SetRect( &rect, 0, 0, descr->width, descr->height );
Alexandre Julliard's avatar
Alexandre Julliard committed
1027 1028 1029 1030
    if (descr->style & LBS_MULTICOLUMN)
        rect.right = rect.left + descr->column_width;
    else if (descr->horz_pos)
    {
1031
        SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
1032
        rect.right += descr->horz_pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1033 1034
    }

1035
    if (descr->font) oldFont = SelectObject( hdc, descr->font );
1036
    hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1037
			   	   (WPARAM)hdc, (LPARAM)descr->self );
1038
    if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1039
    if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1040

Alexandre Julliard's avatar
Alexandre Julliard committed
1041
    if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1042
        (descr->in_focus))
Alexandre Julliard's avatar
Alexandre Julliard committed
1043 1044 1045
    {
        /* Special case for empty listbox: paint focus rect */
        rect.bottom = rect.top + descr->item_height;
1046 1047
        ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
                     &rect, NULL, 0, NULL );
1048
        LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1049 1050
        rect.top = rect.bottom;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1051

Alexandre Julliard's avatar
Alexandre Julliard committed
1052 1053 1054
    /* Paint all the item, regarding the selection
       Focus state will be painted after  */

Alexandre Julliard's avatar
Alexandre Julliard committed
1055 1056 1057 1058 1059 1060
    for (i = descr->top_item; i < descr->nb_items; i++)
    {
        if (!(descr->style & LBS_OWNERDRAWVARIABLE))
            rect.bottom = rect.top + descr->item_height;
        else
            rect.bottom = rect.top + descr->items[i].height;
Alexandre Julliard's avatar
Alexandre Julliard committed
1061

1062
        /* keep the focus rect, to paint the focus item after */
1063
        if (i == descr->focus_item)
1064 1065
            focusRect = rect;

1066
        LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1067
        rect.top = rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1068

Alexandre Julliard's avatar
Alexandre Julliard committed
1069 1070 1071 1072 1073 1074 1075 1076
        if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
        {
            if (!IS_OWNERDRAW(descr))
            {
                /* Clear the bottom of the column */
                if (rect.top < descr->height)
                {
                    rect.bottom = descr->height;
1077
                    ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
Alexandre Julliard's avatar
Alexandre Julliard committed
1078 1079 1080
                                   &rect, NULL, 0, NULL );
                }
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1081

Alexandre Julliard's avatar
Alexandre Julliard committed
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
            /* Go to the next column */
            rect.left += descr->column_width;
            rect.right += descr->column_width;
            rect.top = 0;
            col_pos = descr->page_size - 1;
        }
        else
        {
            col_pos--;
            if (rect.top >= descr->height) break;
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1094

Alexandre Julliard's avatar
Alexandre Julliard committed
1095
    /* Paint the focus item now */
1096 1097
    if (focusRect.top != focusRect.bottom &&
        descr->caret_on && descr->in_focus)
1098
        LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1099

Alexandre Julliard's avatar
Alexandre Julliard committed
1100 1101 1102 1103 1104 1105
    if (!IS_OWNERDRAW(descr))
    {
        /* Clear the remainder of the client area */
        if (rect.top < descr->height)
        {
            rect.bottom = descr->height;
1106
            ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
Alexandre Julliard's avatar
Alexandre Julliard committed
1107 1108 1109 1110 1111 1112 1113 1114
                           &rect, NULL, 0, NULL );
        }
        if (rect.right < descr->width)
        {
            rect.left   = rect.right;
            rect.right  = descr->width;
            rect.top    = 0;
            rect.bottom = descr->height;
1115
            ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
Alexandre Julliard's avatar
Alexandre Julliard committed
1116 1117 1118
                           &rect, NULL, 0, NULL );
        }
    }
1119 1120
    if (oldFont) SelectObject( hdc, oldFont );
    if (oldBrush) SelectObject( hdc, oldBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
1121
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1122 1123 1124
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1125
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1126 1127 1128 1129
 *           LISTBOX_InvalidateItems
 *
 * Invalidate all items from a given item. If the specified item is not
 * visible, nothing happens.
Alexandre Julliard's avatar
Alexandre Julliard committed
1130
 */
1131
static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index )
Alexandre Julliard's avatar
Alexandre Julliard committed
1132
{
1133
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1134

1135
    if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
Alexandre Julliard's avatar
Alexandre Julliard committed
1136
    {
1137 1138 1139 1140 1141
        if (descr->style & LBS_NOREDRAW)
        {
            descr->style |= LBS_DISPLAYCHANGED;
            return;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1142
        rect.bottom = descr->height;
1143
        InvalidateRect( descr->self, &rect, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1144 1145 1146 1147 1148 1149
        if (descr->style & LBS_MULTICOLUMN)
        {
            /* Repaint the other columns */
            rect.left  = rect.right;
            rect.right = descr->width;
            rect.top   = 0;
1150
            InvalidateRect( descr->self, &rect, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1151 1152
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1153 1154
}

1155
static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index )
1156 1157 1158 1159
{
    RECT rect;

    if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1160
        InvalidateRect( descr->self, &rect, TRUE );
1161
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1162

Alexandre Julliard's avatar
Alexandre Julliard committed
1163
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1164
 *           LISTBOX_GetItemHeight
Alexandre Julliard's avatar
Alexandre Julliard committed
1165
 */
1166
static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
Alexandre Julliard's avatar
Alexandre Julliard committed
1167
{
1168
    if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
1169
    {
1170 1171 1172 1173 1174
        if ((index < 0) || (index >= descr->nb_items))
        {
            SetLastError(ERROR_INVALID_INDEX);
            return LB_ERR;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1175 1176 1177
        return descr->items[index].height;
    }
    else return descr->item_height;
Alexandre Julliard's avatar
Alexandre Julliard committed
1178
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1179

Alexandre Julliard's avatar
Alexandre Julliard committed
1180

Alexandre Julliard's avatar
Alexandre Julliard committed
1181
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1182
 *           LISTBOX_SetItemHeight
Alexandre Julliard's avatar
Alexandre Julliard committed
1183
 */
1184
static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
Alexandre Julliard's avatar
Alexandre Julliard committed
1185
{
1186 1187 1188
    if (height > MAXBYTE)
        return -1;

Alexandre Julliard's avatar
Alexandre Julliard committed
1189
    if (!height) height = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1190

Alexandre Julliard's avatar
Alexandre Julliard committed
1191 1192
    if (descr->style & LBS_OWNERDRAWVARIABLE)
    {
1193 1194 1195 1196 1197
        if ((index < 0) || (index >= descr->nb_items))
        {
            SetLastError(ERROR_INVALID_INDEX);
            return LB_ERR;
        }
1198
        TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
Alexandre Julliard's avatar
Alexandre Julliard committed
1199
        descr->items[index].height = height;
1200
        LISTBOX_UpdateScroll( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1201
	if (repaint)
1202
	    LISTBOX_InvalidateItems( descr, index );
Alexandre Julliard's avatar
Alexandre Julliard committed
1203 1204 1205
    }
    else if (height != descr->item_height)
    {
1206
        TRACE("[%p]: new height = %d\n", descr->self, height );
Alexandre Julliard's avatar
Alexandre Julliard committed
1207
        descr->item_height = height;
1208 1209
        LISTBOX_UpdatePage( descr );
        LISTBOX_UpdateScroll( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1210
	if (repaint)
1211
	    InvalidateRect( descr->self, 0, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1212 1213
    }
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1214
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1215

Alexandre Julliard's avatar
Alexandre Julliard committed
1216

Alexandre Julliard's avatar
Alexandre Julliard committed
1217
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1218
 *           LISTBOX_SetHorizontalPos
Alexandre Julliard's avatar
Alexandre Julliard committed
1219
 */
1220
static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
1221
{
1222
    INT diff;
Alexandre Julliard's avatar
Alexandre Julliard committed
1223 1224 1225 1226 1227

    if (pos > descr->horz_extent - descr->width)
        pos = descr->horz_extent - descr->width;
    if (pos < 0) pos = 0;
    if (!(diff = descr->horz_pos - pos)) return;
1228
    TRACE("[%p]: new horz pos = %d\n", descr->self, pos );
Alexandre Julliard's avatar
Alexandre Julliard committed
1229
    descr->horz_pos = pos;
1230
    LISTBOX_UpdateScroll( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1231
    if (abs(diff) < descr->width)
1232 1233 1234 1235
    {
        RECT rect;
        /* Invalidate the focused item so it will be repainted correctly */
        if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
1236 1237
            InvalidateRect( descr->self, &rect, TRUE );
        ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL,
1238
                          SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1239
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1240
    else
1241
        InvalidateRect( descr->self, NULL, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1242 1243
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1244

Alexandre Julliard's avatar
Alexandre Julliard committed
1245
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1246
 *           LISTBOX_SetHorizontalExtent
Alexandre Julliard's avatar
Alexandre Julliard committed
1247
 */
1248
static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
Alexandre Julliard's avatar
Alexandre Julliard committed
1249
{
1250
    if (descr->style & LBS_MULTICOLUMN)
Alexandre Julliard's avatar
Alexandre Julliard committed
1251 1252
        return LB_OKAY;
    if (extent == descr->horz_extent) return LB_OKAY;
1253
    TRACE("[%p]: new horz extent = %d\n", descr->self, extent );
Alexandre Julliard's avatar
Alexandre Julliard committed
1254
    descr->horz_extent = extent;
1255 1256 1257 1258 1259 1260
    if (descr->style & WS_HSCROLL) {
        SCROLLINFO info;
        info.cbSize = sizeof(info);
        info.nMin  = 0;
        info.nMax = descr->horz_extent ? descr->horz_extent - 1 : 0;
        info.fMask = SIF_RANGE;
1261 1262
        if (descr->style & LBS_DISABLENOSCROLL)
            info.fMask |= SIF_DISABLENOSCROLL;
1263 1264
        SetScrollInfo( descr->self, SB_HORZ, &info, TRUE );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1265
    if (descr->horz_pos > extent - descr->width)
1266
        LISTBOX_SetHorizontalPos( descr, extent - descr->width );
Alexandre Julliard's avatar
Alexandre Julliard committed
1267
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1268
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1269

Alexandre Julliard's avatar
Alexandre Julliard committed
1270

Alexandre Julliard's avatar
Alexandre Julliard committed
1271
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1272
 *           LISTBOX_SetColumnWidth
Alexandre Julliard's avatar
Alexandre Julliard committed
1273
 */
1274
static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
Alexandre Julliard's avatar
Alexandre Julliard committed
1275
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1276
    if (width == descr->column_width) return LB_OKAY;
1277
    TRACE("[%p]: new column width = %d\n", descr->self, width );
Alexandre Julliard's avatar
Alexandre Julliard committed
1278
    descr->column_width = width;
1279
    LISTBOX_UpdatePage( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1280
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1281 1282
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1283

Alexandre Julliard's avatar
Alexandre Julliard committed
1284
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1285 1286 1287
 *           LISTBOX_SetFont
 *
 * Returns the item height.
Alexandre Julliard's avatar
Alexandre Julliard committed
1288
 */
1289
static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font )
Alexandre Julliard's avatar
Alexandre Julliard committed
1290
{
1291 1292
    HDC hdc;
    HFONT oldFont = 0;
1293 1294
    const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    SIZE sz;
Alexandre Julliard's avatar
Alexandre Julliard committed
1295 1296 1297

    descr->font = font;

1298
    if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1299
    {
1300
        ERR("unable to get DC.\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1301 1302
        return 16;
    }
1303
    if (font) oldFont = SelectObject( hdc, font );
1304
    GetTextExtentPointA( hdc, alphabet, 52, &sz);
1305
    if (oldFont) SelectObject( hdc, oldFont );
1306
    ReleaseDC( descr->self, hdc );
1307 1308

    descr->avg_char_width = (sz.cx / 26 + 1) / 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
1309
    if (!IS_OWNERDRAW(descr))
1310 1311
        LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE );
    return sz.cy;
Alexandre Julliard's avatar
Alexandre Julliard committed
1312
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1313

Alexandre Julliard's avatar
Alexandre Julliard committed
1314

Alexandre Julliard's avatar
Alexandre Julliard committed
1315
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1316 1317 1318
 *           LISTBOX_MakeItemVisible
 *
 * Make sure that a given item is partially or fully visible.
Alexandre Julliard's avatar
Alexandre Julliard committed
1319
 */
1320
static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
Alexandre Julliard's avatar
Alexandre Julliard committed
1321
{
1322
    INT top;
Alexandre Julliard's avatar
Alexandre Julliard committed
1323

1324 1325
    TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully);

Alexandre Julliard's avatar
Alexandre Julliard committed
1326 1327 1328
    if (index <= descr->top_item) top = index;
    else if (descr->style & LBS_MULTICOLUMN)
    {
1329
        INT cols = descr->width;
Alexandre Julliard's avatar
Alexandre Julliard committed
1330 1331 1332 1333 1334 1335 1336 1337
        if (!fully) cols += descr->column_width - 1;
        if (cols >= descr->column_width) cols /= descr->column_width;
        else cols = 1;
        if (index < descr->top_item + (descr->page_size * cols)) return;
        top = index - descr->page_size * (cols - 1);
    }
    else if (descr->style & LBS_OWNERDRAWVARIABLE)
    {
1338
        INT height = fully ? descr->items[index].height : 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1339 1340 1341 1342 1343 1344 1345 1346 1347 1348
        for (top = index; top > descr->top_item; top--)
            if ((height += descr->items[top-1].height) > descr->height) break;
    }
    else
    {
        if (index < descr->top_item + descr->page_size) return;
        if (!fully && (index == descr->top_item + descr->page_size) &&
            (descr->height > (descr->page_size * descr->item_height))) return;
        top = index - descr->page_size + 1;
    }
1349
    LISTBOX_SetTopItem( descr, top, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1350
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1351

1352 1353 1354 1355 1356 1357 1358
/***********************************************************************
 *           LISTBOX_SetCaretIndex
 *
 * NOTES
 *   index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
 *
 */
1359
static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
1360
{
1361
    INT oldfocus = descr->focus_item;
1362

1363 1364
    TRACE("old focus %d, index %d\n", oldfocus, index);

1365
    if (descr->style & LBS_NOSEL) return LB_ERR;
1366 1367
    if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
    if (index == oldfocus) return LB_OKAY;
1368 1369

    LISTBOX_DrawFocusRect( descr, FALSE );
1370 1371
    descr->focus_item = index;

1372
    LISTBOX_MakeItemVisible( descr, index, fully_visible );
1373
    LISTBOX_DrawFocusRect( descr, TRUE );
1374 1375 1376 1377

    return LB_OKAY;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1378

Alexandre Julliard's avatar
Alexandre Julliard committed
1379
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1380 1381 1382
 *           LISTBOX_SelectItemRange
 *
 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
Alexandre Julliard's avatar
Alexandre Julliard committed
1383
 */
1384
static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
1385
                                        INT last, BOOL on )
Alexandre Julliard's avatar
Alexandre Julliard committed
1386
{
1387
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1388 1389

    /* A few sanity checks */
Alexandre Julliard's avatar
Alexandre Julliard committed
1390

1391
    if (descr->style & LBS_NOSEL) return LB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
1392
    if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1393 1394 1395

    if (!descr->nb_items) return LB_OKAY;

1396
    if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1;
1397 1398
    if (first < 0) first = 0;
    if (last < first) return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1399 1400 1401 1402 1403 1404 1405

    if (on)  /* Turn selection on */
    {
        for (i = first; i <= last; i++)
        {
            if (descr->items[i].selected) continue;
            descr->items[i].selected = TRUE;
1406
            LISTBOX_InvalidateItemRect(descr, i);
Alexandre Julliard's avatar
Alexandre Julliard committed
1407 1408 1409 1410 1411 1412 1413 1414
        }
    }
    else  /* Turn selection off */
    {
        for (i = first; i <= last; i++)
        {
            if (!descr->items[i].selected) continue;
            descr->items[i].selected = FALSE;
1415
            LISTBOX_InvalidateItemRect(descr, i);
Alexandre Julliard's avatar
Alexandre Julliard committed
1416 1417 1418
        }
    }
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1419 1420
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1421
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1422
 *           LISTBOX_SetSelection
Alexandre Julliard's avatar
Alexandre Julliard committed
1423
 */
1424
static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
1425
                                     BOOL on, BOOL send_notify )
Alexandre Julliard's avatar
Alexandre Julliard committed
1426
{
1427 1428
    TRACE( "cur_sel=%d index=%d notify=%s\n",
           descr->selected_item, index, send_notify ? "YES" : "NO" );
1429

1430 1431 1432 1433 1434
    if (descr->style & LBS_NOSEL)
    {
        descr->selected_item = index;
        return LB_ERR;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1435 1436 1437 1438
    if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
    if (descr->style & LBS_MULTIPLESEL)
    {
        if (index == -1)  /* Select all items */
1439
            return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on );
Alexandre Julliard's avatar
Alexandre Julliard committed
1440
        else  /* Only one item */
1441
            return LISTBOX_SelectItemRange( descr, index, index, on );
Alexandre Julliard's avatar
Alexandre Julliard committed
1442 1443 1444
    }
    else
    {
1445
        INT oldsel = descr->selected_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
1446 1447 1448
        if (index == oldsel) return LB_OKAY;
        if (oldsel != -1) descr->items[oldsel].selected = FALSE;
        if (index != -1) descr->items[index].selected = TRUE;
1449
        if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
1450
        descr->selected_item = index;
1451 1452
        if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
        if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
Alexandre Julliard's avatar
Alexandre Julliard committed
1453
                               (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1454 1455 1456
	else
	    if( descr->lphc ) /* set selection change flag for parent combo */
		descr->lphc->wState |= CBF_SELCHANGE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1457 1458
    }
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1459
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1460

Alexandre Julliard's avatar
Alexandre Julliard committed
1461

Alexandre Julliard's avatar
Alexandre Julliard committed
1462
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1463 1464 1465
 *           LISTBOX_MoveCaret
 *
 * Change the caret position and extend the selection to the new caret.
Alexandre Julliard's avatar
Alexandre Julliard committed
1466
 */
1467
static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
Alexandre Julliard's avatar
Alexandre Julliard committed
1468
{
1469
    TRACE("old focus %d, index %d\n", descr->focus_item, index);
Alexandre Julliard's avatar
Alexandre Julliard committed
1470

1471
    if ((index <  0) || (index >= descr->nb_items))
Alexandre Julliard's avatar
Alexandre Julliard committed
1472 1473 1474 1475
        return;

    /* Important, repaint needs to be done in this order if
       you want to mimic Windows behavior:
1476
       1. Remove the focus and paint the item
Alexandre Julliard's avatar
Alexandre Julliard committed
1477 1478 1479 1480 1481
       2. Remove the selection and paint the item(s)
       3. Set the selection and repaint the item(s)
       4. Set the focus to 'index' and repaint the item */

    /* 1. remove the focus and repaint the item */
1482
    LISTBOX_DrawFocusRect( descr, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1483 1484 1485

    /* 2. then turn off the previous selection */
    /* 3. repaint the new selected item */
Alexandre Julliard's avatar
Alexandre Julliard committed
1486 1487 1488 1489
    if (descr->style & LBS_EXTENDEDSEL)
    {
        if (descr->anchor_item != -1)
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1490 1491
            INT first = min( index, descr->anchor_item );
            INT last  = max( index, descr->anchor_item );
Alexandre Julliard's avatar
Alexandre Julliard committed
1492
            if (first > 0)
1493 1494 1495
                LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE );
            LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE );
            LISTBOX_SelectItemRange( descr, first, last, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1496 1497
        }
    }
1498
    else if (!(descr->style & LBS_MULTIPLESEL))
Alexandre Julliard's avatar
Alexandre Julliard committed
1499 1500
    {
        /* Set selection to new caret item */
1501
        LISTBOX_SetSelection( descr, index, TRUE, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1502
    }
1503

Alexandre Julliard's avatar
Alexandre Julliard committed
1504 1505
    /* 4. repaint the new item with the focus */
    descr->focus_item = index;
1506
    LISTBOX_MakeItemVisible( descr, index, fully_visible );
1507
    LISTBOX_DrawFocusRect( descr, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1508
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1509

Alexandre Julliard's avatar
Alexandre Julliard committed
1510

Alexandre Julliard's avatar
Alexandre Julliard committed
1511
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1512
 *           LISTBOX_InsertItem
Alexandre Julliard's avatar
Alexandre Julliard committed
1513
 */
1514
static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
1515
                                   LPWSTR str, ULONG_PTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
1516
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1517
    LB_ITEMDATA *item;
1518
    INT max_items;
1519
    INT oldfocus = descr->focus_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
1520

Alexandre Julliard's avatar
Alexandre Julliard committed
1521 1522 1523
    if (index == -1) index = descr->nb_items;
    else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
    if (!descr->items) max_items = 0;
1524
    else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
Alexandre Julliard's avatar
Alexandre Julliard committed
1525 1526 1527 1528
    if (descr->nb_items == max_items)
    {
        /* We need to grow the array */
        max_items += LB_ARRAY_GRANULARITY;
1529 1530 1531 1532
	if (descr->items)
    	    item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
                                  max_items * sizeof(LB_ITEMDATA) );
	else
1533
	    item = HeapAlloc( GetProcessHeap(), 0,
1534 1535
                                  max_items * sizeof(LB_ITEMDATA) );
        if (!item)
Alexandre Julliard's avatar
Alexandre Julliard committed
1536
        {
1537
            SEND_NOTIFICATION( descr, LBN_ERRSPACE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1538 1539 1540 1541
            return LB_ERRSPACE;
        }
        descr->items = item;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1542

Alexandre Julliard's avatar
Alexandre Julliard committed
1543
    /* Insert the item structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
1544

Alexandre Julliard's avatar
Alexandre Julliard committed
1545 1546 1547 1548 1549 1550 1551 1552 1553
    item = &descr->items[index];
    if (index < descr->nb_items)
        RtlMoveMemory( item + 1, item,
                       (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
    item->str      = str;
    item->data     = data;
    item->height   = 0;
    item->selected = FALSE;
    descr->nb_items++;
Alexandre Julliard's avatar
Alexandre Julliard committed
1554

Alexandre Julliard's avatar
Alexandre Julliard committed
1555
    /* Get item height */
Alexandre Julliard's avatar
Alexandre Julliard committed
1556

Alexandre Julliard's avatar
Alexandre Julliard committed
1557 1558
    if (descr->style & LBS_OWNERDRAWVARIABLE)
    {
1559
        MEASUREITEMSTRUCT mis;
1560
        UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
1561

Alexandre Julliard's avatar
Alexandre Julliard committed
1562
        mis.CtlType    = ODT_LISTBOX;
1563
        mis.CtlID      = id;
Alexandre Julliard's avatar
Alexandre Julliard committed
1564 1565 1566
        mis.itemID     = index;
        mis.itemData   = descr->items[index].data;
        mis.itemHeight = descr->item_height;
1567
        SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
Alexandre Julliard's avatar
Alexandre Julliard committed
1568
        item->height = mis.itemHeight ? mis.itemHeight : 1;
1569
        TRACE("[%p]: measure item %d (%s) = %d\n",
1570
              descr->self, index, str ? debugstr_w(str) : "", item->height );
Alexandre Julliard's avatar
Alexandre Julliard committed
1571
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1572

Alexandre Julliard's avatar
Alexandre Julliard committed
1573
    /* Repaint the items */
Alexandre Julliard's avatar
Alexandre Julliard committed
1574

1575 1576
    LISTBOX_UpdateScroll( descr );
    LISTBOX_InvalidateItems( descr, index );
Alexandre Julliard's avatar
Alexandre Julliard committed
1577

Alexandre Julliard's avatar
Alexandre Julliard committed
1578
    /* Move selection and focused item */
1579 1580
    /* If listbox was empty, set focus to the first item */
    if (descr->nb_items == 1)
1581
         LISTBOX_SetCaretIndex( descr, 0, FALSE );
1582 1583
    /* single select don't change selection index in win31 */
    else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
Alexandre Julliard's avatar
Alexandre Julliard committed
1584
    {
1585
        descr->selected_item++;
1586
        LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE );
1587 1588 1589 1590 1591
    }
    else
    {
        if (index <= descr->selected_item)
        {
1592 1593
            descr->selected_item++;
            descr->focus_item = oldfocus; /* focus not changed */
1594
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1595 1596
    }
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1597 1598
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1599

Alexandre Julliard's avatar
Alexandre Julliard committed
1600
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1601
 *           LISTBOX_InsertString
Alexandre Julliard's avatar
Alexandre Julliard committed
1602
 */
1603
static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
1604
{
1605
    LPWSTR new_str = NULL;
1606
    ULONG_PTR data = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1607 1608 1609 1610
    LRESULT ret;

    if (HAS_STRINGS(descr))
    {
1611 1612 1613
        static const WCHAR empty_stringW[] = { 0 };
        if (!str) str = empty_stringW;
        if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1614
        {
1615
            SEND_NOTIFICATION( descr, LBN_ERRSPACE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1616 1617
            return LB_ERRSPACE;
        }
1618
        strcpyW(new_str, str);
Alexandre Julliard's avatar
Alexandre Julliard committed
1619
    }
1620
    else data = (ULONG_PTR)str;
Alexandre Julliard's avatar
Alexandre Julliard committed
1621 1622

    if (index == -1) index = descr->nb_items;
1623
    if ((ret = LISTBOX_InsertItem( descr, index, new_str, data )) != 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
1624
    {
1625
        HeapFree( GetProcessHeap(), 0, new_str );
Alexandre Julliard's avatar
Alexandre Julliard committed
1626 1627
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1628

1629
    TRACE("[%p]: added item %d %s\n",
1630
          descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1631
    return index;
Alexandre Julliard's avatar
Alexandre Julliard committed
1632 1633
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1634

Alexandre Julliard's avatar
Alexandre Julliard committed
1635
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1636 1637 1638
 *           LISTBOX_DeleteItem
 *
 * Delete the content of an item. 'index' must be a valid index.
Alexandre Julliard's avatar
Alexandre Julliard committed
1639
 */
1640
static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
Alexandre Julliard's avatar
Alexandre Julliard committed
1641
{
1642 1643 1644 1645 1646 1647 1648
    /* save the item data before it gets freed by LB_RESETCONTENT */
    ULONG_PTR item_data = descr->items[index].data;
    LPWSTR item_str = descr->items[index].str;

    if (!descr->nb_items)
        SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );

Alexandre Julliard's avatar
Alexandre Julliard committed
1649 1650 1651 1652 1653
    /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
     *       while Win95 sends it for all items with user data.
     *       It's probably better to send it too often than not
     *       often enough, so this is what we do here.
     */
1654
    if (IS_OWNERDRAW(descr) || item_data)
Alexandre Julliard's avatar
Alexandre Julliard committed
1655
    {
1656
        DELETEITEMSTRUCT dis;
1657
        UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
1658

Alexandre Julliard's avatar
Alexandre Julliard committed
1659
        dis.CtlType  = ODT_LISTBOX;
1660
        dis.CtlID    = id;
Alexandre Julliard's avatar
Alexandre Julliard committed
1661
        dis.itemID   = index;
1662
        dis.hwndItem = descr->self;
1663
        dis.itemData = item_data;
1664
        SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
Alexandre Julliard's avatar
Alexandre Julliard committed
1665
    }
1666
    if (HAS_STRINGS(descr))
1667
        HeapFree( GetProcessHeap(), 0, item_str );
Alexandre Julliard's avatar
Alexandre Julliard committed
1668
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1669

Alexandre Julliard's avatar
Alexandre Julliard committed
1670

Alexandre Julliard's avatar
Alexandre Julliard committed
1671
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1672 1673 1674
 *           LISTBOX_RemoveItem
 *
 * Remove an item from the listbox and delete its content.
Alexandre Julliard's avatar
Alexandre Julliard committed
1675
 */
1676
static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
Alexandre Julliard's avatar
Alexandre Julliard committed
1677
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1678
    LB_ITEMDATA *item;
1679
    INT max_items;
Alexandre Julliard's avatar
Alexandre Julliard committed
1680

1681
    if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1682

1683 1684 1685
    /* We need to invalidate the original rect instead of the updated one. */
    LISTBOX_InvalidateItems( descr, index );

1686 1687 1688 1689 1690
    descr->nb_items--;
    LISTBOX_DeleteItem( descr, index );

    if (!descr->nb_items) return LB_OKAY;

Alexandre Julliard's avatar
Alexandre Julliard committed
1691
    /* Remove the item */
Alexandre Julliard's avatar
Alexandre Julliard committed
1692

Alexandre Julliard's avatar
Alexandre Julliard committed
1693
    item = &descr->items[index];
1694
    if (index < descr->nb_items)
Alexandre Julliard's avatar
Alexandre Julliard committed
1695
        RtlMoveMemory( item, item + 1,
1696
                       (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1697
    if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
Alexandre Julliard's avatar
Alexandre Julliard committed
1698

Alexandre Julliard's avatar
Alexandre Julliard committed
1699
    /* Shrink the item array if possible */
Alexandre Julliard's avatar
Alexandre Julliard committed
1700

1701
    max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
Alexandre Julliard's avatar
Alexandre Julliard committed
1702
    if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
Alexandre Julliard's avatar
Alexandre Julliard committed
1703
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1704
        max_items -= LB_ARRAY_GRANULARITY;
1705
        item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
Alexandre Julliard's avatar
Alexandre Julliard committed
1706 1707 1708 1709 1710
                            max_items * sizeof(LB_ITEMDATA) );
        if (item) descr->items = item;
    }
    /* Repaint the items */

1711
    LISTBOX_UpdateScroll( descr );
1712 1713 1714
    /* if we removed the scrollbar, reset the top of the list
      (correct for owner-drawn ???) */
    if (descr->nb_items == descr->page_size)
1715
        LISTBOX_SetTopItem( descr, 0, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1716 1717

    /* Move selection and focused item */
1718
    if (!IS_MULTISELECT(descr))
Alexandre Julliard's avatar
Alexandre Julliard committed
1719
    {
1720 1721 1722
        if (index == descr->selected_item)
            descr->selected_item = -1;
        else if (index < descr->selected_item)
1723
        {
1724 1725
            descr->selected_item--;
            if (ISWIN31) /* win 31 do not change the selected item number */
1726 1727
               LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE);
        }
1728
    }
1729

1730 1731 1732 1733
    if (descr->focus_item >= descr->nb_items)
    {
          descr->focus_item = descr->nb_items - 1;
          if (descr->focus_item < 0) descr->focus_item = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1734
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1735
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1736
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1737

Alexandre Julliard's avatar
Alexandre Julliard committed
1738

Alexandre Julliard's avatar
Alexandre Julliard committed
1739
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1740
 *           LISTBOX_ResetContent
Alexandre Julliard's avatar
Alexandre Julliard committed
1741
 */
1742
static void LISTBOX_ResetContent( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
1743
{
1744
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1745

1746
    for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
1747
    HeapFree( GetProcessHeap(), 0, descr->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
1748 1749 1750 1751 1752 1753 1754
    descr->nb_items      = 0;
    descr->top_item      = 0;
    descr->selected_item = -1;
    descr->focus_item    = 0;
    descr->anchor_item   = -1;
    descr->items         = NULL;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1755 1756


Alexandre Julliard's avatar
Alexandre Julliard committed
1757 1758 1759
/***********************************************************************
 *           LISTBOX_SetCount
 */
1760
static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
Alexandre Julliard's avatar
Alexandre Julliard committed
1761 1762
{
    LRESULT ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1763

1764 1765 1766 1767 1768 1769
    if (HAS_STRINGS(descr))
    {
        SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
        return LB_ERR;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1770 1771 1772 1773
    /* FIXME: this is far from optimal... */
    if (count > descr->nb_items)
    {
        while (count > descr->nb_items)
1774
            if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
1775 1776 1777 1778 1779
                return ret;
    }
    else if (count < descr->nb_items)
    {
        while (count < descr->nb_items)
1780
            if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
1781 1782
                return ret;
    }
1783 1784

    InvalidateRect( descr->self, NULL, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1785
    return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1786 1787
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1788

Alexandre Julliard's avatar
Alexandre Julliard committed
1789
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1790
 *           LISTBOX_Directory
Alexandre Julliard's avatar
Alexandre Julliard committed
1791
 */
1792
static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
1793
                                  LPCWSTR filespec, BOOL long_names )
Alexandre Julliard's avatar
Alexandre Julliard committed
1794
{
1795
    HANDLE handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
1796
    LRESULT ret = LB_OKAY;
1797
    WIN32_FIND_DATAW entry;
Alexandre Julliard's avatar
Alexandre Julliard committed
1798
    int pos;
1799
    LRESULT maxinsert = LB_ERR;
Alexandre Julliard's avatar
Alexandre Julliard committed
1800

1801 1802 1803
    /* don't scan directory if we just want drives exclusively */
    if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
        /* scan directory */
1804
        if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
Alexandre Julliard's avatar
Alexandre Julliard committed
1805
        {
1806 1807
	     int le = GetLastError();
            if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1808 1809 1810 1811
        }
        else
        {
            do
Alexandre Julliard's avatar
Alexandre Julliard committed
1812
            {
1813
                WCHAR buffer[270];
1814 1815
                if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
1816 1817
                    static const WCHAR bracketW[]  = { ']',0 };
                    static const WCHAR dotW[] = { '.',0 };
1818
                    if (!(attrib & DDL_DIRECTORY) ||
1819
                        !strcmpW( entry.cFileName, dotW )) continue;
1820
                    buffer[0] = '[';
1821 1822 1823 1824
                    if (!long_names && entry.cAlternateFileName[0])
                        strcpyW( buffer + 1, entry.cAlternateFileName );
                    else
                        strcpyW( buffer + 1, entry.cFileName );
1825
                    strcatW(buffer, bracketW);
1826 1827 1828
                }
                else  /* not a directory */
                {
Alexandre Julliard's avatar
Alexandre Julliard committed
1829 1830 1831
#define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
                 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)

1832 1833 1834
                    if ((attrib & DDL_EXCLUSIVE) &&
                        ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
                        continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
1835
#undef ATTRIBS
1836
                    if (!long_names && entry.cAlternateFileName[0])
1837
                        strcpyW( buffer, entry.cAlternateFileName );
1838
                    else
1839
                        strcpyW( buffer, entry.cFileName );
1840
                }
1841
                if (!long_names) CharLowerW( buffer );
1842 1843
                pos = LISTBOX_FindFileStrPos( descr, buffer );
                if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0)
1844
                    break;
1845
                if (ret <= maxinsert) maxinsert++; else maxinsert = ret;
1846
            } while (FindNextFileW( handle, &entry ));
1847 1848
            FindClose( handle );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1849
    }
1850
    if (ret >= 0)
Alexandre Julliard's avatar
Alexandre Julliard committed
1851
    {
1852 1853 1854 1855
        ret = maxinsert;

        /* scan drives */
        if (attrib & DDL_DRIVES)
Alexandre Julliard's avatar
Alexandre Julliard committed
1856
        {
1857 1858 1859 1860 1861 1862 1863 1864 1865
            WCHAR buffer[] = {'[','-','a','-',']',0};
            WCHAR root[] = {'A',':','\\',0};
            int drive;
            for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
            {
                if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
                if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0)
                    break;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1866 1867 1868
        }
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1869
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1870

Alexandre Julliard's avatar
Alexandre Julliard committed
1871

Alexandre Julliard's avatar
Alexandre Julliard committed
1872
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1873
 *           LISTBOX_HandleVScroll
Alexandre Julliard's avatar
Alexandre Julliard committed
1874
 */
1875
static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
1876
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1877 1878 1879
    SCROLLINFO info;

    if (descr->style & LBS_MULTICOLUMN) return 0;
1880
    switch(scrollReq)
Alexandre Julliard's avatar
Alexandre Julliard committed
1881 1882
    {
    case SB_LINEUP:
1883
        LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1884 1885
        break;
    case SB_LINEDOWN:
1886
        LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1887 1888
        break;
    case SB_PAGEUP:
1889
        LISTBOX_SetTopItem( descr, descr->top_item -
1890
                            LISTBOX_GetCurrentPageSize( descr ), TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1891 1892
        break;
    case SB_PAGEDOWN:
1893
        LISTBOX_SetTopItem( descr, descr->top_item +
1894
                            LISTBOX_GetCurrentPageSize( descr ), TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1895 1896
        break;
    case SB_THUMBPOSITION:
1897
        LISTBOX_SetTopItem( descr, pos, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1898 1899 1900 1901
        break;
    case SB_THUMBTRACK:
        info.cbSize = sizeof(info);
        info.fMask = SIF_TRACKPOS;
1902 1903
        GetScrollInfo( descr->self, SB_VERT, &info );
        LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1904 1905
        break;
    case SB_TOP:
1906
        LISTBOX_SetTopItem( descr, 0, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1907 1908
        break;
    case SB_BOTTOM:
1909
        LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1910 1911 1912
        break;
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1913 1914
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1915

Alexandre Julliard's avatar
Alexandre Julliard committed
1916
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1917
 *           LISTBOX_HandleHScroll
Alexandre Julliard's avatar
Alexandre Julliard committed
1918
 */
1919
static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
1920
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1921
    SCROLLINFO info;
1922
    INT page;
Alexandre Julliard's avatar
Alexandre Julliard committed
1923 1924 1925

    if (descr->style & LBS_MULTICOLUMN)
    {
1926
        switch(scrollReq)
Alexandre Julliard's avatar
Alexandre Julliard committed
1927 1928
        {
        case SB_LINELEFT:
1929
            LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size,
Alexandre Julliard's avatar
Alexandre Julliard committed
1930 1931 1932
                                TRUE );
            break;
        case SB_LINERIGHT:
1933
            LISTBOX_SetTopItem( descr, descr->top_item+descr->page_size,
Alexandre Julliard's avatar
Alexandre Julliard committed
1934 1935 1936 1937 1938
                                TRUE );
            break;
        case SB_PAGELEFT:
            page = descr->width / descr->column_width;
            if (page < 1) page = 1;
1939
            LISTBOX_SetTopItem( descr,
Alexandre Julliard's avatar
Alexandre Julliard committed
1940 1941 1942 1943 1944
                             descr->top_item - page * descr->page_size, TRUE );
            break;
        case SB_PAGERIGHT:
            page = descr->width / descr->column_width;
            if (page < 1) page = 1;
1945
            LISTBOX_SetTopItem( descr,
Alexandre Julliard's avatar
Alexandre Julliard committed
1946 1947 1948
                             descr->top_item + page * descr->page_size, TRUE );
            break;
        case SB_THUMBPOSITION:
1949
            LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1950 1951 1952 1953
            break;
        case SB_THUMBTRACK:
            info.cbSize = sizeof(info);
            info.fMask  = SIF_TRACKPOS;
1954 1955
            GetScrollInfo( descr->self, SB_VERT, &info );
            LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size,
Alexandre Julliard's avatar
Alexandre Julliard committed
1956 1957 1958
                                TRUE );
            break;
        case SB_LEFT:
1959
            LISTBOX_SetTopItem( descr, 0, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1960 1961
            break;
        case SB_RIGHT:
1962
            LISTBOX_SetTopItem( descr, descr->nb_items, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1963 1964 1965 1966 1967
            break;
        }
    }
    else if (descr->horz_extent)
    {
1968
        switch(scrollReq)
Alexandre Julliard's avatar
Alexandre Julliard committed
1969 1970
        {
        case SB_LINELEFT:
1971
            LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1972 1973
            break;
        case SB_LINERIGHT:
1974
            LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1975 1976
            break;
        case SB_PAGELEFT:
1977
            LISTBOX_SetHorizontalPos( descr,
Alexandre Julliard's avatar
Alexandre Julliard committed
1978 1979 1980
                                      descr->horz_pos - descr->width );
            break;
        case SB_PAGERIGHT:
1981
            LISTBOX_SetHorizontalPos( descr,
Alexandre Julliard's avatar
Alexandre Julliard committed
1982 1983 1984
                                      descr->horz_pos + descr->width );
            break;
        case SB_THUMBPOSITION:
1985
            LISTBOX_SetHorizontalPos( descr, pos );
Alexandre Julliard's avatar
Alexandre Julliard committed
1986 1987 1988 1989
            break;
        case SB_THUMBTRACK:
            info.cbSize = sizeof(info);
            info.fMask = SIF_TRACKPOS;
1990 1991
            GetScrollInfo( descr->self, SB_HORZ, &info );
            LISTBOX_SetHorizontalPos( descr, info.nTrackPos );
Alexandre Julliard's avatar
Alexandre Julliard committed
1992 1993
            break;
        case SB_LEFT:
1994
            LISTBOX_SetHorizontalPos( descr, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1995 1996
            break;
        case SB_RIGHT:
1997
            LISTBOX_SetHorizontalPos( descr,
Alexandre Julliard's avatar
Alexandre Julliard committed
1998 1999 2000 2001 2002
                                      descr->horz_extent - descr->width );
            break;
        }
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2003
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2004

2005
static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
2006 2007 2008 2009 2010
{
    UINT pulScrollLines = 3;

    SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);

2011 2012 2013 2014 2015 2016
    /* if scrolling changes direction, ignore left overs */
    if ((delta < 0 && descr->wheel_remain < 0) ||
        (delta > 0 && descr->wheel_remain > 0))
        descr->wheel_remain += delta;
    else
        descr->wheel_remain = delta;
2017

2018
    if (descr->wheel_remain && pulScrollLines)
2019
    {
2020 2021 2022 2023 2024
        int cLineScroll;
        pulScrollLines = min((UINT) descr->page_size, pulScrollLines);
        cLineScroll = pulScrollLines * (float)descr->wheel_remain / WHEEL_DELTA;
        descr->wheel_remain -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
        LISTBOX_SetTopItem( descr, descr->top_item - cLineScroll, TRUE );
2025 2026 2027
    }
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2028

Alexandre Julliard's avatar
Alexandre Julliard committed
2029
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2030
 *           LISTBOX_HandleLButtonDown
Alexandre Julliard's avatar
Alexandre Julliard committed
2031
 */
2032
static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
2033
{
2034
    INT index = LISTBOX_GetItemFromPoint( descr, x, y );
2035 2036 2037 2038

    TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n",
          descr->self, x, y, index, descr->focus_item);

Alexandre Julliard's avatar
Alexandre Julliard committed
2039
    if (!descr->caret_on && (descr->in_focus)) return 0;
2040 2041 2042

    if (!descr->in_focus)
    {
2043
        if( !descr->lphc ) SetFocus( descr->self );
2044
        else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
2045 2046
    }

2047 2048
    if (index == -1) return 0;

2049 2050 2051 2052 2053 2054 2055 2056 2057 2058
    if (!descr->lphc)
    {
        if (descr->style & LBS_NOTIFY )
            SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
                            MAKELPARAM( x, y ) );
    }

    descr->captured = TRUE;
    SetCapture( descr->self );

2059
    if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))
Alexandre Julliard's avatar
Alexandre Julliard committed
2060
    {
2061 2062
        /* we should perhaps make sure that all items are deselected
           FIXME: needed for !LBS_EXTENDEDSEL, too ?
2063 2064
           if (!(keys & (MK_SHIFT|MK_CONTROL)))
           LISTBOX_SetSelection( descr, -1, FALSE, FALSE);
2065 2066
        */

2067 2068
        if (!(keys & MK_SHIFT)) descr->anchor_item = index;
        if (keys & MK_CONTROL)
Alexandre Julliard's avatar
Alexandre Julliard committed
2069
        {
2070 2071
            LISTBOX_SetCaretIndex( descr, index, FALSE );
            LISTBOX_SetSelection( descr, index,
2072 2073
                                  !descr->items[index].selected,
                                  (descr->style & LBS_NOTIFY) != 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2074
        }
2075 2076
        else
        {
2077
            LISTBOX_MoveCaret( descr, index, FALSE );
2078

2079
            if (descr->style & LBS_EXTENDEDSEL)
2080
            {
2081
                LISTBOX_SetSelection( descr, index,
2082 2083 2084 2085 2086 2087 2088
                               descr->items[index].selected,
                              (descr->style & LBS_NOTIFY) != 0 );
            }
            else
            {
                LISTBOX_SetSelection( descr, index,
                               !descr->items[index].selected,
2089 2090 2091
                              (descr->style & LBS_NOTIFY) != 0 );
            }
        }
2092 2093 2094
    }
    else
    {
2095
        descr->anchor_item = index;
2096 2097
        LISTBOX_MoveCaret( descr, index, FALSE );
        LISTBOX_SetSelection( descr, index,
2098
                              TRUE, (descr->style & LBS_NOTIFY) != 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2099
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2100

2101
    if (!descr->lphc)
Alexandre Julliard's avatar
Alexandre Julliard committed
2102
    {
2103
        if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
Alexandre Julliard's avatar
Alexandre Julliard committed
2104
        {
2105
            POINT pt;
2106

2107 2108 2109
	    pt.x = x;
	    pt.y = y;

2110
            if (DragDetect( descr->self, pt ))
2111
                SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2112 2113 2114
        }
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2115
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2116

Alexandre Julliard's avatar
Alexandre Julliard committed
2117

2118
/*************************************************************************
2119
 * LISTBOX_HandleLButtonDownCombo [Internal]
2120 2121 2122
 *
 * Process LButtonDown message for the ComboListBox
 *
2123
 * PARAMS
2124 2125
 *     pWnd       [I] The windows internal structure
 *     pDescr     [I] The ListBox internal structure
2126
 *     keys       [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2127 2128 2129 2130 2131 2132 2133 2134 2135 2136
 *     x          [I] X Mouse Coordinate
 *     y          [I] Y Mouse Coordinate
 *
 * RETURNS
 *     0 since we are processing the WM_LBUTTONDOWN Message
 *
 * NOTES
 *  This function is only to be used when a ListBox is a ComboListBox
 */

2137
static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y)
2138 2139 2140 2141 2142 2143 2144
{
    RECT clientRect, screenRect;
    POINT mousePos;

    mousePos.x = x;
    mousePos.y = y;

2145
    GetClientRect(descr->self, &clientRect);
2146 2147

    if(PtInRect(&clientRect, mousePos))
2148
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2149 2150
       /* MousePos is in client, resume normal processing */
        if (msg == WM_LBUTTONDOWN)
2151
        {
2152 2153
           descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1;
           return LISTBOX_HandleLButtonDown( descr, keys, x, y);
2154
        }
2155 2156
        else if (descr->style & LBS_NOTIFY)
            SEND_NOTIFICATION( descr, LBN_DBLCLK );
2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
    }
    else
    {
        POINT screenMousePos;
        HWND hWndOldCapture;

        /* Check the Non-Client Area */
        screenMousePos = mousePos;
        hWndOldCapture = GetCapture();
        ReleaseCapture();
2167 2168
        GetWindowRect(descr->self, &screenRect);
        ClientToScreen(descr->self, &screenMousePos);
2169 2170

        if(!PtInRect(&screenRect, screenMousePos))
2171
        {
2172 2173 2174
            LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE );
            LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE );
            COMBO_FlipListbox( descr->lphc, FALSE, FALSE );
2175 2176 2177 2178 2179
        }
        else
        {
            /* Check to see the NC is a scrollbar */
            INT nHitTestType=0;
2180
            LONG style = GetWindowLongW( descr->self, GWL_STYLE );
2181
            /* Check Vertical scroll bar */
2182
            if (style & WS_VSCROLL)
2183 2184
            {
                clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2185
                if (PtInRect( &clientRect, mousePos ))
2186 2187 2188
                    nHitTestType = HTVSCROLL;
            }
              /* Check horizontal scroll bar */
2189
            if (style & WS_HSCROLL)
2190 2191 2192 2193 2194
            {
                clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
                if (PtInRect( &clientRect, mousePos ))
                    nHitTestType = HTHSCROLL;
            }
2195
            /* Windows sends this message when a scrollbar is clicked
2196
             */
2197

2198 2199
            if(nHitTestType != 0)
            {
2200 2201
                SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType,
                             MAKELONG(screenMousePos.x, screenMousePos.y));
2202
            }
2203
            /* Resume the Capture after scrolling is complete
2204 2205 2206 2207 2208 2209 2210 2211
             */
            if(hWndOldCapture != 0)
                SetCapture(hWndOldCapture);
        }
    }
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2212 2213 2214
/***********************************************************************
 *           LISTBOX_HandleLButtonUp
 */
2215
static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
2216 2217
{
    if (LISTBOX_Timer != LB_TIMER_NONE)
2218
        KillSystemTimer( descr->self, LB_TIMER_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
2219
    LISTBOX_Timer = LB_TIMER_NONE;
2220
    if (descr->captured)
Alexandre Julliard's avatar
Alexandre Julliard committed
2221
    {
2222
        descr->captured = FALSE;
2223
        if (GetCapture() == descr->self) ReleaseCapture();
2224
        if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2225
            SEND_NOTIFICATION( descr, LBN_SELCHANGE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2226
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2227 2228 2229 2230
    return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2231
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2232 2233 2234 2235
 *           LISTBOX_HandleTimer
 *
 * Handle scrolling upon a timer event.
 * Return TRUE if scrolling should continue.
Alexandre Julliard's avatar
Alexandre Julliard committed
2236
 */
2237
static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT index, TIMER_DIRECTION dir )
Alexandre Julliard's avatar
Alexandre Julliard committed
2238
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2239 2240 2241 2242 2243 2244 2245 2246 2247 2248
    switch(dir)
    {
    case LB_TIMER_UP:
        if (descr->top_item) index = descr->top_item - 1;
        else index = 0;
        break;
    case LB_TIMER_LEFT:
        if (descr->top_item) index -= descr->page_size;
        break;
    case LB_TIMER_DOWN:
2249
        index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
2250
        if (index == descr->focus_item) index++;
Alexandre Julliard's avatar
Alexandre Julliard committed
2251 2252 2253 2254 2255 2256 2257 2258 2259 2260
        if (index >= descr->nb_items) index = descr->nb_items - 1;
        break;
    case LB_TIMER_RIGHT:
        if (index + descr->page_size < descr->nb_items)
            index += descr->page_size;
        break;
    case LB_TIMER_NONE:
        break;
    }
    if (index == descr->focus_item) return FALSE;
2261
    LISTBOX_MoveCaret( descr, index, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2262
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2263
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2264

Alexandre Julliard's avatar
Alexandre Julliard committed
2265

Alexandre Julliard's avatar
Alexandre Julliard committed
2266
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2267 2268 2269
 *           LISTBOX_HandleSystemTimer
 *
 * WM_SYSTIMER handler.
Alexandre Julliard's avatar
Alexandre Julliard committed
2270
 */
2271
static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
2272
{
2273
    if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer ))
Alexandre Julliard's avatar
Alexandre Julliard committed
2274
    {
2275
        KillSystemTimer( descr->self, LB_TIMER_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
2276 2277 2278
        LISTBOX_Timer = LB_TIMER_NONE;
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2279
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2280

Alexandre Julliard's avatar
Alexandre Julliard committed
2281

Alexandre Julliard's avatar
Alexandre Julliard committed
2282
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2283 2284 2285
 *           LISTBOX_HandleMouseMove
 *
 * WM_MOUSEMOVE handler.
Alexandre Julliard's avatar
Alexandre Julliard committed
2286
 */
2287
static void LISTBOX_HandleMouseMove( LB_DESCR *descr,
2288
                                     INT x, INT y )
Alexandre Julliard's avatar
Alexandre Julliard committed
2289
{
2290
    INT index;
2291
    TIMER_DIRECTION dir = LB_TIMER_NONE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2292

2293 2294
    if (!descr->captured) return;

Alexandre Julliard's avatar
Alexandre Julliard committed
2295 2296 2297 2298 2299
    if (descr->style & LBS_MULTICOLUMN)
    {
        if (y < 0) y = 0;
        else if (y >= descr->item_height * descr->page_size)
            y = descr->item_height * descr->page_size - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2300

Alexandre Julliard's avatar
Alexandre Julliard committed
2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316
        if (x < 0)
        {
            dir = LB_TIMER_LEFT;
            x = 0;
        }
        else if (x >= descr->width)
        {
            dir = LB_TIMER_RIGHT;
            x = descr->width - 1;
        }
    }
    else
    {
        if (y < 0) dir = LB_TIMER_UP;  /* above */
        else if (y >= descr->height) dir = LB_TIMER_DOWN;  /* below */
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2317

2318
    index = LISTBOX_GetItemFromPoint( descr, x, y );
Alexandre Julliard's avatar
Alexandre Julliard committed
2319
    if (index == -1) index = descr->focus_item;
2320
    if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2321

Alexandre Julliard's avatar
Alexandre Julliard committed
2322
    /* Start/stop the system timer */
Alexandre Julliard's avatar
Alexandre Julliard committed
2323

Alexandre Julliard's avatar
Alexandre Julliard committed
2324
    if (dir != LB_TIMER_NONE)
2325
        SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
2326
    else if (LISTBOX_Timer != LB_TIMER_NONE)
2327
        KillSystemTimer( descr->self, LB_TIMER_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
2328
    LISTBOX_Timer = dir;
Alexandre Julliard's avatar
Alexandre Julliard committed
2329 2330
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2331

Alexandre Julliard's avatar
Alexandre Julliard committed
2332
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2333
 *           LISTBOX_HandleKeyDown
Alexandre Julliard's avatar
Alexandre Julliard committed
2334
 */
2335
static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
Alexandre Julliard's avatar
Alexandre Julliard committed
2336
{
2337
    INT caret = -1;
2338 2339
    BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
    if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2340
        bForceSelection = FALSE; /* only for single select list */
2341

Alexandre Julliard's avatar
Alexandre Julliard committed
2342 2343
    if (descr->style & LBS_WANTKEYBOARDINPUT)
    {
2344
        caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2345 2346
                                MAKEWPARAM(LOWORD(key), descr->focus_item),
                                (LPARAM)descr->self );
Alexandre Julliard's avatar
Alexandre Julliard committed
2347 2348
        if (caret == -2) return 0;
    }
2349
    if (caret == -1) switch(key)
Alexandre Julliard's avatar
Alexandre Julliard committed
2350 2351 2352 2353
    {
    case VK_LEFT:
        if (descr->style & LBS_MULTICOLUMN)
        {
2354
            bForceSelection = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366
            if (descr->focus_item >= descr->page_size)
                caret = descr->focus_item - descr->page_size;
            break;
        }
        /* fall through */
    case VK_UP:
        caret = descr->focus_item - 1;
        if (caret < 0) caret = 0;
        break;
    case VK_RIGHT:
        if (descr->style & LBS_MULTICOLUMN)
        {
2367
            bForceSelection = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2368 2369 2370 2371 2372 2373 2374 2375 2376
            if (descr->focus_item + descr->page_size < descr->nb_items)
                caret = descr->focus_item + descr->page_size;
            break;
        }
        /* fall through */
    case VK_DOWN:
        caret = descr->focus_item + 1;
        if (caret >= descr->nb_items) caret = descr->nb_items - 1;
        break;
2377

Alexandre Julliard's avatar
Alexandre Julliard committed
2378 2379 2380
    case VK_PRIOR:
        if (descr->style & LBS_MULTICOLUMN)
        {
2381
            INT page = descr->width / descr->column_width;
Alexandre Julliard's avatar
Alexandre Julliard committed
2382 2383 2384
            if (page < 1) page = 1;
            caret = descr->focus_item - (page * descr->page_size) + 1;
        }
2385
        else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2386 2387 2388 2389 2390
        if (caret < 0) caret = 0;
        break;
    case VK_NEXT:
        if (descr->style & LBS_MULTICOLUMN)
        {
2391
            INT page = descr->width / descr->column_width;
Alexandre Julliard's avatar
Alexandre Julliard committed
2392 2393 2394
            if (page < 1) page = 1;
            caret = descr->focus_item + (page * descr->page_size) - 1;
        }
2395
        else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2396 2397 2398 2399 2400 2401 2402 2403 2404
        if (caret >= descr->nb_items) caret = descr->nb_items - 1;
        break;
    case VK_HOME:
        caret = 0;
        break;
    case VK_END:
        caret = descr->nb_items - 1;
        break;
    case VK_SPACE:
Alexandre Julliard's avatar
Alexandre Julliard committed
2405
        if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2406
        else if (descr->style & LBS_MULTIPLESEL)
Alexandre Julliard's avatar
Alexandre Julliard committed
2407
        {
2408
            LISTBOX_SetSelection( descr, descr->focus_item,
Alexandre Julliard's avatar
Alexandre Julliard committed
2409 2410
                                  !descr->items[descr->focus_item].selected,
                                  (descr->style & LBS_NOTIFY) != 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2411
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2412
        break;
2413 2414
    default:
        bForceSelection = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2415
    }
2416 2417
    if (bForceSelection) /* focused item is used instead of key */
        caret = descr->focus_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2418 2419
    if (caret >= 0)
    {
2420 2421 2422
        if (((descr->style & LBS_EXTENDEDSEL) &&
            !(GetKeyState( VK_SHIFT ) & 0x8000)) ||
            !IS_MULTISELECT(descr))
Alexandre Julliard's avatar
Alexandre Julliard committed
2423
            descr->anchor_item = caret;
2424
        LISTBOX_MoveCaret( descr, caret, TRUE );
2425 2426 2427 2428

        if (descr->style & LBS_MULTIPLESEL)
            descr->selected_item = caret;
        else
2429
            LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2430
        if (descr->style & LBS_NOTIFY)
Alexandre Julliard's avatar
Alexandre Julliard committed
2431
        {
2432
            if (descr->lphc && IsWindowVisible( descr->self ))
Alexandre Julliard's avatar
Alexandre Julliard committed
2433
            {
2434 2435 2436
                /* make sure that combo parent doesn't hide us */
                descr->lphc->wState |= CBF_NOROLLUP;
            }
2437
            if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2438
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2439 2440
    }
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2441
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2442

Alexandre Julliard's avatar
Alexandre Julliard committed
2443

Alexandre Julliard's avatar
Alexandre Julliard committed
2444
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2445
 *           LISTBOX_HandleChar
Alexandre Julliard's avatar
Alexandre Julliard committed
2446
 */
2447
static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW )
Alexandre Julliard's avatar
Alexandre Julliard committed
2448
{
2449
    INT caret = -1;
2450 2451 2452
    WCHAR str[2];

    str[0] = charW;
2453
    str[1] = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
2454

Alexandre Julliard's avatar
Alexandre Julliard committed
2455 2456
    if (descr->style & LBS_WANTKEYBOARDINPUT)
    {
2457 2458
        caret = SendMessageW( descr->owner, WM_CHARTOITEM,
                                MAKEWPARAM(charW, descr->focus_item),
2459
                                (LPARAM)descr->self );
Alexandre Julliard's avatar
Alexandre Julliard committed
2460 2461 2462
        if (caret == -2) return 0;
    }
    if (caret == -1)
2463
        caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2464 2465
    if (caret != -1)
    {
2466
        if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2467 2468
           LISTBOX_SetSelection( descr, caret, TRUE, FALSE);
        LISTBOX_MoveCaret( descr, caret, TRUE );
2469
        if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2470
            SEND_NOTIFICATION( descr, LBN_SELCHANGE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2471
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2472
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2473
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2474

Alexandre Julliard's avatar
Alexandre Julliard committed
2475

Alexandre Julliard's avatar
Alexandre Julliard committed
2476
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2477
 *           LISTBOX_Create
Alexandre Julliard's avatar
Alexandre Julliard committed
2478
 */
2479
static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
Alexandre Julliard's avatar
Alexandre Julliard committed
2480
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2481
    LB_DESCR *descr;
2482 2483
    MEASUREITEMSTRUCT mis;
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
2484 2485 2486

    if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
        return FALSE;
2487

2488
    GetClientRect( hwnd, &rect );
2489 2490 2491
    descr->self          = hwnd;
    descr->owner         = GetParent( descr->self );
    descr->style         = GetWindowLongW( descr->self, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502
    descr->width         = rect.right - rect.left;
    descr->height        = rect.bottom - rect.top;
    descr->items         = NULL;
    descr->nb_items      = 0;
    descr->top_item      = 0;
    descr->selected_item = -1;
    descr->focus_item    = 0;
    descr->anchor_item   = -1;
    descr->item_height   = 1;
    descr->page_size     = 1;
    descr->column_width  = 150;
2503
    descr->horz_extent   = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2504 2505 2506
    descr->horz_pos      = 0;
    descr->nb_tabs       = 0;
    descr->tabs          = NULL;
2507
    descr->wheel_remain  = 0;
2508
    descr->caret_on      = !lphc;
2509
    if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2510
    descr->in_focus 	 = FALSE;
2511
    descr->captured      = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2512
    descr->font          = 0;
2513
    descr->locale        = GetUserDefaultLCID();
Alexandre Julliard's avatar
Alexandre Julliard committed
2514 2515 2516 2517
    descr->lphc		 = lphc;

    if( lphc )
    {
2518
        TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self );
2519
        descr->owner = lphc->self;
Alexandre Julliard's avatar
Alexandre Julliard committed
2520
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2521

2522
    SetWindowLongPtrW( descr->self, 0, (LONG_PTR)descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
2523

Alexandre Julliard's avatar
Alexandre Julliard committed
2524 2525
/*    if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
2526 2527 2528
    if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
    if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
    if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2529
    descr->item_height = LISTBOX_SetFont( descr, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2530 2531 2532

    if (descr->style & LBS_OWNERDRAWFIXED)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2533 2534 2535
	if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
	{
	    /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2536
	  descr->item_height = lphc->fixedOwnerDrawHeight;
Alexandre Julliard's avatar
Alexandre Julliard committed
2537 2538 2539
	}
	else
	{
2540
            UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
Alexandre Julliard's avatar
Alexandre Julliard committed
2541
            mis.CtlType    = ODT_LISTBOX;
2542
            mis.CtlID      = id;
Alexandre Julliard's avatar
Alexandre Julliard committed
2543 2544 2545 2546
            mis.itemID     = -1;
            mis.itemWidth  =  0;
            mis.itemData   =  0;
            mis.itemHeight = descr->item_height;
2547
            SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
Alexandre Julliard's avatar
Alexandre Julliard committed
2548 2549
            descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2550
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2551

2552
    TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
Alexandre Julliard's avatar
Alexandre Julliard committed
2553
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2554 2555
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2556

Alexandre Julliard's avatar
Alexandre Julliard committed
2557
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2558
 *           LISTBOX_Destroy
Alexandre Julliard's avatar
Alexandre Julliard committed
2559
 */
2560
static BOOL LISTBOX_Destroy( LB_DESCR *descr )
Alexandre Julliard's avatar
Alexandre Julliard committed
2561
{
2562
    LISTBOX_ResetContent( descr );
2563
    SetWindowLongPtrW( descr->self, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2564 2565
    HeapFree( GetProcessHeap(), 0, descr );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2566 2567
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2568

Alexandre Julliard's avatar
Alexandre Julliard committed
2569
/***********************************************************************
2570
 *           ListBoxWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
2571
 */
2572
LRESULT ListBoxWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
2573
{
2574
    LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 );
2575
    LPHEADCOMBO lphc = 0;
2576
    LRESULT ret;
2577

2578
    if (!descr)
Alexandre Julliard's avatar
Alexandre Julliard committed
2579
    {
2580 2581
        if (!IsWindow(hwnd)) return 0;

2582 2583
        if (msg == WM_CREATE)
        {
2584
	    CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam;
2585
            if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams;
2586
            if (!LISTBOX_Create( hwnd, lphc )) return -1;
2587
            TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) );
2588 2589
            return 0;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2590
        /* Ignore all other messages before we get a WM_CREATE */
2591 2592
        return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
                         DefWindowProcA( hwnd, msg, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2593
    }
2594
    if (descr->style & LBS_COMBOBOX) lphc = descr->lphc;
Alexandre Julliard's avatar
Alexandre Julliard committed
2595

2596
    TRACE("[%p]: msg %s wp %08lx lp %08lx\n",
2597
          descr->self, SPY_GetMsgName(msg, descr->self), wParam, lParam );
2598

Alexandre Julliard's avatar
Alexandre Julliard committed
2599
    switch(msg)
Alexandre Julliard's avatar
Alexandre Julliard committed
2600
    {
2601
    case LB_RESETCONTENT:
2602 2603 2604
        LISTBOX_ResetContent( descr );
        LISTBOX_UpdateScroll( descr );
        InvalidateRect( descr->self, NULL, TRUE );
2605
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2606

2607
    case LB_ADDSTRING:
2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618
    {
        INT ret;
        LPWSTR textW;
        if(unicode || !HAS_STRINGS(descr))
            textW = (LPWSTR)lParam;
        else
        {
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2619 2620
            else
                return LB_ERRSPACE;
2621
        }
2622 2623
        wParam = LISTBOX_FindStringPos( descr, textW, FALSE );
        ret = LISTBOX_InsertString( descr, wParam, textW );
2624 2625 2626 2627
        if (!unicode && HAS_STRINGS(descr))
            HeapFree(GetProcessHeap(), 0, textW);
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2628

2629
    case LB_INSERTSTRING:
2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640
    {
        INT ret;
        LPWSTR textW;
        if(unicode || !HAS_STRINGS(descr))
            textW = (LPWSTR)lParam;
        else
        {
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2641 2642
            else
                return LB_ERRSPACE;
2643
        }
2644
        ret = LISTBOX_InsertString( descr, wParam, textW );
2645 2646 2647 2648
        if(!unicode && HAS_STRINGS(descr))
            HeapFree(GetProcessHeap(), 0, textW);
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2649

2650
    case LB_ADDFILE:
2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661
    {
        INT ret;
        LPWSTR textW;
        if(unicode || !HAS_STRINGS(descr))
            textW = (LPWSTR)lParam;
        else
        {
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2662 2663
            else
                return LB_ERRSPACE;
2664
        }
2665 2666
        wParam = LISTBOX_FindFileStrPos( descr, textW );
        ret = LISTBOX_InsertString( descr, wParam, textW );
2667 2668 2669 2670
        if(!unicode && HAS_STRINGS(descr))
            HeapFree(GetProcessHeap(), 0, textW);
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2671

2672
    case LB_DELETESTRING:
2673
        if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR)
2674
            return descr->nb_items;
2675
        else
2676 2677 2678 2679
        {
            SetLastError(ERROR_INVALID_INDEX);
            return LB_ERR;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2680

2681 2682
    case LB_GETITEMDATA:
        if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2683 2684
        {
            SetLastError(ERROR_INVALID_INDEX);
2685
            return LB_ERR;
2686
        }
2687
        return descr->items[wParam].data;
Alexandre Julliard's avatar
Alexandre Julliard committed
2688

2689 2690
    case LB_SETITEMDATA:
        if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2691 2692
        {
            SetLastError(ERROR_INVALID_INDEX);
2693
            return LB_ERR;
2694
        }
2695
        descr->items[wParam].data = lParam;
2696 2697
        /* undocumented: returns TRUE, not LB_OKAY (0) */
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2698

2699
    case LB_GETCOUNT:
2700
        return descr->nb_items;
Alexandre Julliard's avatar
Alexandre Julliard committed
2701

2702
    case LB_GETTEXT:
2703
        return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, unicode );
Alexandre Julliard's avatar
Alexandre Julliard committed
2704

2705
    case LB_GETTEXTLEN:
2706
        if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2707 2708
        {
            SetLastError(ERROR_INVALID_INDEX);
2709
            return LB_ERR;
2710
        }
2711 2712 2713 2714
        if (!HAS_STRINGS(descr)) return sizeof(DWORD);
        if (unicode) return strlenW( descr->items[wParam].str );
        return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
                                    strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2715

2716
    case LB_GETCURSEL:
2717 2718
        if (descr->nb_items == 0)
            return LB_ERR;
2719
        if (!IS_MULTISELECT(descr))
2720 2721 2722 2723
            return descr->selected_item;
        if (descr->selected_item != -1)
            return descr->selected_item;
        return descr->focus_item;
2724 2725
        /* otherwise, if the user tries to move the selection with the    */
        /* arrow keys, we will give the application something to choke on */
2726
    case LB_GETTOPINDEX:
2727
        return descr->top_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2728

2729
    case LB_GETITEMHEIGHT:
2730
        return LISTBOX_GetItemHeight( descr, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2731

2732
    case LB_SETITEMHEIGHT:
2733
        return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2734

2735
    case LB_ITEMFROMPOINT:
Alexandre Julliard's avatar
Alexandre Julliard committed
2736
        {
2737 2738
            POINT pt;
            RECT rect;
2739 2740
            int index;
            BOOL hit = TRUE;
2741

2742 2743 2744
            /* The hiword of the return value is not a client area
               hittest as suggested by MSDN, but rather a hittest on
               the returned listbox item. */
2745

2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770
            if(descr->nb_items == 0)
                return 0x1ffff;      /* win9x returns 0x10000, we copy winnt */

            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);

            SetRect(&rect, 0, 0, descr->width, descr->height);

            if(!PtInRect(&rect, pt))
            {
                pt.x = min(pt.x, rect.right - 1);
                pt.x = max(pt.x, 0);
                pt.y = min(pt.y, rect.bottom - 1);
                pt.y = max(pt.y, 0);
                hit = FALSE;
            }

            index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y);

            if(index == -1)
            {
                index = descr->nb_items - 1;
                hit = FALSE;
            }
            return MAKELONG(index, hit ? 0 : 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
2771 2772
        }

2773
    case LB_SETCARETINDEX:
2774
        if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2775
        if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR)
2776 2777 2778 2779 2780
            return LB_ERR;
        else if (ISWIN31)
             return wParam;
        else
             return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2781

2782
    case LB_GETCARETINDEX:
2783
        return descr->focus_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2784

2785
    case LB_SETTOPINDEX:
2786
        return LISTBOX_SetTopItem( descr, wParam, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2787

2788
    case LB_SETCOLUMNWIDTH:
2789
        return LISTBOX_SetColumnWidth( descr, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2790

2791
    case LB_GETITEMRECT:
2792
        return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2793

2794
    case LB_FINDSTRING:
2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806
    {
        INT ret;
        LPWSTR textW;
        if(unicode || !HAS_STRINGS(descr))
            textW = (LPWSTR)lParam;
        else
        {
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
        }
2807
        ret = LISTBOX_FindString( descr, wParam, textW, FALSE );
2808 2809 2810 2811
        if(!unicode && HAS_STRINGS(descr))
            HeapFree(GetProcessHeap(), 0, textW);
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2812

2813
    case LB_FINDSTRINGEXACT:
2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825
    {
        INT ret;
        LPWSTR textW;
        if(unicode || !HAS_STRINGS(descr))
            textW = (LPWSTR)lParam;
        else
        {
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
        }
2826
        ret = LISTBOX_FindString( descr, wParam, textW, TRUE );
2827 2828 2829 2830
        if(!unicode && HAS_STRINGS(descr))
            HeapFree(GetProcessHeap(), 0, textW);
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2831

2832
    case LB_SELECTSTRING:
2833 2834 2835
    {
        INT index;
        LPWSTR textW;
2836 2837 2838 2839

	if(HAS_STRINGS(descr))
	    TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
						     debugstr_a((LPSTR)lParam));
2840 2841 2842
        if(unicode || !HAS_STRINGS(descr))
            textW = (LPWSTR)lParam;
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
2843
        {
2844 2845 2846 2847
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
Alexandre Julliard's avatar
Alexandre Julliard committed
2848
        }
2849
        index = LISTBOX_FindString( descr, wParam, textW, FALSE );
2850 2851 2852
        if(!unicode && HAS_STRINGS(descr))
            HeapFree(GetProcessHeap(), 0, textW);
        if (index != LB_ERR)
2853
	{
2854 2855
            LISTBOX_MoveCaret( descr, index, TRUE );
            LISTBOX_SetSelection( descr, index, TRUE, FALSE );
2856
	}
2857 2858
        return index;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2859

2860 2861
    case LB_GETSEL:
        if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2862 2863
            return LB_ERR;
        return descr->items[wParam].selected;
Alexandre Julliard's avatar
Alexandre Julliard committed
2864

2865
    case LB_SETSEL:
2866
        return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2867

2868
    case LB_SETCURSEL:
2869
        if (IS_MULTISELECT(descr)) return LB_ERR;
2870
        LISTBOX_SetCaretIndex( descr, wParam, TRUE );
2871
        ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE );
2872
	if (ret != LB_ERR) ret = descr->selected_item;
2873
	return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2874

2875
    case LB_GETSELCOUNT:
2876
        return LISTBOX_GetSelCount( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
2877

2878
    case LB_GETSELITEMS:
2879
        return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2880

2881
    case LB_SELITEMRANGE:
Alexandre Julliard's avatar
Alexandre Julliard committed
2882
        if (LOWORD(lParam) <= HIWORD(lParam))
2883
            return LISTBOX_SelectItemRange( descr, LOWORD(lParam),
Alexandre Julliard's avatar
Alexandre Julliard committed
2884 2885
                                            HIWORD(lParam), wParam );
        else
2886
            return LISTBOX_SelectItemRange( descr, HIWORD(lParam),
Alexandre Julliard's avatar
Alexandre Julliard committed
2887
                                            LOWORD(lParam), wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2888

2889 2890
    case LB_SELITEMRANGEEX:
        if ((INT)lParam >= (INT)wParam)
2891
            return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2892
        else
2893
            return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2894

2895
    case LB_GETHORIZONTALEXTENT:
2896
        return descr->horz_extent;
Alexandre Julliard's avatar
Alexandre Julliard committed
2897

2898
    case LB_SETHORIZONTALEXTENT:
2899
        return LISTBOX_SetHorizontalExtent( descr, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2900

2901
    case LB_GETANCHORINDEX:
2902
        return descr->anchor_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2903

2904 2905
    case LB_SETANCHORINDEX:
        if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2906 2907
        {
            SetLastError(ERROR_INVALID_INDEX);
2908
            return LB_ERR;
2909
        }
2910
        descr->anchor_item = (INT)wParam;
2911
        return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2912

2913
    case LB_DIR:
2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925
    {
        INT ret;
        LPWSTR textW;
        if(unicode)
            textW = (LPWSTR)lParam;
        else
        {
            LPSTR textA = (LPSTR)lParam;
            INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
            if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
                MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
        }
2926
        ret = LISTBOX_Directory( descr, wParam, textW, msg == LB_DIR );
2927 2928 2929 2930
        if(!unicode)
            HeapFree(GetProcessHeap(), 0, textW);
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2931

2932
    case LB_GETLOCALE:
2933
        return descr->locale;
Alexandre Julliard's avatar
Alexandre Julliard committed
2934

2935
    case LB_SETLOCALE:
2936 2937 2938 2939 2940 2941 2942 2943
    {
        LCID ret;
        if (!IsValidLocale((LCID)wParam, LCID_INSTALLED))
            return LB_ERR;
        ret = descr->locale;
        descr->locale = (LCID)wParam;
        return ret;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2944

2945
    case LB_INITSTORAGE:
2946
        return LISTBOX_InitStorage( descr, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2947

2948
    case LB_SETCOUNT:
2949
        return LISTBOX_SetCount( descr, (INT)wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2950

2951
    case LB_SETTABSTOPS:
2952
        return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
2953

2954
    case LB_CARETON:
2955
        if (descr->caret_on)
2956
            return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2957
        descr->caret_on = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2958
        if ((descr->focus_item != -1) && (descr->in_focus))
2959
            LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2960
        return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2961

2962
    case LB_CARETOFF:
2963
        if (!descr->caret_on)
2964
            return LB_OKAY;
Alexandre Julliard's avatar
Alexandre Julliard committed
2965
        descr->caret_on = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2966
        if ((descr->focus_item != -1) && (descr->in_focus))
2967
            LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
2968
        return LB_OKAY;
2969

2970
    case LB_GETLISTBOXINFO:
2971
        return descr->page_size;
2972

Alexandre Julliard's avatar
Alexandre Julliard committed
2973
    case WM_DESTROY:
2974
        return LISTBOX_Destroy( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
2975 2976

    case WM_ENABLE:
2977
        InvalidateRect( descr->self, NULL, TRUE );
2978
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2979 2980

    case WM_SETREDRAW:
2981
        LISTBOX_SetRedraw( descr, wParam != 0 );
2982
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2983 2984

    case WM_GETDLGCODE:
2985 2986
        return DLGC_WANTARROWS | DLGC_WANTCHARS;

2987
    case WM_PRINTCLIENT:
Alexandre Julliard's avatar
Alexandre Julliard committed
2988
    case WM_PAINT:
Alexandre Julliard's avatar
Alexandre Julliard committed
2989
        {
2990
            PAINTSTRUCT ps;
2991 2992 2993
            HDC hdc = ( wParam ) ? ((HDC)wParam) :  BeginPaint( descr->self, &ps );
            ret = LISTBOX_Paint( descr, hdc );
            if( !wParam ) EndPaint( descr->self, &ps );
Alexandre Julliard's avatar
Alexandre Julliard committed
2994
        }
2995
        return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2996
    case WM_SIZE:
2997
        LISTBOX_UpdateSize( descr );
2998
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2999
    case WM_GETFONT:
3000
        return (LRESULT)descr->font;
Alexandre Julliard's avatar
Alexandre Julliard committed
3001
    case WM_SETFONT:
3002 3003
        LISTBOX_SetFont( descr, (HFONT)wParam );
        if (lParam) InvalidateRect( descr->self, 0, TRUE );
3004
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3005
    case WM_SETFOCUS:
Alexandre Julliard's avatar
Alexandre Julliard committed
3006
        descr->in_focus = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3007 3008
        descr->caret_on = TRUE;
        if (descr->focus_item != -1)
3009
            LISTBOX_DrawFocusRect( descr, TRUE );
3010
        SEND_NOTIFICATION( descr, LBN_SETFOCUS );
3011
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3012
    case WM_KILLFOCUS:
3013
        LISTBOX_HandleLButtonUp( descr ); /* Release capture if we have it */
Alexandre Julliard's avatar
Alexandre Julliard committed
3014
        descr->in_focus = FALSE;
3015
        descr->wheel_remain = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3016
        if ((descr->focus_item != -1) && descr->caret_on)
3017 3018
            LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS );
        SEND_NOTIFICATION( descr, LBN_KILLFOCUS );
3019
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3020
    case WM_HSCROLL:
3021
        return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3022
    case WM_VSCROLL:
3023
        return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) );
3024 3025
    case WM_MOUSEWHEEL:
        if (wParam & (MK_SHIFT | MK_CONTROL))
3026 3027
            return DefWindowProcW( descr->self, msg, wParam, lParam );
        return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3028
    case WM_LBUTTONDOWN:
3029
	if (lphc)
3030
            return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3031 3032
                                                  (INT16)LOWORD(lParam),
                                                  (INT16)HIWORD(lParam) );
3033
        return LISTBOX_HandleLButtonDown( descr, wParam,
Alexandre Julliard's avatar
Alexandre Julliard committed
3034 3035 3036
                                          (INT16)LOWORD(lParam),
                                          (INT16)HIWORD(lParam) );
    case WM_LBUTTONDBLCLK:
3037
	if (lphc)
3038
            return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam,
3039 3040
                                                  (INT16)LOWORD(lParam),
                                                  (INT16)HIWORD(lParam) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3041
        if (descr->style & LBS_NOTIFY)
3042
            SEND_NOTIFICATION( descr, LBN_DBLCLK );
3043
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3044
    case WM_MOUSEMOVE:
3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057
        if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) )
        {
            BOOL    captured = descr->captured;
            POINT   mousePos;
            RECT    clientRect;

            mousePos.x = (INT16)LOWORD(lParam);
            mousePos.y = (INT16)HIWORD(lParam);

            /*
             * If we are in a dropdown combobox, we simulate that
             * the mouse is captured to show the tracking of the item.
             */
3058
            if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos ))
3059 3060
                descr->captured = TRUE;

3061
            LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y);
3062 3063 3064

            descr->captured = captured;
        } 
3065
        else if (GetCapture() == descr->self)
3066
        {
3067
            LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam),
Alexandre Julliard's avatar
Alexandre Julliard committed
3068
                                     (INT16)HIWORD(lParam) );
3069
        }
3070
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3071
    case WM_LBUTTONUP:
3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084
	if (lphc)
	{
            POINT mousePos;
            RECT  clientRect;

            /*
             * If the mouse button "up" is not in the listbox,
             * we make sure there is no selection by re-selecting the
             * item that was selected when the listbox was made visible.
             */
            mousePos.x = (INT16)LOWORD(lParam);
            mousePos.y = (INT16)HIWORD(lParam);

3085
            GetClientRect(descr->self, &clientRect);
3086 3087 3088 3089 3090 3091 3092 3093

            /*
             * When the user clicks outside the combobox and the focus
             * is lost, the owning combobox will send a fake buttonup with
             * 0xFFFFFFF as the mouse location, we must also revert the
             * selection to the original selection.
             */
            if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) )
3094
                LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE );
3095
        }
3096
        return LISTBOX_HandleLButtonUp( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
3097
    case WM_KEYDOWN:
3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110
        if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
        {
            /* for some reason Windows makes it possible to
             * show/hide ComboLBox by sending it WM_KEYDOWNs */

            if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
                ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
                  && (wParam == VK_DOWN || wParam == VK_UP)) )
            {
                COMBO_FlipListbox( lphc, FALSE, FALSE );
                return 0;
            }
        }
3111
        return LISTBOX_HandleKeyDown( descr, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3112
    case WM_CHAR:
3113 3114 3115 3116 3117 3118 3119 3120 3121
    {
        WCHAR charW;
        if(unicode)
            charW = (WCHAR)wParam;
        else
        {
            CHAR charA = (CHAR)wParam;
            MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
        }
3122
        return LISTBOX_HandleChar( descr, charW );
3123
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3124
    case WM_SYSTIMER:
3125
        return LISTBOX_HandleSystemTimer( descr );
Alexandre Julliard's avatar
Alexandre Julliard committed
3126
    case WM_ERASEBKGND:
3127
        if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
Alexandre Julliard's avatar
Alexandre Julliard committed
3128
        {
3129
            RECT rect;
3130
            HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3131
                                              wParam, (LPARAM)descr->self );
3132
	    TRACE("hbrush = %p\n", hbrush);
3133 3134 3135 3136
	    if(!hbrush)
		hbrush = GetSysColorBrush(COLOR_WINDOW);
	    if(hbrush)
	    {
3137
		GetClientRect(descr->self, &rect);
3138 3139
		FillRect((HDC)wParam, &rect, hbrush);
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3140
        }
3141
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3142
    case WM_DROPFILES:
3143 3144 3145 3146 3147 3148 3149
        if( lphc ) return 0;
        return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
                         SendMessageA( descr->owner, msg, wParam, lParam );

    case WM_NCDESTROY:
        if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE )
            lphc->hWndLBox = 0;
3150
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3151

3152 3153 3154 3155
    case WM_NCACTIVATE:
        if (lphc) return 0;
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
3156 3157
    default:
        if ((msg >= WM_USER) && (msg < 0xc000))
3158
            WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n",
3159
                 hwnd, msg, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3160
    }
3161 3162 3163

    return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
                     DefWindowProcA( hwnd, msg, wParam, lParam );
3164
}
3165 3166 3167 3168 3169 3170

DWORD WINAPI GetListBoxInfo(HWND hwnd)
{
    TRACE("%p\n", hwnd);
    return SendMessageW(hwnd, LB_GETLISTBOXINFO, 0, 0);
}