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