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