Added calibration of KeStallExecutionProcessor delay
[reactos.git] / reactos / lib / user32 / controls / listbox.c
1 /*
2 * Listbox controls
3 *
4 * Copyright 1996 Alexandre Julliard
5 */
6
7 #include <ntos/minmax.h>
8 #define MIN min
9 #define MAX max
10
11 #include <string.h>
12 #include <windows.h>
13 #include <user32/win.h>
14 #include <user32/combo.h>
15 #include <user32/scroll.h>
16 #include <user32/debug.h>
17 #include <user32/nc.h>
18
19 #define MAX_DOS_DRIVES 26
20
21 #define abs(x) ((x) < 0 ? -(x) : (x))
22
23 #define WM_LBTRACKPOINT 0x0131
24 #define WS_EX_DRAGDETECT 0x00000002L
25
26 /* D&D messages */
27 #define WM_DROPOBJECT 0x022A
28 #define WM_QUERYDROPOBJECT 0x022B
29 #define WM_BEGINDRAG 0x022C
30 #define WM_DRAGLOOP 0x022D
31 #define WM_DRAGSELECT 0x022E
32 #define WM_DRAGMOVE 0x022F
33
34
35 /* Unimplemented yet:
36 * - LBS_NOSEL
37 * - LBS_USETABSTOPS
38 * - Unicode
39 * - Locale handling
40 */
41
42 /* Items array granularity */
43 #define LB_ARRAY_GRANULARITY 16
44
45 /* Scrolling timeout in ms */
46 #define LB_SCROLL_TIMEOUT 50
47
48 /* Listbox system timer id */
49 #define LB_TIMER_ID 2
50
51 /* Item structure */
52 typedef struct
53 {
54 LPSTR str; /* Item text */
55 BOOL selected; /* Is item selected? */
56 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
57 DWORD data; /* User data */
58 } LB_ITEMDATA;
59
60 /* Listbox structure */
61 typedef struct
62 {
63 HANDLE heap; /* Heap for this listbox */
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 LB_ITEMDATA *items; /* Array of items */
69 INT nb_items; /* Number of items */
70 INT top_item; /* Top visible item */
71 INT selected_item; /* Selected item */
72 INT focus_item; /* Item that has the focus */
73 INT anchor_item; /* Anchor item for extended selection */
74 INT item_height; /* Default item height */
75 INT page_size; /* Items per listbox page */
76 INT column_width; /* Column width for multi-column listboxes */
77 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
78 INT horz_pos; /* Horizontal position */
79 INT nb_tabs; /* Number of tabs in array */
80 INT *tabs; /* Array of tabs */
81 BOOL caret_on; /* Is caret on? */
82 HFONT font; /* Current font */
83 LCID locale; /* Current locale for string comparisons */
84 LPHEADCOMBO lphc; /* ComboLBox */
85 } LB_DESCR;
86
87
88 #define IS_OWNERDRAW(descr) \
89 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
90
91 #define HAS_STRINGS(descr) \
92 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
93
94 #define SEND_NOTIFICATION(wnd,descr,code) \
95 (SendMessageA( (descr)->owner, WM_COMMAND, \
96 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (LPARAM)(wnd)->hwndSelf ))
97
98 /* Current timer status */
99 typedef enum
100 {
101 LB_TIMER_NONE,
102 LB_TIMER_UP,
103 LB_TIMER_LEFT,
104 LB_TIMER_DOWN,
105 LB_TIMER_RIGHT
106 } TIMER_DIRECTION;
107
108 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
109
110
111 /***********************************************************************
112 * LISTBOX_DPRINT
113 */
114 void LISTBOX_DPRINT( WND *wnd )
115 {
116 INT i;
117 LB_ITEMDATA *item;
118 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
119
120 DPRINT( "Listbox:\n" );
121 DPRINT( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
122 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
123 descr->top_item );
124 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
125 {
126 DPRINT( "%4d: %-40s %d %08lx %3d\n",
127 i, item->str, item->selected, item->data, item->height );
128 }
129 }
130
131
132 /***********************************************************************
133 * LISTBOX_GetCurrentPageSize
134 *
135 * Return the current page size
136 */
137 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
138 {
139 INT i, height;
140 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
141 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
142 {
143 if ((height += descr->items[i].height) > descr->height) break;
144 }
145 if (i == descr->top_item) return 1;
146 else return i - descr->top_item;
147 }
148
149
150 /***********************************************************************
151 * LISTBOX_GetMaxTopIndex
152 *
153 * Return the maximum possible index for the top of the listbox.
154 */
155 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
156 {
157 INT max, page;
158
159 if (descr->style & LBS_OWNERDRAWVARIABLE)
160 {
161 page = descr->height;
162 for (max = descr->nb_items - 1; max >= 0; max--)
163 if ((page -= descr->items[max].height) < 0) break;
164 if (max < descr->nb_items - 1) max++;
165 }
166 else if (descr->style & LBS_MULTICOLUMN)
167 {
168 if ((page = descr->width / descr->column_width) < 1) page = 1;
169 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
170 max = (max - page) * descr->page_size;
171 }
172 else
173 {
174 max = descr->nb_items - descr->page_size;
175 }
176 if (max < 0) max = 0;
177 return max;
178 }
179
180
181 /***********************************************************************
182 * LISTBOX_UpdateScroll
183 *
184 * Update the scrollbars. Should be called whenever the content
185 * of the listbox changes.
186 */
187 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
188 {
189 SCROLLINFO info;
190
191 if (!(descr->style & WS_VSCROLL)) return;
192 /* It is important that we check descr->style, and not wnd->dwStyle,
193 for WS_VSCROLL, as the former is exactly the one passed in
194 argument to CreateWindow.
195 In Windows (and from now on in Wine :) a listbox created
196 with such a style (no WS_SCROLL) does not update
197 the scrollbar with listbox-related data, thus letting
198 the programmer use it for his/her own purposes. */
199
200 if (descr->style & LBS_NOREDRAW) return;
201 info.cbSize = sizeof(info);
202
203 if (descr->style & LBS_MULTICOLUMN)
204 {
205 info.nMin = 0;
206 info.nMax = (descr->nb_items - 1) / descr->page_size;
207 info.nPos = descr->top_item / descr->page_size;
208 info.nPage = descr->width / descr->column_width;
209 if (info.nPage < 1) info.nPage = 1;
210 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
211 if (descr->style & LBS_DISABLENOSCROLL)
212 info.fMask |= SIF_DISABLENOSCROLL;
213 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
214 info.nMax = 0;
215 info.fMask = SIF_RANGE;
216 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
217 }
218 else
219 {
220 info.nMin = 0;
221 info.nMax = descr->nb_items - 1;
222 info.nPos = descr->top_item;
223 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
224 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
225 if (descr->style & LBS_DISABLENOSCROLL)
226 info.fMask |= SIF_DISABLENOSCROLL;
227 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
228
229 if (descr->horz_extent)
230 {
231 info.nMin = 0;
232 info.nMax = descr->horz_extent - 1;
233 info.nPos = descr->horz_pos;
234 info.nPage = descr->width;
235 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
236 if (descr->style & LBS_DISABLENOSCROLL)
237 info.fMask |= SIF_DISABLENOSCROLL;
238 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
239 }
240 }
241 }
242
243
244 /***********************************************************************
245 * LISTBOX_SetTopItem
246 *
247 * Set the top item of the listbox, scrolling up or down if necessary.
248 */
249 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
250 BOOL scroll )
251 {
252 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
253 if (index > max) index = max;
254 if (index < 0) index = 0;
255 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
256 if (descr->top_item == index) return LB_OKAY;
257 if (descr->style & LBS_MULTICOLUMN)
258 {
259 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
260 if (scroll && (abs(diff) < descr->width))
261 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
262 SW_INVALIDATE | SW_ERASE );
263 else
264 scroll = FALSE;
265 }
266 else if (scroll)
267 {
268 INT diff;
269 if (descr->style & LBS_OWNERDRAWVARIABLE)
270 {
271 INT i;
272 diff = 0;
273 if (index > descr->top_item)
274 {
275 for (i = index - 1; i >= descr->top_item; i--)
276 diff -= descr->items[i].height;
277 }
278 else
279 {
280 for (i = index; i < descr->top_item; i++)
281 diff += descr->items[i].height;
282 }
283 }
284 else
285 diff = (descr->top_item - index) * descr->item_height;
286
287 if (abs(diff) < descr->height)
288 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
289 SW_INVALIDATE | SW_ERASE );
290 else
291 scroll = FALSE;
292 }
293 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
294 descr->top_item = index;
295 LISTBOX_UpdateScroll( wnd, descr );
296 return LB_OKAY;
297 }
298
299
300 /***********************************************************************
301 * LISTBOX_UpdatePage
302 *
303 * Update the page size. Should be called when the size of
304 * the client area or the item height changes.
305 */
306 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
307 {
308 INT page_size;
309
310 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
311 if (page_size == descr->page_size) return;
312 descr->page_size = page_size;
313 if (descr->style & LBS_MULTICOLUMN)
314 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
315 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
316 }
317
318
319 /***********************************************************************
320 * LISTBOX_UpdateSize
321 *
322 * Update the size of the listbox. Should be called when the size of
323 * the client area changes.
324 */
325 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
326 {
327 RECT rect;
328
329 GetClientRect( wnd->hwndSelf, &rect );
330 descr->width = rect.right - rect.left;
331 descr->height = rect.bottom - rect.top;
332 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
333 {
334 if ((descr->height > descr->item_height) &&
335 (descr->height % descr->item_height))
336 {
337 DPRINT( "[%04x]: changing height %d -> %d\n",
338 wnd->hwndSelf, descr->height,
339 descr->height - descr->height%descr->item_height );
340 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
341 wnd->rectWindow.right - wnd->rectWindow.left,
342 wnd->rectWindow.bottom - wnd->rectWindow.top -
343 (descr->height % descr->item_height),
344 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
345 return;
346 }
347 }
348 DPRINT( "[%04x]: new size = %d,%d\n",
349 wnd->hwndSelf, descr->width, descr->height );
350 LISTBOX_UpdatePage( wnd, descr );
351 LISTBOX_UpdateScroll( wnd, descr );
352 }
353
354
355 /***********************************************************************
356 * LISTBOX_GetItemRect
357 *
358 * Get the rectangle enclosing an item, in listbox client coordinates.
359 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
360 */
361 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
362 RECT *rect )
363 {
364 /* Index <= 0 is legal even on empty listboxes */
365 if (index && (index >= descr->nb_items)) return -1;
366 SetRect( rect, 0, 0, descr->width, descr->height );
367 if (descr->style & LBS_MULTICOLUMN)
368 {
369 INT col = (index / descr->page_size) -
370 (descr->top_item / descr->page_size);
371 rect->left += col * descr->column_width;
372 rect->right = rect->left + descr->column_width;
373 rect->top += (index % descr->page_size) * descr->item_height;
374 rect->bottom = rect->top + descr->item_height;
375 }
376 else if (descr->style & LBS_OWNERDRAWVARIABLE)
377 {
378 INT i;
379 rect->right += descr->horz_pos;
380 if ((index >= 0) && (index < descr->nb_items))
381 {
382 if (index < descr->top_item)
383 {
384 for (i = descr->top_item-1; i >= index; i--)
385 rect->top -= descr->items[i].height;
386 }
387 else
388 {
389 for (i = descr->top_item; i < index; i++)
390 rect->top += descr->items[i].height;
391 }
392 rect->bottom = rect->top + descr->items[index].height;
393
394 }
395 }
396 else
397 {
398 rect->top += (index - descr->top_item) * descr->item_height;
399 rect->bottom = rect->top + descr->item_height;
400 rect->right += descr->horz_pos;
401 }
402
403 return ((rect->left < descr->width) && (rect->right > 0) &&
404 (rect->top < descr->height) && (rect->bottom > 0));
405 }
406
407
408 /***********************************************************************
409 * LISTBOX_GetItemFromPoint
410 *
411 * Return the item nearest from point (x,y) (in client coordinates).
412 */
413 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
414 INT x, INT y )
415 {
416 INT index = descr->top_item;
417
418 if (!descr->nb_items) return -1; /* No items */
419 if (descr->style & LBS_OWNERDRAWVARIABLE)
420 {
421 INT pos = 0;
422 if (y >= 0)
423 {
424 while (index < descr->nb_items)
425 {
426 if ((pos += descr->items[index].height) > y) break;
427 index++;
428 }
429 }
430 else
431 {
432 while (index > 0)
433 {
434 index--;
435 if ((pos -= descr->items[index].height) <= y) break;
436 }
437 }
438 }
439 else if (descr->style & LBS_MULTICOLUMN)
440 {
441 if (y >= descr->item_height * descr->page_size) return -1;
442 if (y >= 0) index += y / descr->item_height;
443 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
444 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
445 }
446 else
447 {
448 index += (y / descr->item_height);
449 }
450 if (index < 0) return 0;
451 if (index >= descr->nb_items) return -1;
452 return index;
453 }
454
455
456 /***********************************************************************
457 * LISTBOX_PaintItem
458 *
459 * Paint an item.
460 */
461 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
462 const RECT *rect, INT index, UINT action )
463 {
464 LB_ITEMDATA *item = NULL;
465 if (index < descr->nb_items) item = &descr->items[index];
466
467 if (IS_OWNERDRAW(descr))
468 {
469 DRAWITEMSTRUCT dis;
470 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
471
472 dis.CtlType = ODT_LISTBOX;
473 dis.CtlID = id;
474 dis.hwndItem = wnd->hwndSelf;
475 dis.itemAction = action;
476 dis.hDC = hdc;
477 dis.itemID = index;
478 dis.itemState = 0;
479 if (item && item->selected) dis.itemState |= ODS_SELECTED;
480 if ((descr->focus_item == index) &&
481 (descr->caret_on) &&
482 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
483 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
484 dis.itemData = item ? item->data : 0;
485 dis.rcItem = *rect;
486 DPRINT( "[%04x]: drawitem %d (%s) action=%02x "
487 "state=%02x rect=%d,%d-%d,%d\n",
488 wnd->hwndSelf, index, item ? item->str : "", action,
489 dis.itemState, rect->left, rect->top,
490 rect->right, rect->bottom );
491 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
492 }
493 else
494 {
495 COLORREF oldText = 0, oldBk = 0;
496
497 if (action == ODA_FOCUS)
498 {
499 DrawFocusRect( hdc, rect );
500 return;
501 }
502 if (item && item->selected)
503 {
504 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
505 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
506 }
507
508 DPRINT( "[%04x]: painting %d (%s) action=%02x "
509 "rect=%d,%d-%d,%d\n",
510 wnd->hwndSelf, index, item ? item->str : "", action,
511 rect->left, rect->top, rect->right, rect->bottom );
512 if (!item)
513 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
514 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
515 else if (!(descr->style & LBS_USETABSTOPS))
516 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
517 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
518 strlen(item->str), NULL );
519 else
520 {
521 /* Output empty string to paint background in the full width. */
522 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
523 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
524 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
525 item->str, strlen(item->str),
526 descr->nb_tabs, descr->tabs, 0);
527 }
528 if (item && item->selected)
529 {
530 SetBkColor( hdc, oldBk );
531 SetTextColor( hdc, oldText );
532 }
533 if ((descr->focus_item == index) &&
534 (descr->caret_on) &&
535 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
536 }
537 }
538
539
540 /***********************************************************************
541 * LISTBOX_SetRedraw
542 *
543 * Change the redraw flag.
544 */
545 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
546 {
547 if (on)
548 {
549 if (!(descr->style & LBS_NOREDRAW)) return;
550 descr->style &= ~LBS_NOREDRAW;
551 LISTBOX_UpdateScroll( wnd, descr );
552 }
553 else descr->style |= LBS_NOREDRAW;
554 }
555
556
557 /***********************************************************************
558 * LISTBOX_RepaintItem
559 *
560 * Repaint a single item synchronously.
561 */
562 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
563 UINT action )
564 {
565 HDC hdc;
566 RECT rect;
567 HFONT oldFont = 0;
568 HBRUSH hbrush, oldBrush = 0;
569
570 if (descr->style & LBS_NOREDRAW) return;
571 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
572 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
573 if (descr->font) oldFont = SelectObject( hdc, descr->font );
574 hbrush = (HBRUSH)SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
575 (WPARAM)hdc, (LPARAM)wnd->hwndSelf );
576 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
577 if (wnd->dwStyle & WS_DISABLED)
578 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
579 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
580 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
581 if (oldFont) SelectObject( hdc, oldFont );
582 if (oldBrush) SelectObject( hdc, oldBrush );
583 ReleaseDC( wnd->hwndSelf, hdc );
584 }
585
586
587 /***********************************************************************
588 * LISTBOX_InitStorage
589 */
590 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
591 DWORD bytes )
592 {
593 LB_ITEMDATA *item;
594
595 nb_items += LB_ARRAY_GRANULARITY - 1;
596 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
597 if (descr->items)
598 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
599 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
600 nb_items * sizeof(LB_ITEMDATA) )))
601 {
602 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
603 return LB_ERRSPACE;
604 }
605 descr->items = item;
606 return LB_OKAY;
607 }
608
609
610 /***********************************************************************
611 * LISTBOX_SetTabStops
612 */
613 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
614 LPINT tabs, BOOL short_ints )
615 {
616 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
617 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
618 if (!(descr->nb_tabs = count))
619 {
620 descr->tabs = NULL;
621 return TRUE;
622 }
623 /* FIXME: count = 1 */
624 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
625 descr->nb_tabs * sizeof(INT) )))
626 return FALSE;
627 if (short_ints)
628 {
629 INT i;
630 INT * p = (INT *)tabs;
631 // dbg_decl_str(listbox, 256);
632
633 for (i = 0; i < descr->nb_tabs; i++) {
634 descr->tabs[i] = *p++<<1; /* FIXME */
635 // if(TRACE_ON(listbox))
636 // dsprintf(listbox, "%hd ", descr->tabs[i]);
637 }
638 DPRINT( "[%04x]: settabstops %s\n",
639 wnd->hwndSelf, dbg_str(listbox));
640 }
641 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
642 /* FIXME: repaint the window? */
643 return TRUE;
644 }
645
646
647 /***********************************************************************
648 * LISTBOX_GetText
649 */
650 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
651 LPSTR buffer )
652 {
653 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
654 if (HAS_STRINGS(descr))
655 {
656 if (!buffer)
657 return strlen(descr->items[index].str);
658 lstrcpyA( buffer, descr->items[index].str );
659 return strlen(buffer);
660 } else {
661 if (buffer)
662 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
663 return sizeof(DWORD);
664 }
665 }
666
667
668 /***********************************************************************
669 * LISTBOX_FindStringPos
670 *
671 * Find the nearest string located before a given string in sort order.
672 * If 'exact' is TRUE, return an error if we don't get an exact match.
673 */
674 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
675 BOOL exact )
676 {
677 INT index, min, max, res = -1;
678
679 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
680 min = 0;
681 max = descr->nb_items;
682 while (min != max)
683 {
684 index = (min + max) / 2;
685 if (HAS_STRINGS(descr))
686 res = lstrcmpiA( descr->items[index].str, str );
687 else
688 {
689 COMPAREITEMSTRUCT cis;
690 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
691
692 cis.CtlType = ODT_LISTBOX;
693 cis.CtlID = id;
694 cis.hwndItem = wnd->hwndSelf;
695 cis.itemID1 = index;
696 cis.itemData1 = descr->items[index].data;
697 cis.itemID2 = -1;
698 cis.itemData2 = (DWORD)str;
699 //cis.dwLocaleId = descr->locale;
700 res = SendMessageA( descr->owner, WM_COMPAREITEM,
701 id, (LPARAM)&cis );
702 }
703 if (!res) return index;
704 if (res > 0) max = index;
705 else min = index + 1;
706 }
707 return exact ? -1 : max;
708 }
709
710
711 /***********************************************************************
712 * LISTBOX_FindFileStrPos
713 *
714 * Find the nearest string located before a given string in directory
715 * sort order (i.e. first files, then directories, then drives).
716 */
717 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
718 {
719 INT min, max, res = -1;
720
721 if (!HAS_STRINGS(descr))
722 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
723 min = 0;
724 max = descr->nb_items;
725 while (min != max)
726 {
727 INT index = (min + max) / 2;
728 const char *p = descr->items[index].str;
729 if (*p == '[') /* drive or directory */
730 {
731 if (*str != '[') res = -1;
732 else if (p[1] == '-') /* drive */
733 {
734 if (str[1] == '-') res = str[2] - p[2];
735 else res = -1;
736 }
737 else /* directory */
738 {
739 if (str[1] == '-') res = 1;
740 else res = lstrcmpiA( str, p );
741 }
742 }
743 else /* filename */
744 {
745 if (*str == '[') res = 1;
746 else res = lstrcmpiA( str, p );
747 }
748 if (!res) return index;
749 if (res < 0) max = index;
750 else min = index + 1;
751 }
752 return max;
753 }
754
755
756 /***********************************************************************
757 * LISTBOX_FindString
758 *
759 * Find the item beginning with a given string.
760 */
761 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
762 LPCSTR str, BOOL exact )
763 {
764 INT i;
765 LB_ITEMDATA *item;
766
767 if (start >= descr->nb_items) start = -1;
768 item = descr->items + start + 1;
769 if (HAS_STRINGS(descr))
770 {
771 if (!str) return LB_ERR;
772 if (exact)
773 {
774 for (i = start + 1; i < descr->nb_items; i++, item++)
775 if (!lstrcmpiA( str, item->str )) return i;
776 for (i = 0, item = descr->items; i <= start; i++, item++)
777 if (!lstrcmpiA( str, item->str )) return i;
778 }
779 else
780 {
781 /* Special case for drives and directories: ignore prefix */
782 #define CHECK_DRIVE(item) \
783 if ((item)->str[0] == '[') \
784 { \
785 if (!lstrcmpiA( str, (item)->str+1 )) return i; \
786 if (((item)->str[1] == '-') && !lstrcmpiA(str,(item)->str+2)) \
787 return i; \
788 }
789
790 INT len = lstrlenA(str);
791 for (i = start + 1; i < descr->nb_items; i++, item++)
792 {
793 if (!lstrcmpiA( str, item->str )) return i;
794 CHECK_DRIVE(item);
795 }
796 for (i = 0, item = descr->items; i <= start; i++, item++)
797 {
798 if (!lstrcmpiA( str, item->str )) return i;
799 CHECK_DRIVE(item);
800 }
801 #undef CHECK_DRIVE
802 }
803 }
804 else
805 {
806 if (exact && (descr->style & LBS_SORT))
807 /* If sorted, use a WM_COMPAREITEM binary search */
808 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
809
810 /* Otherwise use a linear search */
811 for (i = start + 1; i < descr->nb_items; i++, item++)
812 if (item->data == (DWORD)str) return i;
813 for (i = 0, item = descr->items; i <= start; i++, item++)
814 if (item->data == (DWORD)str) return i;
815 }
816 return LB_ERR;
817 }
818
819
820 /***********************************************************************
821 * LISTBOX_GetSelCount
822 */
823 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
824 {
825 INT i, count;
826 LB_ITEMDATA *item = descr->items;
827
828 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
829 for (i = count = 0; i < descr->nb_items; i++, item++)
830 if (item->selected) count++;
831 return count;
832 }
833
834
835
836
837 /***********************************************************************
838 * LISTBOX_GetSelItems
839 */
840 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
841 LPINT array )
842 {
843 INT i, count;
844 LB_ITEMDATA *item = descr->items;
845
846 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
847 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
848 if (item->selected) array[count++] = i;
849 return count;
850 }
851
852
853 /***********************************************************************
854 * LISTBOX_Paint
855 */
856 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
857 {
858 INT i, col_pos = descr->page_size - 1;
859 RECT rect;
860 HFONT oldFont = 0;
861 HBRUSH hbrush, oldBrush = 0;
862
863 SetRect( &rect, 0, 0, descr->width, descr->height );
864 if (descr->style & LBS_NOREDRAW) return 0;
865 if (descr->style & LBS_MULTICOLUMN)
866 rect.right = rect.left + descr->column_width;
867 else if (descr->horz_pos)
868 {
869 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
870 rect.right += descr->horz_pos;
871 }
872
873 if (descr->font) oldFont = SelectObject( hdc, descr->font );
874 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
875 (WPARAM)hdc, (LPARAM)wnd->hwndSelf );
876 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
877 if (wnd->dwStyle & WS_DISABLED)
878 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
879
880 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
881 (GetFocus() == wnd->hwndSelf))
882 {
883 /* Special case for empty listbox: paint focus rect */
884 rect.bottom = rect.top + descr->item_height;
885 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
886 ODA_FOCUS );
887 rect.top = rect.bottom;
888 }
889
890 for (i = descr->top_item; i < descr->nb_items; i++)
891 {
892 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
893 rect.bottom = rect.top + descr->item_height;
894 else
895 rect.bottom = rect.top + descr->items[i].height;
896
897 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
898 rect.top = rect.bottom;
899
900 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
901 {
902 if (!IS_OWNERDRAW(descr))
903 {
904 /* Clear the bottom of the column */
905 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
906 if (rect.top < descr->height)
907 {
908 rect.bottom = descr->height;
909 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
910 &rect, NULL, 0, NULL );
911 }
912 }
913
914 /* Go to the next column */
915 rect.left += descr->column_width;
916 rect.right += descr->column_width;
917 rect.top = 0;
918 col_pos = descr->page_size - 1;
919 }
920 else
921 {
922 col_pos--;
923 if (rect.top >= descr->height) break;
924 }
925 }
926
927 if (!IS_OWNERDRAW(descr))
928 {
929 /* Clear the remainder of the client area */
930 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
931 if (rect.top < descr->height)
932 {
933 rect.bottom = descr->height;
934 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
935 &rect, NULL, 0, NULL );
936 }
937 if (rect.right < descr->width)
938 {
939 rect.left = rect.right;
940 rect.right = descr->width;
941 rect.top = 0;
942 rect.bottom = descr->height;
943 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
944 &rect, NULL, 0, NULL );
945 }
946 }
947 if (oldFont) SelectObject( hdc, oldFont );
948 if (oldBrush) SelectObject( hdc, oldBrush );
949 return 0;
950 }
951
952
953 /***********************************************************************
954 * LISTBOX_InvalidateItems
955 *
956 * Invalidate all items from a given item. If the specified item is not
957 * visible, nothing happens.
958 */
959 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
960 {
961 RECT rect;
962
963 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
964 {
965 rect.bottom = descr->height;
966 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
967 if (descr->style & LBS_MULTICOLUMN)
968 {
969 /* Repaint the other columns */
970 rect.left = rect.right;
971 rect.right = descr->width;
972 rect.top = 0;
973 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
974 }
975 }
976 }
977
978
979 /***********************************************************************
980 * LISTBOX_GetItemHeight
981 */
982 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
983 {
984 if (descr->style & LBS_OWNERDRAWVARIABLE)
985 {
986 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
987 return descr->items[index].height;
988 }
989 else return descr->item_height;
990 }
991
992
993 /***********************************************************************
994 * LISTBOX_SetItemHeight
995 */
996 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
997 UINT height )
998 {
999 if (!height) height = 1;
1000
1001 if (descr->style & LBS_OWNERDRAWVARIABLE)
1002 {
1003 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1004 DPRINT( "[%04x]: item %d height = %d\n",
1005 wnd->hwndSelf, index, height );
1006 descr->items[index].height = height;
1007 LISTBOX_UpdateScroll( wnd, descr );
1008 LISTBOX_InvalidateItems( wnd, descr, index );
1009 }
1010 else if (height != descr->item_height)
1011 {
1012 DPRINT( "[%04x]: new height = %d\n",
1013 wnd->hwndSelf, height );
1014 descr->item_height = height;
1015 LISTBOX_UpdatePage( wnd, descr );
1016 LISTBOX_UpdateScroll( wnd, descr );
1017 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1018 }
1019 return LB_OKAY;
1020 }
1021
1022
1023 /***********************************************************************
1024 * LISTBOX_SetHorizontalPos
1025 */
1026 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1027 {
1028 INT diff;
1029
1030 if (pos > descr->horz_extent - descr->width)
1031 pos = descr->horz_extent - descr->width;
1032 if (pos < 0) pos = 0;
1033 if (!(diff = descr->horz_pos - pos)) return;
1034 DPRINT( "[%04x]: new horz pos = %d\n",
1035 wnd->hwndSelf, pos );
1036 descr->horz_pos = pos;
1037 LISTBOX_UpdateScroll( wnd, descr );
1038 if (abs(diff) < descr->width)
1039 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1040 SW_INVALIDATE | SW_ERASE );
1041 else
1042 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1043 }
1044
1045
1046 /***********************************************************************
1047 * LISTBOX_SetHorizontalExtent
1048 */
1049 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1050 UINT extent )
1051 {
1052 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1053 return LB_OKAY;
1054 if (extent <= 0) extent = 1;
1055 if (extent == descr->horz_extent) return LB_OKAY;
1056 DPRINT( "[%04x]: new horz extent = %d\n",
1057 wnd->hwndSelf, extent );
1058 descr->horz_extent = extent;
1059 if (descr->horz_pos > extent - descr->width)
1060 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1061 else
1062 LISTBOX_UpdateScroll( wnd, descr );
1063 return LB_OKAY;
1064 }
1065
1066
1067 /***********************************************************************
1068 * LISTBOX_SetColumnWidth
1069 */
1070 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1071 {
1072 width += 2; /* For left and right margin */
1073 if (width == descr->column_width) return LB_OKAY;
1074 DPRINT( "[%04x]: new column width = %d\n",
1075 wnd->hwndSelf, width );
1076 descr->column_width = width;
1077 LISTBOX_UpdatePage( wnd, descr );
1078 return LB_OKAY;
1079 }
1080
1081
1082 /***********************************************************************
1083 * LISTBOX_SetFont
1084 *
1085 * Returns the item height.
1086 */
1087 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1088 {
1089 HDC hdc;
1090 HFONT oldFont = 0;
1091 TEXTMETRIC tm;
1092
1093 descr->font = font;
1094
1095 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1096 {
1097 DPRINT("unable to get DC.\n" );
1098 return 16;
1099 }
1100 if (font) oldFont = SelectObject( hdc, font );
1101 GetTextMetricsA( hdc, &tm );
1102 if (oldFont) SelectObject( hdc, oldFont );
1103 ReleaseDC( wnd->hwndSelf, hdc );
1104 if (!IS_OWNERDRAW(descr))
1105 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1106 return tm.tmHeight ;
1107 }
1108
1109
1110 /***********************************************************************
1111 * LISTBOX_MakeItemVisible
1112 *
1113 * Make sure that a given item is partially or fully visible.
1114 */
1115 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1116 BOOL fully )
1117 {
1118 INT top;
1119
1120 if (index <= descr->top_item) top = index;
1121 else if (descr->style & LBS_MULTICOLUMN)
1122 {
1123 INT cols = descr->width;
1124 if (!fully) cols += descr->column_width - 1;
1125 if (cols >= descr->column_width) cols /= descr->column_width;
1126 else cols = 1;
1127 if (index < descr->top_item + (descr->page_size * cols)) return;
1128 top = index - descr->page_size * (cols - 1);
1129 }
1130 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1131 {
1132 INT height = fully ? descr->items[index].height : 1;
1133 for (top = index; top > descr->top_item; top--)
1134 if ((height += descr->items[top-1].height) > descr->height) break;
1135 }
1136 else
1137 {
1138 if (index < descr->top_item + descr->page_size) return;
1139 if (!fully && (index == descr->top_item + descr->page_size) &&
1140 (descr->height > (descr->page_size * descr->item_height))) return;
1141 top = index - descr->page_size + 1;
1142 }
1143 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1144 }
1145
1146
1147 /***********************************************************************
1148 * LISTBOX_SelectItemRange
1149 *
1150 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1151 */
1152 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1153 INT last, BOOL on )
1154 {
1155 INT i;
1156
1157 /* A few sanity checks */
1158
1159 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1160 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1161 if (last == -1) last = descr->nb_items - 1;
1162 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1163 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1164 /* selected_item reflects last selected/unselected item on multiple sel */
1165 descr->selected_item = last;
1166
1167 if (on) /* Turn selection on */
1168 {
1169 for (i = first; i <= last; i++)
1170 {
1171 if (descr->items[i].selected) continue;
1172 descr->items[i].selected = TRUE;
1173 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1174 }
1175 }
1176 else /* Turn selection off */
1177 {
1178 for (i = first; i <= last; i++)
1179 {
1180 if (!descr->items[i].selected) continue;
1181 descr->items[i].selected = FALSE;
1182 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1183 }
1184 }
1185 return LB_OKAY;
1186 }
1187
1188
1189 /***********************************************************************
1190 * LISTBOX_SetCaretIndex
1191 *
1192 * NOTES
1193 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1194 *
1195 */
1196 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1197 BOOL fully_visible )
1198 {
1199 INT oldfocus = descr->focus_item;
1200
1201 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1202 if (index == oldfocus) return LB_OKAY;
1203 descr->focus_item = index;
1204 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1205 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1206
1207 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1208 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1209 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1210
1211 return LB_OKAY;
1212 }
1213
1214
1215 /***********************************************************************
1216 * LISTBOX_SetSelection
1217 */
1218 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1219 BOOL on, BOOL send_notify )
1220 {
1221 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1222 if (descr->style & LBS_MULTIPLESEL)
1223 {
1224 if (index == -1) /* Select all items */
1225 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1226 else /* Only one item */
1227 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1228 }
1229 else
1230 {
1231 INT oldsel = descr->selected_item;
1232 if (index == oldsel) return LB_OKAY;
1233 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1234 if (index != -1) descr->items[index].selected = TRUE;
1235 descr->selected_item = index;
1236 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1237 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1238 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1239 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1240 else
1241 if( descr->lphc ) /* set selection change flag for parent combo */
1242 descr->lphc->wState |= CBF_SELCHANGE;
1243 }
1244 return LB_OKAY;
1245 }
1246
1247
1248 /***********************************************************************
1249 * LISTBOX_MoveCaret
1250 *
1251 * Change the caret position and extend the selection to the new caret.
1252 */
1253 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1254 BOOL fully_visible )
1255 {
1256 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1257 if (descr->style & LBS_EXTENDEDSEL)
1258 {
1259 if (descr->anchor_item != -1)
1260 {
1261 INT first = MIN( descr->focus_item, descr->anchor_item );
1262 INT last = MAX( descr->focus_item, descr->anchor_item );
1263 if (first > 0)
1264 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1265 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1266 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1267 }
1268 }
1269 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1270 {
1271 /* Set selection to new caret item */
1272 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1273 }
1274 }
1275
1276
1277 /***********************************************************************
1278 * LISTBOX_InsertItem
1279 */
1280 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1281 LPSTR str, DWORD data )
1282 {
1283 LB_ITEMDATA *item;
1284 INT max_items;
1285
1286 if (index == -1) index = descr->nb_items;
1287 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1288 if (!descr->items) max_items = 0;
1289 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1290 if (descr->nb_items == max_items)
1291 {
1292 /* We need to grow the array */
1293 max_items += LB_ARRAY_GRANULARITY;
1294 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1295 max_items * sizeof(LB_ITEMDATA) )))
1296 {
1297 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1298 return LB_ERRSPACE;
1299 }
1300 descr->items = item;
1301 }
1302
1303 /* Insert the item structure */
1304
1305 item = &descr->items[index];
1306 if (index < descr->nb_items)
1307 MoveMemory( item + 1, item, (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1308 item->str = str;
1309 item->data = data;
1310 item->height = 0;
1311 item->selected = FALSE;
1312 descr->nb_items++;
1313
1314 /* Get item height */
1315
1316 if (descr->style & LBS_OWNERDRAWVARIABLE)
1317 {
1318 MEASUREITEMSTRUCT mis;
1319 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1320
1321 mis.CtlType = ODT_LISTBOX;
1322 mis.CtlID = id;
1323 mis.itemID = index;
1324 mis.itemData = descr->items[index].data;
1325 mis.itemHeight = descr->item_height;
1326 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1327 item->height = mis.itemHeight ? mis.itemHeight : 1;
1328 DPRINT( "[%04x]: measure item %d (%s) = %d\n",
1329 wnd->hwndSelf, index, str ? str : "", item->height );
1330 }
1331
1332 /* Repaint the items */
1333
1334 LISTBOX_UpdateScroll( wnd, descr );
1335 LISTBOX_InvalidateItems( wnd, descr, index );
1336
1337 /* Move selection and focused item */
1338
1339 if (index <= descr->selected_item) descr->selected_item++;
1340 if (index <= descr->focus_item)
1341 {
1342 descr->focus_item++;
1343 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1344 }
1345
1346 /* If listbox was empty, set focus to the first item */
1347
1348 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1349 return LB_OKAY;
1350 }
1351
1352
1353 /***********************************************************************
1354 * LISTBOX_InsertString
1355 */
1356 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1357 LPCSTR str )
1358 {
1359 LPSTR new_str = NULL;
1360 DWORD data = 0;
1361 LRESULT ret;
1362
1363 if (HAS_STRINGS(descr))
1364 {
1365 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1366 {
1367 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1368 return LB_ERRSPACE;
1369 }
1370 }
1371 else data = (DWORD)str;
1372
1373 if (index == -1) index = descr->nb_items;
1374 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1375 {
1376 if (new_str) HeapFree( descr->heap, 0, new_str );
1377 return ret;
1378 }
1379
1380 DPRINT( "[%04x]: added item %d '%s'\n",
1381 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1382 return index;
1383 }
1384
1385
1386 /***********************************************************************
1387 * LISTBOX_DeleteItem
1388 *
1389 * Delete the content of an item. 'index' must be a valid index.
1390 */
1391 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1392 {
1393 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1394 * while Win95 sends it for all items with user data.
1395 * It's probably better to send it too often than not
1396 * often enough, so this is what we do here.
1397 */
1398 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1399 {
1400 DELETEITEMSTRUCT dis;
1401 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1402
1403 dis.CtlType = ODT_LISTBOX;
1404 dis.CtlID = id;
1405 dis.itemID = index;
1406 dis.hwndItem = wnd->hwndSelf;
1407 dis.itemData = descr->items[index].data;
1408 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1409 }
1410 if (HAS_STRINGS(descr) && descr->items[index].str)
1411 HeapFree( descr->heap, 0, descr->items[index].str );
1412 }
1413
1414
1415 /***********************************************************************
1416 * LISTBOX_RemoveItem
1417 *
1418 * Remove an item from the listbox and delete its content.
1419 */
1420 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1421 {
1422 LB_ITEMDATA *item;
1423 INT max_items;
1424
1425 if (index == -1) index = descr->nb_items - 1;
1426 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1427 LISTBOX_DeleteItem( wnd, descr, index );
1428
1429 /* Remove the item */
1430
1431 item = &descr->items[index];
1432 if (index < descr->nb_items-1)
1433 MoveMemory( item, item + 1,
1434 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1435 descr->nb_items--;
1436 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1437
1438 /* Shrink the item array if possible */
1439
1440 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1441 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1442 {
1443 max_items -= LB_ARRAY_GRANULARITY;
1444 item = HeapReAlloc( descr->heap, 0, descr->items,
1445 max_items * sizeof(LB_ITEMDATA) );
1446 if (item) descr->items = item;
1447 }
1448
1449 /* Repaint the items */
1450
1451 LISTBOX_UpdateScroll( wnd, descr );
1452 LISTBOX_InvalidateItems( wnd, descr, index );
1453
1454 /* Move selection and focused item */
1455
1456 if (index <= descr->selected_item) descr->selected_item--;
1457 if (index <= descr->focus_item)
1458 {
1459 descr->focus_item--;
1460 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1461 }
1462 return LB_OKAY;
1463 }
1464
1465
1466 /***********************************************************************
1467 * LISTBOX_ResetContent
1468 */
1469 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1470 {
1471 INT i;
1472
1473 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1474 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1475 descr->nb_items = 0;
1476 descr->top_item = 0;
1477 descr->selected_item = -1;
1478 descr->focus_item = 0;
1479 descr->anchor_item = -1;
1480 descr->items = NULL;
1481 LISTBOX_UpdateScroll( wnd, descr );
1482 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1483 }
1484
1485
1486 /***********************************************************************
1487 * LISTBOX_SetCount
1488 */
1489 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1490 {
1491 LRESULT ret;
1492
1493 if (HAS_STRINGS(descr)) return LB_ERR;
1494 /* FIXME: this is far from optimal... */
1495 if (count > descr->nb_items)
1496 {
1497 while (count > descr->nb_items)
1498 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1499 return ret;
1500 }
1501 else if (count < descr->nb_items)
1502 {
1503 while (count < descr->nb_items)
1504 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1505 return ret;
1506 }
1507 return LB_OKAY;
1508 }
1509
1510
1511 /***********************************************************************
1512 * LISTBOX_Directory
1513 */
1514 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1515 LPCSTR filespec, BOOL long_names )
1516 {
1517 HANDLE handle;
1518 LRESULT ret = LB_OKAY;
1519 WIN32_FIND_DATA entry;
1520 int pos;
1521
1522 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1523 {
1524 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1525 }
1526 else
1527 {
1528 do
1529 {
1530 char buffer[270];
1531 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1532 {
1533 if (!(attrib & DDL_DIRECTORY) ||
1534 !strcmp( entry.cAlternateFileName, "." )) continue;
1535 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1536 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1537 }
1538 else /* not a directory */
1539 {
1540 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1541 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1542
1543 if ((attrib & DDL_EXCLUSIVE) &&
1544 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1545 continue;
1546 #undef ATTRIBS
1547 if (long_names) strcpy( buffer, entry.cFileName );
1548 else strcpy( buffer, entry.cAlternateFileName );
1549 }
1550 if (!long_names) CharLowerA( buffer );
1551 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1552 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1553 break;
1554 } while (FindNextFileA( handle, &entry ));
1555 FindClose( handle );
1556 }
1557
1558 if ((ret >= 0) && (attrib & DDL_DRIVES))
1559 {
1560 char buffer[] = "[-a-]";
1561 int drive;
1562 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1563 {
1564 //if (!DRIVE_IsValid(drive)) continue;
1565 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1566 break;
1567 }
1568 }
1569 return ret;
1570 }
1571
1572
1573 /***********************************************************************
1574 * LISTBOX_HandleVScroll
1575 */
1576 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1577 WPARAM wParam, LPARAM lParam )
1578 {
1579 SCROLLINFO info;
1580
1581 if (descr->style & LBS_MULTICOLUMN) return 0;
1582 switch(LOWORD(wParam))
1583 {
1584 case SB_LINEUP:
1585 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1586 break;
1587 case SB_LINEDOWN:
1588 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1589 break;
1590 case SB_PAGEUP:
1591 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1592 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1593 break;
1594 case SB_PAGEDOWN:
1595 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1596 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1597 break;
1598 case SB_THUMBPOSITION:
1599 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1600 break;
1601 case SB_THUMBTRACK:
1602 info.cbSize = sizeof(info);
1603 info.fMask = SIF_TRACKPOS;
1604 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1605 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1606 break;
1607 case SB_TOP:
1608 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1609 break;
1610 case SB_BOTTOM:
1611 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1612 break;
1613 }
1614 return 0;
1615 }
1616
1617
1618 /***********************************************************************
1619 * LISTBOX_HandleHScroll
1620 */
1621 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1622 WPARAM wParam, LPARAM lParam )
1623 {
1624 SCROLLINFO info;
1625 INT page;
1626
1627 if (descr->style & LBS_MULTICOLUMN)
1628 {
1629 switch(LOWORD(wParam))
1630 {
1631 case SB_LINELEFT:
1632 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1633 TRUE );
1634 break;
1635 case SB_LINERIGHT:
1636 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1637 TRUE );
1638 break;
1639 case SB_PAGELEFT:
1640 page = descr->width / descr->column_width;
1641 if (page < 1) page = 1;
1642 LISTBOX_SetTopItem( wnd, descr,
1643 descr->top_item - page * descr->page_size, TRUE );
1644 break;
1645 case SB_PAGERIGHT:
1646 page = descr->width / descr->column_width;
1647 if (page < 1) page = 1;
1648 LISTBOX_SetTopItem( wnd, descr,
1649 descr->top_item + page * descr->page_size, TRUE );
1650 break;
1651 case SB_THUMBPOSITION:
1652 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1653 TRUE );
1654 break;
1655 case SB_THUMBTRACK:
1656 info.cbSize = sizeof(info);
1657 info.fMask = SIF_TRACKPOS;
1658 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1659 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1660 TRUE );
1661 break;
1662 case SB_LEFT:
1663 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1664 break;
1665 case SB_RIGHT:
1666 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1667 break;
1668 }
1669 }
1670 else if (descr->horz_extent)
1671 {
1672 switch(LOWORD(wParam))
1673 {
1674 case SB_LINELEFT:
1675 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1676 break;
1677 case SB_LINERIGHT:
1678 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1679 break;
1680 case SB_PAGELEFT:
1681 LISTBOX_SetHorizontalPos( wnd, descr,
1682 descr->horz_pos - descr->width );
1683 break;
1684 case SB_PAGERIGHT:
1685 LISTBOX_SetHorizontalPos( wnd, descr,
1686 descr->horz_pos + descr->width );
1687 break;
1688 case SB_THUMBPOSITION:
1689 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1690 break;
1691 case SB_THUMBTRACK:
1692 info.cbSize = sizeof(info);
1693 info.fMask = SIF_TRACKPOS;
1694 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1695 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1696 break;
1697 case SB_LEFT:
1698 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1699 break;
1700 case SB_RIGHT:
1701 LISTBOX_SetHorizontalPos( wnd, descr,
1702 descr->horz_extent - descr->width );
1703 break;
1704 }
1705 }
1706 return 0;
1707 }
1708
1709
1710 /***********************************************************************
1711 * LISTBOX_HandleLButtonDown
1712 */
1713 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1714 WPARAM wParam, INT x, INT y )
1715 {
1716 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1717 DPRINT( "[%04x]: lbuttondown %d,%d item %d\n",
1718 wnd->hwndSelf, x, y, index );
1719 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1720 if (index != -1)
1721 {
1722 if (descr->style & LBS_EXTENDEDSEL)
1723 {
1724 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1725 if (wParam & MK_CONTROL)
1726 {
1727 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1728 LISTBOX_SetSelection( wnd, descr, index,
1729 !descr->items[index].selected, FALSE );
1730 }
1731 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1732 }
1733 else
1734 {
1735 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1736 LISTBOX_SetSelection( wnd, descr, index,
1737 (!(descr->style & LBS_MULTIPLESEL) ||
1738 !descr->items[index].selected), FALSE );
1739 }
1740 }
1741
1742 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1743 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1744 : descr->lphc->self->hwndSelf ) ;
1745
1746 SetCapture( wnd->hwndSelf );
1747 if (index != -1 && !descr->lphc)
1748 {
1749 if (descr->style & LBS_NOTIFY )
1750 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1751 MAKELPARAM( x, y ) );
1752 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1753 {
1754 POINT pt = { x, y };
1755 // if (DragDetect( wnd->hwndSelf, pt ))
1756 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1757 }
1758 }
1759 return 0;
1760 }
1761
1762
1763 /***********************************************************************
1764 * LISTBOX_HandleLButtonUp
1765 */
1766 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1767 {
1768 if (LISTBOX_Timer != LB_TIMER_NONE)
1769 KillTimer( wnd->hwndSelf, LB_TIMER_ID );
1770 LISTBOX_Timer = LB_TIMER_NONE;
1771 if (GetCapture() == wnd->hwndSelf)
1772 {
1773 ReleaseCapture();
1774 if (descr->style & LBS_NOTIFY)
1775 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1776 }
1777 return 0;
1778 }
1779
1780
1781 /***********************************************************************
1782 * LISTBOX_HandleTimer
1783 *
1784 * Handle scrolling upon a timer event.
1785 * Return TRUE if scrolling should continue.
1786 */
1787 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1788 INT index, TIMER_DIRECTION dir )
1789 {
1790 switch(dir)
1791 {
1792 case LB_TIMER_UP:
1793 if (descr->top_item) index = descr->top_item - 1;
1794 else index = 0;
1795 break;
1796 case LB_TIMER_LEFT:
1797 if (descr->top_item) index -= descr->page_size;
1798 break;
1799 case LB_TIMER_DOWN:
1800 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1801 if (index == descr->focus_item) index++;
1802 if (index >= descr->nb_items) index = descr->nb_items - 1;
1803 break;
1804 case LB_TIMER_RIGHT:
1805 if (index + descr->page_size < descr->nb_items)
1806 index += descr->page_size;
1807 break;
1808 case LB_TIMER_NONE:
1809 break;
1810 }
1811 if (index == descr->focus_item) return FALSE;
1812 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1813 return TRUE;
1814 }
1815
1816
1817 /***********************************************************************
1818 * LISTBOX_HandleSystemTimer
1819 *
1820 * WM_SYSTIMER handler.
1821 */
1822 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1823 {
1824 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1825 {
1826 KillTimer( wnd->hwndSelf, LB_TIMER_ID );
1827 LISTBOX_Timer = LB_TIMER_NONE;
1828 }
1829 return 0;
1830 }
1831
1832
1833 /***********************************************************************
1834 * LISTBOX_HandleMouseMove
1835 *
1836 * WM_MOUSEMOVE handler.
1837 */
1838 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1839 INT x, INT y )
1840 {
1841 INT index;
1842 TIMER_DIRECTION dir;
1843
1844 if (descr->style & LBS_MULTICOLUMN)
1845 {
1846 if (y < 0) y = 0;
1847 else if (y >= descr->item_height * descr->page_size)
1848 y = descr->item_height * descr->page_size - 1;
1849
1850 if (x < 0)
1851 {
1852 dir = LB_TIMER_LEFT;
1853 x = 0;
1854 }
1855 else if (x >= descr->width)
1856 {
1857 dir = LB_TIMER_RIGHT;
1858 x = descr->width - 1;
1859 }
1860 else dir = LB_TIMER_NONE; /* inside */
1861 }
1862 else
1863 {
1864 if (y < 0) dir = LB_TIMER_UP; /* above */
1865 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1866 else dir = LB_TIMER_NONE; /* inside */
1867 }
1868
1869 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1870 if (index == -1) index = descr->focus_item;
1871 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1872
1873 /* Start/stop the system timer */
1874
1875 if (dir != LB_TIMER_NONE)
1876 SetTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1877 else if (LISTBOX_Timer != LB_TIMER_NONE)
1878 KillTimer( wnd->hwndSelf, LB_TIMER_ID );
1879 LISTBOX_Timer = dir;
1880 }
1881
1882
1883 /***********************************************************************
1884 * LISTBOX_HandleKeyDown
1885 */
1886 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
1887 {
1888 INT caret = -1;
1889 if (descr->style & LBS_WANTKEYBOARDINPUT)
1890 {
1891 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
1892 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1893 wnd->hwndSelf );
1894 if (caret == -2) return 0;
1895 }
1896 if (caret == -1) switch(wParam)
1897 {
1898 case VK_LEFT:
1899 if (descr->style & LBS_MULTICOLUMN)
1900 {
1901 if (descr->focus_item >= descr->page_size)
1902 caret = descr->focus_item - descr->page_size;
1903 break;
1904 }
1905 /* fall through */
1906 case VK_UP:
1907 caret = descr->focus_item - 1;
1908 if (caret < 0) caret = 0;
1909 break;
1910 case VK_RIGHT:
1911 if (descr->style & LBS_MULTICOLUMN)
1912 {
1913 if (descr->focus_item + descr->page_size < descr->nb_items)
1914 caret = descr->focus_item + descr->page_size;
1915 break;
1916 }
1917 /* fall through */
1918 case VK_DOWN:
1919 caret = descr->focus_item + 1;
1920 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1921 break;
1922 case VK_PRIOR:
1923 if (descr->style & LBS_MULTICOLUMN)
1924 {
1925 INT page = descr->width / descr->column_width;
1926 if (page < 1) page = 1;
1927 caret = descr->focus_item - (page * descr->page_size) + 1;
1928 }
1929 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1930 if (caret < 0) caret = 0;
1931 break;
1932 case VK_NEXT:
1933 if (descr->style & LBS_MULTICOLUMN)
1934 {
1935 INT page = descr->width / descr->column_width;
1936 if (page < 1) page = 1;
1937 caret = descr->focus_item + (page * descr->page_size) - 1;
1938 }
1939 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1940 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1941 break;
1942 case VK_HOME:
1943 caret = 0;
1944 break;
1945 case VK_END:
1946 caret = descr->nb_items - 1;
1947 break;
1948 case VK_SPACE:
1949 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1950 else if (descr->style & LBS_MULTIPLESEL)
1951 {
1952 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1953 !descr->items[descr->focus_item].selected,
1954 (descr->style & LBS_NOTIFY) != 0 );
1955 }
1956 else if (descr->selected_item == -1)
1957 {
1958 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1959 (descr->style & LBS_NOTIFY) != 0 );
1960 }
1961 break;
1962 }
1963 if (caret >= 0)
1964 {
1965 if ((descr->style & LBS_EXTENDEDSEL) &&
1966 !(GetKeyState( VK_SHIFT ) & 0x8000))
1967 descr->anchor_item = caret;
1968 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1969 if (descr->style & LBS_NOTIFY)
1970 {
1971 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1972 {
1973 /* make sure that combo parent doesn't hide us */
1974 descr->lphc->wState |= CBF_NOROLLUP;
1975 }
1976 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1977 }
1978 }
1979 return 0;
1980 }
1981
1982
1983 /***********************************************************************
1984 * LISTBOX_HandleChar
1985 */
1986 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1987 WPARAM wParam )
1988 {
1989 INT caret = -1;
1990 char str[2] = { wParam & 0xff, '\0' };
1991
1992 if (descr->style & LBS_WANTKEYBOARDINPUT)
1993 {
1994 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
1995 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1996 wnd->hwndSelf );
1997 if (caret == -2) return 0;
1998 }
1999 if (caret == -1)
2000 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2001 if (caret != -1)
2002 {
2003 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2004 if (descr->style & LBS_NOTIFY)
2005 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2006 }
2007 return 0;
2008 }
2009
2010
2011 /***********************************************************************
2012 * LISTBOX_Create
2013 */
2014 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2015 {
2016 LB_DESCR *descr;
2017 MEASUREITEMSTRUCT mis;
2018 RECT rect;
2019
2020 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2021 return FALSE;
2022 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2023 {
2024 HeapFree( GetProcessHeap(), 0, descr );
2025 return FALSE;
2026 }
2027 GetClientRect( wnd->hwndSelf, &rect );
2028 descr->owner = GetParent( wnd->hwndSelf );
2029 descr->style = wnd->dwStyle;
2030 descr->width = rect.right - rect.left;
2031 descr->height = rect.bottom - rect.top;
2032 descr->items = NULL;
2033 descr->nb_items = 0;
2034 descr->top_item = 0;
2035 descr->selected_item = -1;
2036 descr->focus_item = 0;
2037 descr->anchor_item = -1;
2038 descr->item_height = 1;
2039 descr->page_size = 1;
2040 descr->column_width = 150;
2041 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2042 descr->horz_pos = 0;
2043 descr->nb_tabs = 0;
2044 descr->tabs = NULL;
2045 descr->caret_on = TRUE;
2046 descr->font = 0;
2047 descr->locale = 0; /* FIXME */
2048 descr->lphc = lphc;
2049
2050 if( lphc )
2051 {
2052 DPRINT("[%04x]: resetting owner %04x -> %04x\n",
2053 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2054 descr->owner = lphc->self->hwndSelf;
2055 }
2056
2057 *(LB_DESCR **)wnd->wExtra = descr;
2058
2059 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2060 */
2061 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2062 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2063 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2064 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2065
2066 if (descr->style & LBS_OWNERDRAWFIXED)
2067 {
2068 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2069 {
2070 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2071 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2072 }
2073 else
2074 {
2075 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2076
2077 mis.CtlType = ODT_LISTBOX;
2078 mis.CtlID = id;
2079 mis.itemID = -1;
2080 mis.itemWidth = 0;
2081 mis.itemData = 0;
2082 mis.itemHeight = descr->item_height;
2083 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2084 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2085 }
2086 }
2087
2088 return TRUE;
2089 }
2090
2091
2092 /***********************************************************************
2093 * LISTBOX_Destroy
2094 */
2095 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2096 {
2097 LISTBOX_ResetContent( wnd, descr );
2098 HeapDestroy( descr->heap );
2099 HeapFree( GetProcessHeap(), 0, descr );
2100 wnd->wExtra[0] = 0;
2101 return TRUE;
2102 }
2103
2104
2105 /***********************************************************************
2106 * ListBoxWndProc
2107 */
2108 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2109 WPARAM wParam, LPARAM lParam )
2110 {
2111 LRESULT ret;
2112 LB_DESCR *descr;
2113 WND *wnd = WIN_FindWndPtr( hwnd );
2114
2115 if (!wnd) return 0;
2116 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2117 {
2118 if (msg == WM_CREATE)
2119 {
2120 if (!LISTBOX_Create( wnd, NULL )) return -1;
2121 DPRINT( "creating wnd=%04x descr=%p\n",
2122 hwnd, *(LB_DESCR **)wnd->wExtra );
2123 return 0;
2124 }
2125 /* Ignore all other messages before we get a WM_CREATE */
2126 return DefWindowProcA( hwnd, msg, wParam, lParam );
2127 }
2128
2129 DPRINT( "[%04x]: msg %s wp %08x lp %08lx\n",
2130 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2131 switch(msg)
2132 {
2133 case LB_RESETCONTENT:
2134 LISTBOX_ResetContent( wnd, descr );
2135 return 0;
2136
2137 case LB_ADDSTRING:
2138 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2139 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2140
2141 case LB_INSERTSTRING:
2142 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2143
2144
2145 case LB_ADDFILE:
2146 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2147 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2148
2149 case LB_DELETESTRING:
2150 return LISTBOX_RemoveItem( wnd, descr, wParam );
2151
2152 case LB_GETITEMDATA:
2153 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2154 return LB_ERR;
2155 return descr->items[wParam].data;
2156
2157 case LB_SETITEMDATA:
2158 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2159 return LB_ERR;
2160 descr->items[wParam].data = (DWORD)lParam;
2161 return LB_OKAY;
2162
2163 case LB_GETCOUNT:
2164 return descr->nb_items;
2165
2166 case LB_GETTEXT:
2167 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2168
2169 case LB_GETTEXTLEN:
2170 if (wParam >= descr->nb_items) return LB_ERR;
2171 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2172 : sizeof(DWORD));
2173
2174
2175 case LB_GETCURSEL:
2176 return descr->selected_item;
2177
2178
2179 case LB_GETTOPINDEX:
2180 return descr->top_item;
2181
2182 case LB_GETITEMHEIGHT:
2183 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2184
2185
2186 case LB_SETITEMHEIGHT:
2187 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2188
2189 case LB_ITEMFROMPOINT:
2190 {
2191 POINT pt = { LOWORD(lParam), HIWORD(lParam) };
2192 RECT rect = { 0, 0, descr->width, descr->height };
2193 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2194 PtInRect( &rect, pt ) );
2195 }
2196
2197
2198 case LB_SETCARETINDEX:
2199 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2200
2201
2202 case LB_GETCARETINDEX:
2203 return descr->focus_item;
2204
2205
2206 case LB_SETTOPINDEX:
2207 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2208
2209
2210 case LB_SETCOLUMNWIDTH:
2211 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2212
2213
2214
2215 case LB_GETITEMRECT:
2216 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2217
2218
2219 case LB_FINDSTRING:
2220 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2221
2222
2223 case LB_FINDSTRINGEXACT:
2224 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2225
2226
2227 case LB_SELECTSTRING:
2228 {
2229 INT index = LISTBOX_FindString( wnd, descr, wParam,
2230 (LPCSTR)lParam, FALSE );
2231 if (index == LB_ERR) return LB_ERR;
2232 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2233 return index;
2234 }
2235
2236
2237 case LB_GETSEL:
2238 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2239 return LB_ERR;
2240 return descr->items[wParam].selected;
2241
2242 case LB_SETSEL:
2243 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2244
2245
2246 case LB_SETCURSEL:
2247 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2248 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2249
2250 case LB_GETSELCOUNT:
2251 return LISTBOX_GetSelCount( wnd, descr );
2252
2253
2254 case LB_GETSELITEMS:
2255 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2256
2257 case LB_SELITEMRANGE:
2258 if (LOWORD(lParam) <= HIWORD(lParam))
2259 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2260 HIWORD(lParam), wParam );
2261 else
2262 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2263 LOWORD(lParam), wParam );
2264
2265
2266 case LB_SELITEMRANGEEX:
2267 if ((INT)lParam >= (INT)wParam)
2268 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2269 else
2270 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2271
2272
2273 case LB_GETHORIZONTALEXTENT:
2274 return descr->horz_extent;
2275
2276
2277 case LB_SETHORIZONTALEXTENT:
2278 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2279
2280 case LB_GETANCHORINDEX:
2281 return descr->anchor_item;
2282
2283
2284 case LB_SETANCHORINDEX:
2285 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2286 return LB_ERR;
2287 descr->anchor_item = (INT)wParam;
2288 return LB_OKAY;
2289
2290
2291
2292 case LB_DIR:
2293 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2294
2295 case LB_GETLOCALE:
2296 return descr->locale;
2297
2298 case LB_SETLOCALE:
2299 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2300 return LB_OKAY;
2301
2302 case LB_INITSTORAGE:
2303 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2304
2305 case LB_SETCOUNT:
2306 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2307
2308
2309
2310 case LB_SETTABSTOPS:
2311 return LISTBOX_SetTabStops( wnd, descr, wParam,
2312 (LPINT)lParam, FALSE );
2313
2314
2315 case LB_CARETON:
2316 if (descr->caret_on) return LB_OKAY;
2317 descr->caret_on = TRUE;
2318 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2319 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2320 return LB_OKAY;
2321
2322
2323 case LB_CARETOFF:
2324 if (!descr->caret_on) return LB_OKAY;
2325 descr->caret_on = FALSE;
2326 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2327 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2328 return LB_OKAY;
2329
2330 case WM_DESTROY:
2331 return LISTBOX_Destroy( wnd, descr );
2332
2333 case WM_ENABLE:
2334 InvalidateRect( hwnd, NULL, TRUE );
2335 return 0;
2336
2337 case WM_SETREDRAW:
2338 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2339 return 0;
2340
2341 case WM_GETDLGCODE:
2342 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2343
2344 case WM_PAINT:
2345 {
2346 PAINTSTRUCT ps;
2347 HDC hdc = ( wParam ) ? ((HDC)wParam)
2348 : BeginPaint( hwnd, &ps );
2349 ret = LISTBOX_Paint( wnd, descr, hdc );
2350 if( !wParam ) EndPaint( hwnd, &ps );
2351 }
2352 return ret;
2353
2354 case WM_SIZE:
2355 LISTBOX_UpdateSize( wnd, descr );
2356 return 0;
2357
2358 case WM_GETFONT:
2359 return descr->font;
2360
2361 case WM_SETFONT:
2362 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2363 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2364 return 0;
2365
2366 case WM_SETFOCUS:
2367 descr->caret_on = TRUE;
2368 if (descr->focus_item != -1)
2369 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2370 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2371 return 0;
2372
2373 case WM_KILLFOCUS:
2374 if ((descr->focus_item != -1) && descr->caret_on)
2375 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2376 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2377 return 0;
2378
2379 case WM_HSCROLL:
2380 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2381
2382 case WM_VSCROLL:
2383 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2384
2385 case WM_LBUTTONDOWN:
2386 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2387 (INT)LOWORD(lParam),
2388 (INT)HIWORD(lParam) );
2389
2390 case WM_LBUTTONDBLCLK:
2391 if (descr->style & LBS_NOTIFY)
2392 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2393 return 0;
2394
2395 case WM_MOUSEMOVE:
2396 if (GetCapture() == hwnd)
2397 LISTBOX_HandleMouseMove( wnd, descr, (INT)LOWORD(lParam),
2398 (INT)HIWORD(lParam) );
2399 return 0;
2400
2401 case WM_LBUTTONUP:
2402 return LISTBOX_HandleLButtonUp( wnd, descr );
2403
2404 case WM_KEYDOWN:
2405 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2406
2407 case WM_CHAR:
2408 return LISTBOX_HandleChar( wnd, descr, wParam );
2409
2410 case WM_SYSTIMER:
2411 return LISTBOX_HandleSystemTimer( wnd, descr );
2412
2413 case WM_ERASEBKGND:
2414 if (IS_OWNERDRAW(descr))
2415 {
2416 RECT rect = { 0, 0, descr->width, descr->height };
2417 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2418 wParam, (LPARAM)wnd->hwndSelf );
2419 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2420 }
2421 return 1;
2422
2423 case WM_DROPFILES:
2424 if( !descr->lphc )
2425 return SendMessageA( descr->owner, msg, wParam, lParam );
2426 break;
2427
2428 case WM_DROPOBJECT:
2429 case WM_QUERYDROPOBJECT:
2430 case WM_DRAGSELECT:
2431 case WM_DRAGMOVE:
2432 if( !descr->lphc )
2433 {
2434 // LPDRAGINFO dragInfo = (LPDRAGINFO)lParam;
2435 // dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2436 // dragInfo->pt.y );
2437 return SendMessageA( descr->owner, msg, wParam, lParam );
2438 }
2439 break;
2440
2441 case WM_NCCREATE:
2442 if (TWEAK_WineLook > WIN31_LOOK)
2443 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2444 return DefWindowProcA( hwnd, msg, wParam, lParam );
2445
2446 default:
2447 if ((msg >= WM_USER) && (msg < 0xc000))
2448 DPRINT( "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2449 hwnd, msg, wParam, lParam );
2450 return DefWindowProcA( hwnd, msg, wParam, lParam );
2451 }
2452 return 0;
2453 }
2454
2455 /***********************************************************************
2456 * COMBO_Directory
2457 */
2458 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2459 {
2460 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2461
2462 if( wnd )
2463 {
2464 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2465 if( descr )
2466 {
2467 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2468
2469 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2470 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2471 return lRet;
2472 }
2473 }
2474 return CB_ERR;
2475 }
2476
2477 /***********************************************************************
2478 * ComboLBWndProc
2479 *
2480 * NOTE: in Windows, winproc address of the ComboLBox is the same
2481 * as that of the Listbox.
2482 */
2483 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2484 WPARAM wParam, LPARAM lParam )
2485 {
2486 LRESULT lRet = 0;
2487 WND *wnd = WIN_FindWndPtr( hwnd );
2488
2489 if (wnd)
2490 {
2491 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2492
2493 DPRINT( "[%04x]: msg %s wp %08x lp %08lx\n",
2494 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2495
2496 if( descr || msg == WM_CREATE )
2497 {
2498 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2499
2500 switch( msg )
2501 {
2502 case WM_CREATE:
2503 #define lpcs ((LPCREATESTRUCT)lParam)
2504 DPRINT( "\tpassed parent handle = 0x%08x\n",
2505 (UINT)lpcs->lpCreateParams);
2506
2507 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2508 #undef lpcs
2509 return LISTBOX_Create( wnd, lphc );
2510
2511 case WM_LBUTTONDOWN:
2512 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2513 (INT)LOWORD(lParam), (INT)HIWORD(lParam));
2514
2515 /* avoid activation at all costs */
2516
2517 case WM_MOUSEACTIVATE:
2518 return MA_NOACTIVATE;
2519
2520 case WM_NCACTIVATE:
2521 return FALSE;
2522
2523 case WM_KEYDOWN:
2524 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2525 {
2526 /* for some reason(?) Windows makes it possible to
2527 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2528
2529 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2530 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2531 && (wParam == VK_DOWN || wParam == VK_UP)) )
2532 {
2533 COMBO_FlipListbox( lphc, FALSE );
2534 return 0;
2535 }
2536 }
2537 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2538
2539 case LB_SETCURSEL:
2540 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2541 return (lRet == LB_ERR) ? lRet : descr->selected_item;
2542
2543 case WM_NCDESTROY:
2544 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2545 lphc->hWndLBox = 0;
2546 /* fall through */
2547
2548 default:
2549 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2550 }
2551 }
2552 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2553
2554 DPRINT("\t default on msg [%04x]\n", (UINT)msg );
2555 }
2556
2557 return lRet;
2558 }
2559