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