Release 961222
[wine/gsoc-2012-control.git] / controls / listbox.c
blobceed8b2fd78cc4beab742c7be9e3cabb57678d56
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #define NO_TRANSITION_TYPES /* This file is Win32-clean */
8 #include <string.h>
9 #include <stdio.h>
10 #include "windows.h"
11 #include "drive.h"
12 #include "dos_fs.h"
13 #include "msdos.h"
14 #include "heap.h"
15 #include "spy.h"
16 #include "win.h"
17 #include "stddebug.h"
18 #include "debug.h"
20 /* Unimplemented yet:
21 * - LBS_NOSEL
22 * - LBS_USETABSTOPS
23 * - Unicode
24 * - Locale handling
25 * - COMBOLBOX
28 /* Items array granularity */
29 #define LB_ARRAY_GRANULARITY 16
31 /* Scrolling timeout in ms */
32 #define LB_SCROLL_TIMEOUT 50
34 /* Listbox system timer id */
35 #define LB_TIMER_ID 2
37 /* Item structure */
38 typedef struct
40 LPSTR str; /* Item text */
41 BOOL32 selected; /* Is item selected? */
42 UINT32 height; /* Item height (only for OWNERDRAWVARIABLE) */
43 DWORD data; /* User data */
44 } LB_ITEMDATA;
46 /* Listbox structure */
47 typedef struct
49 HANDLE32 heap; /* Heap for this listbox */
50 HWND32 owner; /* Owner window */
51 UINT32 style; /* Window style */
52 INT32 width; /* Window width */
53 INT32 height; /* Window height */
54 LB_ITEMDATA *items; /* Array of items */
55 INT32 nb_items; /* Number of items */
56 INT32 top_item; /* Top visible item */
57 INT32 selected_item; /* Selected item */
58 INT32 focus_item; /* Item that has the focus */
59 INT32 anchor_item; /* Anchor item for extended selection */
60 INT32 item_height; /* Default item height */
61 INT32 page_size; /* Items per listbox page */
62 INT32 column_width; /* Column width for multi-column listboxes */
63 INT32 horz_extent; /* Horizontal extent (0 if no hscroll) */
64 INT32 horz_pos; /* Horizontal position */
65 INT32 nb_tabs; /* Number of tabs in array */
66 INT32 *tabs; /* Array of tabs */
67 BOOL32 caret_on; /* Is caret on? */
68 HFONT32 font; /* Current font */
69 LCID locale; /* Current locale for string comparisons */
70 } LB_DESCR;
73 #define IS_OWNERDRAW(descr) \
74 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
76 #define HAS_STRINGS(descr) \
77 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
79 #define SEND_NOTIFICATION(wnd,descr,code) \
80 (SendMessage32A( (descr)->owner, WM_COMMAND, \
81 MAKEWPARAM( (wnd)->wIDmenu, (code) ), (wnd)->hwndSelf ))
83 /* Current timer status */
84 typedef enum
86 LB_TIMER_NONE,
87 LB_TIMER_UP,
88 LB_TIMER_LEFT,
89 LB_TIMER_DOWN,
90 LB_TIMER_RIGHT
91 } TIMER_DIRECTION;
93 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
96 /***********************************************************************
97 * LISTBOX_Dump
99 void LISTBOX_Dump( WND *wnd )
101 INT32 i;
102 LB_ITEMDATA *item;
103 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
105 printf( "Listbox:\n" );
106 printf( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
107 wnd->hwndSelf, (UINT32)descr, descr->heap, descr->nb_items,
108 descr->top_item );
109 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
111 printf( "%4d: %-40s %d %08lx %3d\n",
112 i, item->str, item->selected, item->data, item->height );
117 /***********************************************************************
118 * LISTBOX_GetCurrentPageSize
120 * Return the current page size
122 static INT32 LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
124 INT32 i, height;
125 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
126 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
128 if ((height += descr->items[i].height) > descr->height) break;
130 if (i == descr->top_item) return 1;
131 else return i - descr->top_item;
135 /***********************************************************************
136 * LISTBOX_GetMaxTopIndex
138 * Return the maximum possible index for the top of the listbox.
140 static INT32 LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
142 INT32 max, page;
144 if (descr->style & LBS_OWNERDRAWVARIABLE)
146 page = descr->height;
147 for (max = descr->nb_items - 1; max >= 0; max--)
148 if ((page -= descr->items[max].height) < 0) break;
149 if (max < descr->nb_items - 1) max++;
151 else if (descr->style & LBS_MULTICOLUMN)
153 if ((page = descr->width / descr->column_width) < 1) page = 1;
154 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
155 max = (max - page) * descr->page_size;
157 else
159 max = descr->nb_items - descr->page_size;
161 if (max < 0) max = 0;
162 return max;
166 /***********************************************************************
167 * LISTBOX_UpdateScroll
169 * Update the scrollbars. Should be called whenever the content
170 * of the listbox changes.
172 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
174 SCROLLINFO info;
176 if (descr->style & LBS_NOREDRAW) return;
177 info.cbSize = sizeof(info);
179 if (descr->style & LBS_MULTICOLUMN)
181 info.nMin = 0;
182 info.nMax = (descr->nb_items - 1) / descr->page_size;
183 info.nPos = descr->top_item / descr->page_size;
184 info.nPage = descr->width / descr->column_width;
185 if (info.nPage < 1) info.nPage = 1;
186 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
187 if (descr->style & LBS_DISABLENOSCROLL)
188 info.fMask |= SIF_DISABLENOSCROLL;
189 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
190 info.nMax = 0;
191 info.fMask = SIF_RANGE;
192 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
194 else
196 info.nMin = 0;
197 info.nMax = descr->nb_items - 1;
198 info.nPos = descr->top_item;
199 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
200 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
201 if (descr->style & LBS_DISABLENOSCROLL)
202 info.fMask |= SIF_DISABLENOSCROLL;
203 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
205 if (descr->horz_extent)
207 info.nMin = 0;
208 info.nMax = descr->horz_extent - 1;
209 info.nPos = descr->horz_pos;
210 info.nPage = descr->width;
211 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
212 if (descr->style & LBS_DISABLENOSCROLL)
213 info.fMask |= SIF_DISABLENOSCROLL;
214 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
220 /***********************************************************************
221 * LISTBOX_SetTopItem
223 * Set the top item of the listbox, scrolling up or down if necessary.
225 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index,
226 BOOL32 scroll )
228 INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr );
229 if (index > max) index = max;
230 if (index < 0) index = 0;
231 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
232 if (descr->top_item == index) return LB_OKAY;
233 if (descr->style & LBS_MULTICOLUMN)
235 INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width;
236 if (scroll && (abs(diff) < descr->width))
237 ScrollWindow32( wnd->hwndSelf, diff, 0, NULL, NULL );
238 else
239 scroll = FALSE;
241 else if (scroll)
243 INT32 diff;
244 if (descr->style & LBS_OWNERDRAWVARIABLE)
246 INT32 i;
247 diff = 0;
248 if (index > descr->top_item)
250 for (i = index - 1; i >= descr->top_item; i--)
251 diff -= descr->items[i].height;
253 else
255 for (i = index; i < descr->top_item; i++)
256 diff += descr->items[i].height;
259 else
260 diff = (descr->top_item - index) * descr->item_height;
262 if (abs(diff) < descr->height)
263 ScrollWindow32( wnd->hwndSelf, 0, diff, NULL, NULL );
264 else
265 scroll = FALSE;
267 if (!scroll) InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
268 descr->top_item = index;
269 LISTBOX_UpdateScroll( wnd, descr );
270 return LB_OKAY;
274 /***********************************************************************
275 * LISTBOX_UpdatePage
277 * Update the page size. Should be called when the size of
278 * the client area or the item height changes.
280 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
282 INT32 page_size;
284 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
285 if (page_size == descr->page_size) return;
286 descr->page_size = page_size;
287 if (descr->style & LBS_MULTICOLUMN)
288 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
289 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
293 /***********************************************************************
294 * LISTBOX_UpdateSize
296 * Update the size of the listbox. Should be called when the size of
297 * the client area changes.
299 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
301 RECT32 rect;
303 GetClientRect32( wnd->hwndSelf, &rect );
304 descr->width = rect.right - rect.left;
305 descr->height = rect.bottom - rect.top;
306 if (!(descr->style & LBS_NOINTEGRALHEIGHT))
308 if ((descr->height > descr->item_height) &&
309 (descr->height % descr->item_height))
311 dprintf_listbox(stddeb, "Listbox %04x: changing height %d -> %d\n",
312 wnd->hwndSelf, descr->height,
313 descr->height - descr->height%descr->item_height );
314 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
315 wnd->rectWindow.right - wnd->rectWindow.left,
316 wnd->rectWindow.bottom - wnd->rectWindow.top -
317 (descr->height % descr->item_height),
318 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
319 return;
322 dprintf_listbox( stddeb, "Listbox %04x: new size = %d,%d\n",
323 wnd->hwndSelf, descr->width, descr->height );
324 LISTBOX_UpdatePage( wnd, descr );
325 LISTBOX_UpdateScroll( wnd, descr );
329 /***********************************************************************
330 * LISTBOX_GetItemRect
332 * Get the rectangle enclosing an item, in listbox client coordinates.
333 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
335 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT32 index,
336 RECT32 *rect )
338 /* Index <= 0 is legal even on empty listboxes */
339 if (index && (index >= descr->nb_items)) return -1;
340 SetRect32( rect, 0, 0, descr->width, descr->height );
341 if (descr->style & LBS_MULTICOLUMN)
343 INT32 col = (index / descr->page_size) -
344 (descr->top_item / descr->page_size);
345 rect->left += col * descr->column_width;
346 rect->right = rect->left + descr->column_width;
347 rect->top += (index % descr->page_size) * descr->item_height;
348 rect->bottom = rect->top + descr->item_height;
350 else if (descr->style & LBS_OWNERDRAWVARIABLE)
352 INT32 i;
353 if (index < descr->top_item)
355 for (i = descr->top_item-1; i >= index; i--)
356 rect->top -= descr->items[i].height;
358 else
360 for (i = descr->top_item; i < index; i++)
361 rect->top += descr->items[i].height;
363 rect->bottom = rect->top + descr->items[index].height;
364 rect->right += descr->horz_pos;
366 else
368 rect->top += (index - descr->top_item) * descr->item_height;
369 rect->bottom = rect->top + descr->item_height;
370 rect->right += descr->horz_pos;
373 return ((rect->left < descr->width) && (rect->right > 0) &&
374 (rect->top < descr->height) && (rect->bottom > 0));
378 /***********************************************************************
379 * LISTBOX_GetItemFromPoint
381 * Return the item nearest from point (x,y) (in client coordinates).
383 static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
384 INT32 x, INT32 y )
386 INT32 index = descr->top_item;
388 if (!descr->nb_items) return -1; /* No items */
389 if (descr->style & LBS_OWNERDRAWVARIABLE)
391 INT32 pos = 0;
392 if (y >= 0)
394 while (index < descr->nb_items)
396 if ((pos += descr->items[index].height) > y) break;
397 index++;
400 else
402 while (index > 0)
404 index--;
405 if ((pos -= descr->items[index].height) <= y) break;
409 else if (descr->style & LBS_MULTICOLUMN)
411 if (y >= descr->item_height * descr->page_size) return -1;
412 if (y >= 0) index += y / descr->item_height;
413 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
414 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
416 else
418 index += (y / descr->item_height);
420 if (index < 0) return 0;
421 if (index >= descr->nb_items) return -1;
422 return index;
426 /***********************************************************************
427 * LISTBOX_PaintItem
429 * Paint an item.
431 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc,
432 const RECT32 *rect, INT32 index, UINT32 action )
434 LB_ITEMDATA *item = NULL;
435 if (index < descr->nb_items) item = &descr->items[index];
437 if (IS_OWNERDRAW(descr))
439 DRAWITEMSTRUCT32 dis;
440 dis.CtlType = ODT_LISTBOX;
441 dis.CtlID = wnd->wIDmenu;
442 dis.hwndItem = wnd->hwndSelf;
443 dis.itemAction = action;
444 dis.hDC = hdc;
445 dis.itemID = index;
446 dis.itemState = 0;
447 if (item && item->selected) dis.itemState |= ODS_SELECTED;
448 if ((descr->focus_item == index) &&
449 (descr->caret_on) &&
450 (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
451 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
452 dis.itemData = item ? item->data : 0;
453 dis.rcItem = *rect;
454 dprintf_listbox( stddeb, "Listbox %04x: drawitem %d (%s) action=%02x "
455 "state=%02x rect=%d,%d-%d,%d\n",
456 wnd->hwndSelf, index, item ? item->str : "", action,
457 dis.itemState, rect->left, rect->top,
458 rect->right, rect->bottom );
459 SendMessage32A(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
461 else
463 if (action == ODA_FOCUS)
465 DrawFocusRect32( hdc, rect );
466 return;
468 if (item && item->selected)
470 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
471 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
473 else
475 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
476 if (wnd->dwStyle & WS_DISABLED)
477 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
478 else
479 SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
481 dprintf_listbox( stddeb, "Listbox %04x: painting %d (%s) action=%02x"
482 "rect=%d,%d-%d,%d\n",
483 wnd->hwndSelf, index, item ? item->str : "", action,
484 rect->left, rect->top, rect->right, rect->bottom );
485 /* FIXME: check LBS_USETABSTOPS style */
486 if (item)
487 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
488 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
489 strlen(item->str), NULL );
490 else
491 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
492 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
493 if ((descr->focus_item == index) &&
494 (descr->caret_on) &&
495 (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect );
500 /***********************************************************************
501 * LISTBOX_SetRedraw
503 * Change the redraw flag.
505 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on )
507 if (on)
509 if (!(descr->style & LBS_NOREDRAW)) return;
510 descr->style &= ~LBS_NOREDRAW;
511 LISTBOX_UpdateScroll( wnd, descr );
513 else descr->style |= LBS_NOREDRAW;
517 /***********************************************************************
518 * LISTBOX_RepaintItem
520 * Repaint a single item synchronously.
522 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index,
523 UINT32 action )
525 HDC32 hdc;
526 RECT32 rect;
528 if (descr->style & LBS_NOREDRAW) return;
529 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
530 if ((hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
532 HFONT32 oldFont = 0;
533 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
534 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
535 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
536 if (oldFont) SelectObject32( hdc, oldFont );
537 ReleaseDC32( wnd->hwndSelf, hdc );
542 /***********************************************************************
543 * LISTBOX_InitStorage
545 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items,
546 DWORD bytes )
548 LB_ITEMDATA *item;
550 nb_items += LB_ARRAY_GRANULARITY - 1;
551 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
552 if (descr->items)
553 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
554 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
555 nb_items * sizeof(LB_ITEMDATA) )))
557 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
558 return LB_ERRSPACE;
560 descr->items = item;
561 return LB_OKAY;
565 /***********************************************************************
566 * LISTBOX_SetTabStops
568 static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count,
569 LPINT32 tabs, BOOL32 short_ints )
571 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
572 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
573 if (!(descr->nb_tabs = count))
575 descr->tabs = NULL;
576 return TRUE;
578 /* FIXME: count = 1 */
579 if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0,
580 descr->nb_tabs * sizeof(INT32) )))
581 return FALSE;
582 if (short_ints)
584 INT32 i;
585 LPINT16 p = (LPINT16)tabs;
586 for (i = 0; i < descr->nb_tabs; i++) descr->tabs[i] = *p++;
588 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) );
589 /* FIXME: repaint the window? */
590 return TRUE;
594 /***********************************************************************
595 * LISTBOX_GetText
597 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index,
598 LPSTR buffer )
600 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
601 if (HAS_STRINGS(descr))
603 lstrcpy32A( buffer, descr->items[index].str );
604 return strlen(buffer);
606 else
608 memcpy( buffer, &descr->items[index].data, sizeof(DWORD) );
609 return sizeof(DWORD);
614 /***********************************************************************
615 * LISTBOX_FindStringPos
617 * Find the nearest string located before a given string in sort order.
618 * If 'exact' is TRUE, return an error if we don't get an exact match.
620 static INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
621 BOOL32 exact )
623 INT32 index, min, max, res = -1;
625 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
626 min = 0;
627 max = descr->nb_items;
628 while (min != max)
630 index = (min + max) / 2;
631 if (HAS_STRINGS(descr))
632 res = lstrcmpi32A( descr->items[index].str, str );
633 else
635 COMPAREITEMSTRUCT32 cis;
636 cis.CtlType = ODT_LISTBOX;
637 cis.CtlID = wnd->wIDmenu;
638 cis.hwndItem = wnd->hwndSelf;
639 cis.itemID1 = index;
640 cis.itemData1 = descr->items[index].data;
641 cis.itemID2 = -1;
642 cis.itemData2 = (DWORD)str;
643 cis.dwLocaleId = descr->locale;
644 res = SendMessage32A( descr->owner, WM_COMPAREITEM,
645 wnd->wIDmenu, (LPARAM)&cis );
647 if (!res) return index;
648 if (res > 0) max = index;
649 else min = index + 1;
651 return exact ? -1 : max;
655 /***********************************************************************
656 * LISTBOX_FindFileStrPos
658 * Find the nearest string located before a given string in directory
659 * sort order (i.e. first files, then directories, then drives).
661 static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
663 INT32 min, max, res = -1;
665 if (!HAS_STRINGS(descr))
666 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
667 min = 0;
668 max = descr->nb_items;
669 while (min != max)
671 INT32 index = (min + max) / 2;
672 const char *p = descr->items[index].str;
673 if (*p == '[') /* drive or directory */
675 if (*str != '[') res = -1;
676 else if (p[1] == '-') /* drive */
678 if (str[1] == '-') res = str[2] - p[2];
679 else res = -1;
681 else /* directory */
683 if (str[1] == '-') res = 1;
684 else res = lstrcmpi32A( str, p );
687 else /* filename */
689 if (*str == '[') res = 1;
690 else res = lstrcmpi32A( str, p );
692 if (!res) return index;
693 if (res < 0) max = index;
694 else min = index + 1;
696 return max;
700 /***********************************************************************
701 * LISTBOX_FindString
703 * Find the item beginning with a given string.
705 static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start,
706 LPCSTR str, BOOL32 exact )
708 INT32 i;
709 LB_ITEMDATA *item;
711 if (start >= descr->nb_items) start = -1;
712 item = descr->items + start + 1;
713 if (HAS_STRINGS(descr))
715 if (exact)
717 for (i = start + 1; i < descr->nb_items; i++, item++)
718 if (!lstrcmpi32A( str, item->str )) return i;
719 for (i = 0, item = descr->items; i <= start; i++, item++)
720 if (!lstrcmpi32A( str, item->str )) return i;
722 else
724 /* Special case for drives and directories: ignore prefix */
725 #define CHECK_DRIVE(item) \
726 if ((item)->str[0] == '[') \
728 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
729 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
730 return i; \
733 INT32 len = strlen(str);
734 for (i = start + 1; i < descr->nb_items; i++, item++)
736 if (!lstrncmpi32A( str, item->str, len )) return i;
737 CHECK_DRIVE(item);
739 for (i = 0, item = descr->items; i <= start; i++, item++)
741 if (!lstrncmpi32A( str, item->str, len )) return i;
742 CHECK_DRIVE(item);
744 #undef CHECK_DRIVE
747 else
749 if (exact && (descr->style & LBS_SORT))
750 /* If sorted, use a WM_COMPAREITEM binary search */
751 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
753 /* Otherwise use a linear search */
754 for (i = start + 1; i < descr->nb_items; i++, item++)
755 if (item->data == (DWORD)str) return i;
756 for (i = 0, item = descr->items; i <= start; i++, item++)
757 if (item->data == (DWORD)str) return i;
759 return LB_ERR;
763 /***********************************************************************
764 * LISTBOX_GetSelCount
766 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
768 INT32 i, count;
769 LB_ITEMDATA *item = descr->items;
771 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
772 for (i = count = 0; i < descr->nb_items; i++, item++)
773 if (item->selected) count++;
774 return count;
778 /***********************************************************************
779 * LISTBOX_GetSelItems16
781 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
782 LPINT16 array )
784 INT32 i, count;
785 LB_ITEMDATA *item = descr->items;
787 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
788 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
789 if (item->selected) array[count++] = (INT16)i;
790 return count;
794 /***********************************************************************
795 * LISTBOX_GetSelItems32
797 static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max,
798 LPINT32 array )
800 INT32 i, count;
801 LB_ITEMDATA *item = descr->items;
803 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
804 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
805 if (item->selected) array[count++] = i;
806 return count;
810 /***********************************************************************
811 * LISTBOX_Paint
813 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc )
815 INT32 i, col_pos = descr->page_size - 1;
816 RECT32 rect;
817 HFONT32 oldFont = 0;
819 SetRect32( &rect, 0, 0, descr->width, descr->height );
820 if (descr->style & LBS_NOREDRAW) return 0;
821 if (descr->style & LBS_MULTICOLUMN)
822 rect.right = rect.left + descr->column_width;
823 else if (descr->horz_pos)
825 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
826 rect.right += descr->horz_pos;
829 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
831 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
832 (GetFocus32() == wnd->hwndSelf))
834 /* Special case for empty listbox: paint focus rect */
835 rect.bottom = rect.top + descr->item_height;
836 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
837 ODA_DRAWENTIRE );
838 rect.top = rect.bottom;
841 for (i = descr->top_item; i < descr->nb_items; i++)
843 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
844 rect.bottom = rect.top + descr->item_height;
845 else
846 rect.bottom = rect.top + descr->items[i].height;
848 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
849 rect.top = rect.bottom;
851 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
853 if (!IS_OWNERDRAW(descr))
855 /* Clear the bottom of the column */
856 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
857 if (rect.top < descr->height)
859 rect.bottom = descr->height;
860 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
861 &rect, NULL, 0, NULL );
865 /* Go to the next column */
866 rect.left += descr->column_width;
867 rect.right += descr->column_width;
868 rect.top = 0;
869 col_pos = descr->page_size - 1;
871 else
873 col_pos--;
874 if (rect.top >= descr->height) break;
878 if (!IS_OWNERDRAW(descr))
880 /* Clear the remainder of the client area */
881 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
882 if (rect.top < descr->height)
884 rect.bottom = descr->height;
885 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
886 &rect, NULL, 0, NULL );
888 if (rect.right < descr->width)
890 rect.left = rect.right;
891 rect.right = descr->width;
892 rect.top = 0;
893 rect.bottom = descr->height;
894 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
895 &rect, NULL, 0, NULL );
898 if (oldFont) SelectObject32( hdc, oldFont );
899 return 0;
903 /***********************************************************************
904 * LISTBOX_InvalidateItems
906 * Invalidate all items from a given item. If the specified item is not
907 * visible, nothing happens.
909 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index )
911 RECT32 rect;
913 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
915 rect.bottom = descr->height;
916 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
917 if (descr->style & LBS_MULTICOLUMN)
919 /* Repaint the other columns */
920 rect.left = rect.right;
921 rect.right = descr->width;
922 rect.top = 0;
923 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
929 /***********************************************************************
930 * LISTBOX_GetItemHeight
932 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index )
934 if (descr->style & LBS_OWNERDRAWVARIABLE)
936 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
937 return descr->items[index].height;
939 else return descr->item_height;
943 /***********************************************************************
944 * LISTBOX_SetItemHeight
946 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index,
947 UINT32 height )
949 if (!height) height = 1;
951 if (descr->style & LBS_OWNERDRAWVARIABLE)
953 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
954 dprintf_listbox( stddeb, "Listbox %04x: item %d height = %d\n",
955 wnd->hwndSelf, index, height );
956 descr->items[index].height = height;
957 LISTBOX_UpdateScroll( wnd, descr );
958 LISTBOX_InvalidateItems( wnd, descr, index );
960 else if (height != descr->item_height)
962 dprintf_listbox( stddeb, "Listbox %04x: new height = %d\n",
963 wnd->hwndSelf, height );
964 descr->item_height = height;
965 LISTBOX_UpdatePage( wnd, descr );
966 LISTBOX_UpdateScroll( wnd, descr );
967 InvalidateRect32( wnd->hwndSelf, 0, TRUE );
969 return LB_OKAY;
973 /***********************************************************************
974 * LISTBOX_SetHorizontalPos
976 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos )
978 INT32 diff;
980 if (pos > descr->horz_extent - descr->width)
981 pos = descr->horz_extent - descr->width;
982 if (pos < 0) pos = 0;
983 if (!(diff = descr->horz_pos - pos)) return;
984 dprintf_listbox( stddeb, "Listbox %04x: new horz pos = %d\n",
985 wnd->hwndSelf, pos );
986 descr->horz_pos = pos;
987 LISTBOX_UpdateScroll( wnd, descr );
988 if (abs(diff) < descr->width)
989 ScrollWindow32( wnd->hwndSelf, diff, 0, NULL, NULL );
990 else
991 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
995 /***********************************************************************
996 * LISTBOX_SetHorizontalExtent
998 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
999 UINT32 extent )
1001 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1002 return LB_OKAY;
1003 if (extent <= 0) extent = 1;
1004 if (extent == descr->horz_extent) return LB_OKAY;
1005 dprintf_listbox( stddeb, "Listbox %04x: new horz extent = %d\n",
1006 wnd->hwndSelf, extent );
1007 descr->horz_extent = extent;
1008 if (descr->horz_pos > extent - descr->width)
1009 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1010 else
1011 LISTBOX_UpdateScroll( wnd, descr );
1012 return LB_OKAY;
1016 /***********************************************************************
1017 * LISTBOX_SetColumnWidth
1019 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width)
1021 width += 2; /* For left and right margin */
1022 if (width == descr->column_width) return LB_OKAY;
1023 dprintf_listbox( stddeb, "Listbox %04x: new column width = %d\n",
1024 wnd->hwndSelf, width );
1025 descr->column_width = width;
1026 LISTBOX_UpdatePage( wnd, descr );
1027 return LB_OKAY;
1031 /***********************************************************************
1032 * LISTBOX_SetFont
1034 * Returns the item height.
1036 static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font )
1038 HDC32 hdc;
1039 HFONT32 oldFont = 0;
1040 TEXTMETRIC32A tm;
1042 descr->font = font;
1044 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
1046 fprintf( stderr, "LISTBOX_SetFont: unable to get DC\n" );
1047 return 16;
1049 if (font) oldFont = SelectObject32( hdc, font );
1050 GetTextMetrics32A( hdc, &tm );
1051 if (oldFont) SelectObject32( hdc, oldFont );
1052 ReleaseDC32( wnd->hwndSelf, hdc );
1053 if (!IS_OWNERDRAW(descr))
1054 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight + 2 );
1055 return tm.tmHeight + 2;
1059 /***********************************************************************
1060 * LISTBOX_MakeItemVisible
1062 * Make sure that a given item is partially or fully visible.
1064 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index,
1065 BOOL32 fully )
1067 INT32 top;
1069 if (index <= descr->top_item) top = index;
1070 else if (descr->style & LBS_MULTICOLUMN)
1072 INT32 cols = descr->width;
1073 if (!fully) cols += descr->column_width - 1;
1074 if (cols >= descr->column_width) cols /= descr->column_width;
1075 else cols = 1;
1076 if (index < descr->top_item + (descr->page_size * cols)) return;
1077 top = index - descr->page_size * (cols - 1);
1079 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1081 INT32 height = fully ? descr->items[index].height : 1;
1082 for (top = index; top > descr->top_item; top--)
1083 if ((height += descr->items[top-1].height) > descr->height) break;
1085 else
1087 if (index < descr->top_item + descr->page_size) return;
1088 if (!fully && (index == descr->top_item + descr->page_size) &&
1089 (descr->height > (descr->page_size * descr->item_height))) return;
1090 top = index - descr->page_size + 1;
1092 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1096 /***********************************************************************
1097 * LISTBOX_SelectItemRange
1099 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1101 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first,
1102 INT32 last, BOOL32 on )
1104 INT32 i;
1106 /* A few sanity checks */
1108 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1109 if (last == -1) last = descr->nb_items - 1;
1110 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1111 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1112 /* selected_item reflects last selected/unselected item on multiple sel */
1113 descr->selected_item = last;
1115 if (on) /* Turn selection on */
1117 for (i = first; i <= last; i++)
1119 if (descr->items[i].selected) continue;
1120 descr->items[i].selected = TRUE;
1121 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1124 else /* Turn selection off */
1126 for (i = first; i <= last; i++)
1128 if (!descr->items[i].selected) continue;
1129 descr->items[i].selected = FALSE;
1130 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1133 return LB_OKAY;
1137 /***********************************************************************
1138 * LISTBOX_SetCaretIndex
1140 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index,
1141 BOOL32 fully_visible )
1143 INT32 oldfocus = descr->focus_item;
1145 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1146 if (index == oldfocus) return LB_OKAY;
1147 descr->focus_item = index;
1148 if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1149 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1150 if (index != -1)
1152 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1153 if (descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1154 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1156 return LB_OKAY;
1160 /***********************************************************************
1161 * LISTBOX_SetSelection
1163 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index,
1164 BOOL32 on, BOOL32 send_notify )
1166 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1167 if (descr->style & LBS_MULTIPLESEL)
1169 if (index == -1) /* Select all items */
1170 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1171 else /* Only one item */
1172 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1174 else
1176 INT32 oldsel = descr->selected_item;
1177 if (index == oldsel) return LB_OKAY;
1178 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1179 if (index != -1) descr->items[index].selected = TRUE;
1180 descr->selected_item = index;
1181 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1182 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1183 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1184 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1186 return LB_OKAY;
1190 /***********************************************************************
1191 * LISTBOX_MoveCaret
1193 * Change the caret position and extend the selection to the new caret.
1195 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index,
1196 BOOL32 fully_visible )
1198 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1199 if (descr->style & LBS_EXTENDEDSEL)
1201 if (descr->anchor_item != -1)
1203 INT32 first = MIN( descr->focus_item, descr->anchor_item );
1204 INT32 last = MAX( descr->focus_item, descr->anchor_item );
1205 if (first > 0)
1206 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1207 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1208 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1211 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1213 /* Set selection to new caret item */
1214 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1219 /***********************************************************************
1220 * LISTBOX_InsertItem
1222 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index,
1223 LPSTR str, DWORD data )
1225 LB_ITEMDATA *item;
1226 INT32 max_items;
1228 if (index == -1) index = descr->nb_items;
1229 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1230 if (!descr->items) max_items = 0;
1231 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1232 if (descr->nb_items == max_items)
1234 /* We need to grow the array */
1235 max_items += LB_ARRAY_GRANULARITY;
1236 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1237 max_items * sizeof(LB_ITEMDATA) )))
1239 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1240 return LB_ERRSPACE;
1242 descr->items = item;
1245 /* Insert the item structure */
1247 item = &descr->items[index];
1248 if (index < descr->nb_items)
1249 RtlMoveMemory( item + 1, item,
1250 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1251 item->str = str;
1252 item->data = data;
1253 item->height = 0;
1254 item->selected = FALSE;
1255 descr->nb_items++;
1257 /* Get item height */
1259 if (descr->style & LBS_OWNERDRAWVARIABLE)
1261 MEASUREITEMSTRUCT32 mis;
1262 mis.CtlType = ODT_LISTBOX;
1263 mis.CtlID = wnd->wIDmenu;
1264 mis.itemID = index;
1265 mis.itemData = descr->items[index].data;
1266 mis.itemHeight = descr->item_height;
1267 SendMessage32A( descr->owner, WM_MEASUREITEM, wnd->wIDmenu,
1268 (LPARAM)&mis );
1269 item->height = mis.itemHeight ? mis.itemHeight : 1;
1270 dprintf_listbox( stddeb, "Listbox %04x: measure item %d (%s) = %d\n",
1271 wnd->hwndSelf, index, str ? str : "", item->height );
1274 /* Repaint the items */
1276 LISTBOX_UpdateScroll( wnd, descr );
1277 LISTBOX_InvalidateItems( wnd, descr, index );
1279 /* Move selection and focused item */
1281 if (index <= descr->selected_item) descr->selected_item++;
1282 if (index <= descr->focus_item)
1284 descr->focus_item++;
1285 LISTBOX_MoveCaret( wnd, descr, descr->focus_item - 1, FALSE );
1288 /* If listbox was empty, set focus to the first item */
1290 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1291 return LB_OKAY;
1295 /***********************************************************************
1296 * LISTBOX_InsertString
1298 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index,
1299 LPCSTR str )
1301 LPSTR new_str = NULL;
1302 DWORD data = 0;
1303 LRESULT ret;
1305 if (HAS_STRINGS(descr))
1307 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1309 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1310 return LB_ERRSPACE;
1313 else data = (DWORD)str;
1315 if (index == -1) index = descr->nb_items;
1316 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1318 if (new_str) HeapFree( descr->heap, 0, new_str );
1319 return ret;
1322 dprintf_listbox( stddeb, "Listbox %04x: added item %d '%s'\n",
1323 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1324 return index;
1328 /***********************************************************************
1329 * LISTBOX_DeleteItem
1331 * Delete the content of an item. 'index' must be a valid index.
1333 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index )
1335 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1336 * while Win95 sends it for all items with user data.
1337 * It's probably better to send it too often than not
1338 * often enough, so this is what we do here.
1340 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1342 DELETEITEMSTRUCT32 dis;
1343 dis.CtlType = ODT_LISTBOX;
1344 dis.CtlID = wnd->wIDmenu;
1345 dis.itemID = index;
1346 dis.hwndItem = wnd->hwndSelf;
1347 dis.itemData = descr->items[index].data;
1348 SendMessage32A( wnd->hwndSelf, WM_DELETEITEM, wnd->wIDmenu,
1349 (LPARAM)&dis );
1351 if (HAS_STRINGS(descr) && descr->items[index].str)
1352 HeapFree( descr->heap, 0, descr->items[index].str );
1356 /***********************************************************************
1357 * LISTBOX_RemoveItem
1359 * Remove an item from the listbox and delete its content.
1361 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index )
1363 LB_ITEMDATA *item;
1364 INT32 max_items;
1366 if (index == -1) index = descr->nb_items - 1;
1367 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1368 LISTBOX_DeleteItem( wnd, descr, index );
1370 /* Remove the item */
1372 item = &descr->items[index];
1373 if (index < descr->nb_items-1)
1374 RtlMoveMemory( item, item + 1,
1375 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1376 descr->nb_items--;
1377 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1379 /* Shrink the item array if possible */
1381 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1382 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1384 max_items -= LB_ARRAY_GRANULARITY;
1385 item = HeapReAlloc( descr->heap, 0, descr->items,
1386 max_items * sizeof(LB_ITEMDATA) );
1387 if (item) descr->items = item;
1390 /* Repaint the items */
1392 LISTBOX_UpdateScroll( wnd, descr );
1393 LISTBOX_InvalidateItems( wnd, descr, index );
1395 /* Move selection and focused item */
1397 if (index <= descr->selected_item) descr->selected_item--;
1398 if (index <= descr->focus_item)
1400 descr->focus_item--;
1401 LISTBOX_MoveCaret( wnd, descr, descr->focus_item + 1, FALSE );
1403 return LB_OKAY;
1407 /***********************************************************************
1408 * LISTBOX_ResetContent
1410 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1412 INT32 i;
1414 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1415 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1416 descr->nb_items = 0;
1417 descr->top_item = 0;
1418 descr->selected_item = -1;
1419 descr->focus_item = 0;
1420 descr->anchor_item = -1;
1421 descr->items = NULL;
1422 LISTBOX_UpdateScroll( wnd, descr );
1423 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1427 /***********************************************************************
1428 * LISTBOX_SetCount
1430 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count )
1432 LRESULT ret;
1434 if (HAS_STRINGS(descr)) return LB_ERR;
1435 /* FIXME: this is far from optimal... */
1436 if (count > descr->nb_items)
1438 while (count > descr->nb_items)
1439 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1440 return ret;
1442 else if (count < descr->nb_items)
1444 while (count < descr->nb_items)
1445 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1446 return ret;
1448 return LB_OKAY;
1452 /***********************************************************************
1453 * LISTBOX_Directory
1455 LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib,
1456 LPCSTR filespec, BOOL32 long_names )
1458 char mask[13];
1459 const char *ptr;
1460 char *path, *p;
1461 int count, skip, pos;
1462 LRESULT ret;
1463 DOS_DIRENT entry;
1465 /* FIXME: should use FindFirstFile/FindNextFile */
1467 if (!filespec) return LB_ERR;
1468 if (!(ptr = DOSFS_GetUnixFileName( filespec, FALSE ))) return LB_ERR;
1469 path = HEAP_strdupA( SystemHeap, 0, ptr );
1470 p = strrchr( path, '/' );
1471 *p++ = '\0';
1472 if (!(ptr = DOSFS_ToDosFCBFormat( p )))
1474 HeapFree( SystemHeap, 0, path );
1475 return LB_ERR;
1477 strcpy( mask, ptr );
1479 skip = 0;
1480 ret = LB_OKAY;
1481 attrib &= ~FA_LABEL;
1482 while ((count = DOSFS_FindNext( path, mask, NULL, 0,
1483 attrib, skip, &entry )) > 0)
1485 char buffer[260];
1486 skip += count;
1487 if (entry.attr & FA_DIRECTORY)
1489 if (!(attrib & DDL_DIRECTORY) || !strcmp(entry.name,". "))
1490 continue;
1491 if (long_names) sprintf( buffer, "[%s]", entry.unixname );
1492 else sprintf( buffer, "[%s]", DOSFS_ToDosDTAFormat( entry.name ) );
1494 else /* not a directory */
1496 if ((attrib & DDL_EXCLUSIVE) &&
1497 ((attrib & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE)) !=
1498 (entry.attr & (FA_RDONLY|FA_HIDDEN|FA_SYSTEM|FA_ARCHIVE))))
1499 continue;
1500 if (long_names) strcpy( buffer, entry.unixname );
1501 else strcpy( buffer, DOSFS_ToDosDTAFormat( entry.name ) );
1503 if (!long_names) AnsiLower( buffer );
1504 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1505 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1506 break;
1509 if ((ret == LB_OKAY) && (attrib & DDL_DRIVES))
1511 char buffer[] = "[-a-]";
1512 int drive;
1513 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1515 if (!DRIVE_IsValid(drive)) continue;
1516 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1517 break;
1521 HeapFree( SystemHeap, 0, path );
1522 return ret;
1526 /***********************************************************************
1527 * LISTBOX_HandleVScroll
1529 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1530 WPARAM32 wParam, LPARAM lParam )
1532 SCROLLINFO info;
1534 if (descr->style & LBS_MULTICOLUMN) return 0;
1535 switch(LOWORD(wParam))
1537 case SB_LINEUP:
1538 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1539 break;
1540 case SB_LINEDOWN:
1541 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1542 break;
1543 case SB_PAGEUP:
1544 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1545 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1546 break;
1547 case SB_PAGEDOWN:
1548 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1549 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1550 break;
1551 case SB_THUMBPOSITION:
1552 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1553 break;
1554 case SB_THUMBTRACK:
1555 info.cbSize = sizeof(info);
1556 info.fMask = SIF_TRACKPOS;
1557 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1558 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1559 break;
1560 case SB_TOP:
1561 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1562 break;
1563 case SB_BOTTOM:
1564 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1565 break;
1567 return 0;
1571 /***********************************************************************
1572 * LISTBOX_HandleHScroll
1574 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1575 WPARAM32 wParam, LPARAM lParam )
1577 SCROLLINFO info;
1578 INT32 page;
1580 if (descr->style & LBS_MULTICOLUMN)
1582 switch(LOWORD(wParam))
1584 case SB_LINELEFT:
1585 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1586 TRUE );
1587 break;
1588 case SB_LINERIGHT:
1589 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1590 TRUE );
1591 break;
1592 case SB_PAGELEFT:
1593 page = descr->width / descr->column_width;
1594 if (page < 1) page = 1;
1595 LISTBOX_SetTopItem( wnd, descr,
1596 descr->top_item - page * descr->page_size, TRUE );
1597 break;
1598 case SB_PAGERIGHT:
1599 page = descr->width / descr->column_width;
1600 if (page < 1) page = 1;
1601 LISTBOX_SetTopItem( wnd, descr,
1602 descr->top_item + page * descr->page_size, TRUE );
1603 break;
1604 case SB_THUMBPOSITION:
1605 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1606 TRUE );
1607 break;
1608 case SB_THUMBTRACK:
1609 info.cbSize = sizeof(info);
1610 info.fMask = SIF_TRACKPOS;
1611 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1612 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1613 TRUE );
1614 break;
1615 case SB_LEFT:
1616 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1617 break;
1618 case SB_RIGHT:
1619 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1620 break;
1623 else if (descr->horz_extent)
1625 switch(LOWORD(wParam))
1627 case SB_LINELEFT:
1628 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1629 break;
1630 case SB_LINERIGHT:
1631 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1632 break;
1633 case SB_PAGELEFT:
1634 LISTBOX_SetHorizontalPos( wnd, descr,
1635 descr->horz_pos - descr->width );
1636 break;
1637 case SB_PAGERIGHT:
1638 LISTBOX_SetHorizontalPos( wnd, descr,
1639 descr->horz_pos + descr->width );
1640 break;
1641 case SB_THUMBPOSITION:
1642 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1643 break;
1644 case SB_THUMBTRACK:
1645 info.cbSize = sizeof(info);
1646 info.fMask = SIF_TRACKPOS;
1647 GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info );
1648 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1649 break;
1650 case SB_LEFT:
1651 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1652 break;
1653 case SB_RIGHT:
1654 LISTBOX_SetHorizontalPos( wnd, descr,
1655 descr->horz_extent - descr->width );
1656 break;
1659 return 0;
1663 /***********************************************************************
1664 * LISTBOX_HandleLButtonDown
1666 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1667 WPARAM32 wParam, INT32 x, INT32 y )
1669 INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1670 dprintf_listbox( stddeb, "Listbox %04x: lbuttondown %d,%d item %d\n",
1671 wnd->hwndSelf, x, y, index );
1672 if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0;
1673 if (index != -1)
1675 if (descr->style & LBS_EXTENDEDSEL)
1677 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1678 if (wParam & MK_CONTROL)
1680 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1681 LISTBOX_SetSelection( wnd, descr, index,
1682 !descr->items[index].selected, FALSE );
1684 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1686 else
1688 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1689 LISTBOX_SetSelection( wnd, descr, index,
1690 (!(descr->style & LBS_MULTIPLESEL) ||
1691 !descr->items[index].selected), FALSE );
1694 SetFocus32( wnd->hwndSelf );
1695 SetCapture32( wnd->hwndSelf );
1696 if (index != -1)
1698 if (descr->style & LBS_NOTIFY)
1699 SendMessage32A( descr->owner, WM_LBTRACKPOINT, index,
1700 MAKELPARAM( x, y ) );
1701 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1703 POINT16 pt = { x, y };
1704 if (DragDetect( wnd->hwndSelf, pt ))
1705 SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 );
1708 return 0;
1712 /***********************************************************************
1713 * LISTBOX_HandleLButtonUp
1715 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1717 if (LISTBOX_Timer != LB_TIMER_NONE)
1718 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1719 LISTBOX_Timer = LB_TIMER_NONE;
1720 if (GetCapture32() == wnd->hwndSelf) ReleaseCapture();
1721 if (descr->style & LBS_NOTIFY)
1722 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1723 return 0;
1727 /***********************************************************************
1728 * LISTBOX_HandleTimer
1730 * Handle scrolling upon a timer event.
1731 * Return TRUE if scrolling should continue.
1733 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1734 INT32 index, TIMER_DIRECTION dir )
1736 switch(dir)
1738 case LB_TIMER_UP:
1739 if (descr->top_item) index = descr->top_item - 1;
1740 else index = 0;
1741 break;
1742 case LB_TIMER_LEFT:
1743 if (descr->top_item) index -= descr->page_size;
1744 break;
1745 case LB_TIMER_DOWN:
1746 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1747 if (index >= descr->nb_items) index = descr->nb_items - 1;
1748 break;
1749 case LB_TIMER_RIGHT:
1750 if (index + descr->page_size < descr->nb_items)
1751 index += descr->page_size;
1752 break;
1753 case LB_TIMER_NONE:
1754 break;
1756 if (index == descr->focus_item) return FALSE;
1757 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1758 return TRUE;
1762 /***********************************************************************
1763 * LISTBOX_HandleSystemTimer
1765 * WM_SYSTIMER handler.
1767 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1769 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1771 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1772 LISTBOX_Timer = LB_TIMER_NONE;
1774 return 0;
1778 /***********************************************************************
1779 * LISTBOX_HandleMouseMove
1781 * WM_MOUSEMOVE handler.
1783 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1784 INT32 x, INT32 y )
1786 INT32 index;
1787 TIMER_DIRECTION dir;
1789 if (descr->style & LBS_MULTICOLUMN)
1791 if (y < 0) y = 0;
1792 else if (y >= descr->item_height * descr->page_size)
1793 y = descr->item_height * descr->page_size - 1;
1795 if (x < 0)
1797 dir = LB_TIMER_LEFT;
1798 x = 0;
1800 else if (x >= descr->width)
1802 dir = LB_TIMER_RIGHT;
1803 x = descr->width - 1;
1805 else dir = LB_TIMER_NONE; /* inside */
1807 else
1809 if (y < 0) dir = LB_TIMER_UP; /* above */
1810 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1811 else dir = LB_TIMER_NONE; /* inside */
1814 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1815 if (index == -1) index = descr->focus_item;
1816 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1818 /* Start/stop the system timer */
1820 if (dir != LB_TIMER_NONE)
1821 SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1822 else if (LISTBOX_Timer != LB_TIMER_NONE)
1823 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1824 LISTBOX_Timer = dir;
1828 /***********************************************************************
1829 * LISTBOX_HandleKeyDown
1831 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr,
1832 WPARAM32 wParam )
1834 INT32 caret = -1;
1835 if (descr->style & LBS_WANTKEYBOARDINPUT)
1837 caret = SendMessage32A( descr->owner, WM_VKEYTOITEM,
1838 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1839 wnd->hwndSelf );
1840 if (caret == -2) return 0;
1842 if (caret == -1) switch(wParam)
1844 case VK_LEFT:
1845 if (descr->style & LBS_MULTICOLUMN)
1847 if (descr->focus_item >= descr->page_size)
1848 caret = descr->focus_item - descr->page_size;
1849 break;
1851 /* fall through */
1852 case VK_UP:
1853 caret = descr->focus_item - 1;
1854 if (caret < 0) caret = 0;
1855 break;
1856 case VK_RIGHT:
1857 if (descr->style & LBS_MULTICOLUMN)
1859 if (descr->focus_item + descr->page_size < descr->nb_items)
1860 caret = descr->focus_item + descr->page_size;
1861 break;
1863 /* fall through */
1864 case VK_DOWN:
1865 caret = descr->focus_item + 1;
1866 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1867 break;
1868 case VK_PRIOR:
1869 if (descr->style & LBS_MULTICOLUMN)
1871 INT32 page = descr->width / descr->column_width;
1872 if (page < 1) page = 1;
1873 caret = descr->focus_item - (page * descr->page_size) + 1;
1875 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1876 if (caret < 0) caret = 0;
1877 break;
1878 case VK_NEXT:
1879 if (descr->style & LBS_MULTICOLUMN)
1881 INT32 page = descr->width / descr->column_width;
1882 if (page < 1) page = 1;
1883 caret = descr->focus_item + (page * descr->page_size) - 1;
1885 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1886 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1887 break;
1888 case VK_HOME:
1889 caret = 0;
1890 break;
1891 case VK_END:
1892 caret = descr->nb_items - 1;
1893 break;
1894 case VK_SPACE:
1895 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1896 else if (descr->style & LBS_MULTIPLESEL)
1898 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1899 !descr->items[descr->focus_item].selected,
1900 (descr->style & LBS_NOTIFY) != 0 );
1902 break;
1904 if (caret >= 0)
1906 if ((descr->style & LBS_EXTENDEDSEL) &&
1907 !(GetKeyState( VK_SHIFT ) & 0x8000))
1908 descr->anchor_item = caret;
1909 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1910 if (descr->style & LBS_NOTIFY)
1911 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1913 return 0;
1917 /***********************************************************************
1918 * LISTBOX_HandleChar
1920 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1921 WPARAM32 wParam )
1923 INT32 caret = -1;
1924 char str[2] = { wParam & 0xff, '\0' };
1926 if (descr->style & LBS_WANTKEYBOARDINPUT)
1928 caret = SendMessage32A( descr->owner, WM_CHARTOITEM,
1929 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1930 wnd->hwndSelf );
1931 if (caret == -2) return 0;
1933 if (caret == -1)
1934 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
1935 if (caret != -1)
1937 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1938 if (descr->style & LBS_NOTIFY)
1939 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1941 return 0;
1945 /***********************************************************************
1946 * LISTBOX_Create
1948 static BOOL32 LISTBOX_Create( WND *wnd )
1950 LB_DESCR *descr;
1951 MEASUREITEMSTRUCT32 mis;
1952 RECT32 rect;
1954 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
1955 return FALSE;
1956 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
1958 HeapFree( GetProcessHeap(), 0, descr );
1959 return FALSE;
1961 GetClientRect32( wnd->hwndSelf, &rect );
1962 descr->owner = GetParent32( wnd->hwndSelf );
1963 descr->style = wnd->dwStyle;
1964 descr->width = rect.right - rect.left;
1965 descr->height = rect.bottom - rect.top;
1966 descr->items = NULL;
1967 descr->nb_items = 0;
1968 descr->top_item = 0;
1969 descr->selected_item = -1;
1970 descr->focus_item = 0;
1971 descr->anchor_item = -1;
1972 descr->item_height = 1;
1973 descr->page_size = 1;
1974 descr->column_width = 150;
1975 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
1976 descr->horz_pos = 0;
1977 descr->nb_tabs = 0;
1978 descr->tabs = NULL;
1979 descr->caret_on = TRUE;
1980 descr->font = 0;
1981 descr->locale = 0; /* FIXME */
1983 *(LB_DESCR **)wnd->wExtra = descr;
1985 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
1986 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
1987 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
1988 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
1990 if (descr->style & LBS_OWNERDRAWFIXED)
1992 mis.CtlType = ODT_LISTBOX;
1993 mis.CtlID = wnd->wIDmenu;
1994 mis.itemID = 0;
1995 mis.itemWidth = 0;
1996 mis.itemData = 0;
1997 mis.itemHeight = descr->item_height;
1998 SendMessage32A( descr->owner, WM_MEASUREITEM, wnd->wIDmenu,
1999 (LPARAM)&mis );
2000 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2003 return TRUE;
2007 /***********************************************************************
2008 * LISTBOX_Destroy
2010 static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2012 LISTBOX_ResetContent( wnd, descr );
2013 HeapDestroy( descr->heap );
2014 HeapFree( GetProcessHeap(), 0, descr );
2015 *(LB_DESCR **)wnd->wExtra = NULL;
2016 return TRUE;
2020 /***********************************************************************
2021 * ListBoxWndProc
2023 LRESULT ListBoxWndProc(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
2025 LRESULT ret;
2026 LB_DESCR *descr;
2027 WND *wnd = WIN_FindWndPtr( hwnd );
2029 if (!wnd) return 0;
2030 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2032 if (msg == WM_CREATE)
2034 if (!LISTBOX_Create( wnd )) return -1;
2035 dprintf_listbox( stddeb, "Listbox: creating wnd=%04x descr=%p\n",
2036 hwnd, *(LB_DESCR **)wnd->wExtra );
2037 return 0;
2039 /* Ignore all other messages before we get a WM_CREATE */
2040 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2043 dprintf_listbox( stddeb, "Listbox %04x: msg %s wp %08x lp %08lx\n",
2044 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2045 switch(msg)
2047 case LB_RESETCONTENT16:
2048 case LB_RESETCONTENT32:
2049 LISTBOX_ResetContent( wnd, descr );
2050 return 0;
2052 case LB_ADDSTRING16:
2053 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2054 /* fall through */
2055 case LB_ADDSTRING32:
2056 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2057 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2059 case LB_INSERTSTRING16:
2060 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2061 wParam = (INT32)(INT16)wParam;
2062 /* fall through */
2063 case LB_INSERTSTRING32:
2064 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2066 case LB_ADDFILE16:
2067 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2068 /* fall through */
2069 case LB_ADDFILE32:
2070 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2071 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2073 case LB_DELETESTRING16:
2074 case LB_DELETESTRING32:
2075 return LISTBOX_RemoveItem( wnd, descr, wParam );
2077 case LB_GETITEMDATA16:
2078 case LB_GETITEMDATA32:
2079 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2080 return LB_ERR;
2081 return descr->items[wParam].data;
2083 case LB_SETITEMDATA16:
2084 case LB_SETITEMDATA32:
2085 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2086 return LB_ERR;
2087 descr->items[wParam].data = (DWORD)lParam;
2088 return LB_OKAY;
2090 case LB_GETCOUNT16:
2091 case LB_GETCOUNT32:
2092 return descr->nb_items;
2094 case LB_GETTEXT16:
2095 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2096 /* fall through */
2097 case LB_GETTEXT32:
2098 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2100 case LB_GETTEXTLEN16:
2101 /* fall through */
2102 case LB_GETTEXTLEN32:
2103 if (wParam >= descr->nb_items) return LB_ERR;
2104 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2105 : sizeof(DWORD));
2107 case LB_GETCURSEL16:
2108 case LB_GETCURSEL32:
2109 return descr->selected_item;
2111 case LB_GETTOPINDEX16:
2112 case LB_GETTOPINDEX32:
2113 return descr->top_item;
2115 case LB_GETITEMHEIGHT16:
2116 case LB_GETITEMHEIGHT32:
2117 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2119 case LB_SETITEMHEIGHT16:
2120 lParam = LOWORD(lParam);
2121 /* fall through */
2122 case LB_SETITEMHEIGHT32:
2123 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2125 case LB_ITEMFROMPOINT32:
2127 POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
2128 RECT32 rect = { 0, 0, descr->width, descr->height };
2129 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2130 PtInRect32( &rect, pt ) );
2133 case LB_SETCARETINDEX16:
2134 case LB_SETCARETINDEX32:
2135 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2137 case LB_GETCARETINDEX16:
2138 case LB_GETCARETINDEX32:
2139 return descr->focus_item;
2141 case LB_SETTOPINDEX16:
2142 case LB_SETTOPINDEX32:
2143 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2145 case LB_SETCOLUMNWIDTH16:
2146 case LB_SETCOLUMNWIDTH32:
2147 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2149 case LB_GETITEMRECT16:
2151 RECT32 rect;
2152 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2153 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2155 return ret;
2157 case LB_GETITEMRECT32:
2158 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam );
2160 case LB_FINDSTRING16:
2161 wParam = (INT32)(INT16)wParam;
2162 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2163 /* fall through */
2164 case LB_FINDSTRING32:
2165 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2167 case LB_FINDSTRINGEXACT16:
2168 wParam = (INT32)(INT16)wParam;
2169 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2170 /* fall through */
2171 case LB_FINDSTRINGEXACT32:
2172 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2174 case LB_SELECTSTRING16:
2175 wParam = (INT32)(INT16)wParam;
2176 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2177 /* fall through */
2178 case LB_SELECTSTRING32:
2180 INT32 index = LISTBOX_FindString( wnd, descr, wParam,
2181 (LPCSTR)lParam, FALSE );
2182 if (index == LB_ERR) return LB_ERR;
2183 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2184 return index;
2187 case LB_GETSEL16:
2188 wParam = (INT32)(INT16)wParam;
2189 /* fall through */
2190 case LB_GETSEL32:
2191 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2192 return LB_ERR;
2193 return descr->items[wParam].selected;
2195 case LB_SETSEL16:
2196 lParam = (INT32)(INT16)lParam;
2197 /* fall through */
2198 case LB_SETSEL32:
2199 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2201 case LB_SETCURSEL16:
2202 wParam = (INT32)(INT16)wParam;
2203 /* fall through */
2204 case LB_SETCURSEL32:
2205 if (wParam != -1) LISTBOX_MakeItemVisible( wnd, descr, wParam, TRUE );
2206 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2208 case LB_GETSELCOUNT16:
2209 case LB_GETSELCOUNT32:
2210 return LISTBOX_GetSelCount( wnd, descr );
2212 case LB_GETSELITEMS16:
2213 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2214 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2216 case LB_GETSELITEMS32:
2217 return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam );
2219 case LB_SELITEMRANGE16:
2220 case LB_SELITEMRANGE32:
2221 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2222 HIWORD(lParam), wParam );
2224 case LB_SELITEMRANGEEX16:
2225 case LB_SELITEMRANGEEX32:
2226 if ((INT32)lParam >= (INT32)wParam)
2227 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2228 else
2229 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2231 case LB_GETHORIZONTALEXTENT16:
2232 case LB_GETHORIZONTALEXTENT32:
2233 return descr->horz_extent;
2235 case LB_SETHORIZONTALEXTENT16:
2236 case LB_SETHORIZONTALEXTENT32:
2237 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2239 case LB_GETANCHORINDEX16:
2240 case LB_GETANCHORINDEX32:
2241 return descr->anchor_item;
2243 case LB_SETANCHORINDEX16:
2244 wParam = (INT32)(INT16)wParam;
2245 /* fall through */
2246 case LB_SETANCHORINDEX32:
2247 if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items))
2248 return LB_ERR;
2249 descr->anchor_item = (INT32)wParam;
2250 return LB_OKAY;
2252 case LB_DIR16:
2253 return LISTBOX_Directory( wnd, descr, wParam,
2254 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2256 case LB_DIR32:
2257 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2259 case LB_GETLOCALE32:
2260 return descr->locale;
2262 case LB_SETLOCALE32:
2263 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2264 return LB_OKAY;
2266 case LB_INITSTORAGE32:
2267 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2269 case LB_SETCOUNT32:
2270 return LISTBOX_SetCount( wnd, descr, (INT32)wParam );
2272 case LB_SETTABSTOPS16:
2273 return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam,
2274 (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE );
2276 case LB_SETTABSTOPS32:
2277 return LISTBOX_SetTabStops( wnd, descr, wParam,
2278 (LPINT32)lParam, FALSE );
2280 case LB_CARETON16:
2281 case LB_CARETON32:
2282 if (descr->caret_on) return LB_OKAY;
2283 descr->caret_on = TRUE;
2284 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2285 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2286 return LB_OKAY;
2288 case LB_CARETOFF16:
2289 case LB_CARETOFF32:
2290 if (!descr->caret_on) return LB_OKAY;
2291 descr->caret_on = FALSE;
2292 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2293 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2294 return LB_OKAY;
2296 case WM_DESTROY:
2297 return LISTBOX_Destroy( wnd, descr );
2299 case WM_ENABLE:
2300 InvalidateRect32( hwnd, NULL, TRUE );
2301 return 0;
2303 case WM_SETREDRAW:
2304 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2305 return 0;
2307 case WM_GETDLGCODE:
2308 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2310 case WM_PAINT:
2312 PAINTSTRUCT32 ps;
2313 HDC32 hdc = BeginPaint32( hwnd, &ps );
2314 ret = LISTBOX_Paint( wnd, descr, hdc );
2315 EndPaint32( hwnd, &ps );
2317 return ret;
2319 case WM_SIZE:
2320 LISTBOX_UpdateSize( wnd, descr );
2321 return 0;
2323 case WM_GETFONT:
2324 return descr->font;
2326 case WM_SETFONT:
2327 LISTBOX_SetFont( wnd, descr, (HFONT32)wParam );
2328 if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE );
2329 return 0;
2331 case WM_SETFOCUS:
2332 descr->caret_on = TRUE;
2333 if (descr->focus_item != -1)
2334 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2335 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2336 return 0;
2338 case WM_KILLFOCUS:
2339 if ((descr->focus_item != -1) && descr->caret_on)
2340 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2341 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2342 return 0;
2344 case WM_HSCROLL:
2345 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2347 case WM_VSCROLL:
2348 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2350 case WM_LBUTTONDOWN:
2351 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2352 (INT16)LOWORD(lParam),
2353 (INT16)HIWORD(lParam) );
2355 case WM_LBUTTONDBLCLK:
2356 if (descr->style & LBS_NOTIFY)
2357 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2358 return 0;
2360 case WM_MOUSEMOVE:
2361 if (GetCapture32() == hwnd)
2362 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2363 (INT16)HIWORD(lParam) );
2364 return 0;
2366 case WM_LBUTTONUP:
2367 return LISTBOX_HandleLButtonUp( wnd, descr );
2369 case WM_KEYDOWN:
2370 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2372 case WM_CHAR:
2373 return LISTBOX_HandleChar( wnd, descr, wParam );
2375 case WM_SYSTIMER:
2376 return LISTBOX_HandleSystemTimer( wnd, descr );
2378 case WM_ERASEBKGND:
2379 if (IS_OWNERDRAW(descr))
2381 RECT32 rect = { 0, 0, descr->width, descr->height };
2382 HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
2383 wParam, (LPARAM)wnd->hwndSelf );
2384 if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush );
2386 return 1;
2388 case WM_DROPFILES:
2389 return SendMessage32A( descr->owner, msg, wParam, lParam );
2391 case WM_DROPOBJECT:
2392 case WM_QUERYDROPOBJECT:
2393 case WM_DRAGSELECT:
2394 case WM_DRAGMOVE:
2396 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2397 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2398 dragInfo->pt.y );
2399 return SendMessage32A( descr->owner, msg, wParam, lParam );
2402 default:
2403 if ((msg >= WM_USER) && (msg < 0xc000))
2404 fprintf(stderr,"Listbox %04x: unknown msg %04x wp %08x lp %08lx\n",
2405 hwnd, msg, wParam, lParam );
2406 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2408 return 0;