Release 980329
[wine/multimedia.git] / controls / listbox.c
blob58b650ed967ec52dab903cf1e22b93d48b328f51
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <string.h>
8 #include <stdio.h>
9 #include "windows.h"
10 #include "winerror.h"
11 #include "drive.h"
12 #include "heap.h"
13 #include "spy.h"
14 #include "win.h"
15 #include "combo.h"
16 #include "debug.h"
18 /* Unimplemented yet:
19 * - LBS_NOSEL
20 * - LBS_USETABSTOPS
21 * - Unicode
22 * - Locale handling
25 /* Items array granularity */
26 #define LB_ARRAY_GRANULARITY 16
28 /* Scrolling timeout in ms */
29 #define LB_SCROLL_TIMEOUT 50
31 /* Listbox system timer id */
32 #define LB_TIMER_ID 2
34 /* Item structure */
35 typedef struct
37 LPSTR str; /* Item text */
38 BOOL32 selected; /* Is item selected? */
39 UINT32 height; /* Item height (only for OWNERDRAWVARIABLE) */
40 DWORD data; /* User data */
41 } LB_ITEMDATA;
43 /* Listbox structure */
44 typedef struct
46 HANDLE32 heap; /* Heap for this listbox */
47 HWND32 owner; /* Owner window to send notifications to */
48 UINT32 style; /* Window style */
49 INT32 width; /* Window width */
50 INT32 height; /* Window height */
51 LB_ITEMDATA *items; /* Array of items */
52 INT32 nb_items; /* Number of items */
53 INT32 top_item; /* Top visible item */
54 INT32 selected_item; /* Selected item */
55 INT32 focus_item; /* Item that has the focus */
56 INT32 anchor_item; /* Anchor item for extended selection */
57 INT32 item_height; /* Default item height */
58 INT32 page_size; /* Items per listbox page */
59 INT32 column_width; /* Column width for multi-column listboxes */
60 INT32 horz_extent; /* Horizontal extent (0 if no hscroll) */
61 INT32 horz_pos; /* Horizontal position */
62 INT32 nb_tabs; /* Number of tabs in array */
63 INT32 *tabs; /* Array of tabs */
64 BOOL32 caret_on; /* Is caret on? */
65 HFONT32 font; /* Current font */
66 LCID locale; /* Current locale for string comparisons */
67 LPHEADCOMBO lphc; /* ComboLBox */
68 } LB_DESCR;
71 #define IS_OWNERDRAW(descr) \
72 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
74 #define HAS_STRINGS(descr) \
75 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
77 #define SEND_NOTIFICATION(wnd,descr,code) \
78 (SendMessage32A( (descr)->owner, WM_COMMAND, \
79 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
81 /* Current timer status */
82 typedef enum
84 LB_TIMER_NONE,
85 LB_TIMER_UP,
86 LB_TIMER_LEFT,
87 LB_TIMER_DOWN,
88 LB_TIMER_RIGHT
89 } TIMER_DIRECTION;
91 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
94 /***********************************************************************
95 * LISTBOX_Dump
97 void LISTBOX_Dump( WND *wnd )
99 INT32 i;
100 LB_ITEMDATA *item;
101 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
103 DUMP( "Listbox:\n" );
104 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
105 wnd->hwndSelf, (UINT32)descr, descr->heap, descr->nb_items,
106 descr->top_item );
107 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
109 DUMP( "%4d: %-40s %d %08lx %3d\n",
110 i, item->str, item->selected, item->data, item->height );
115 /***********************************************************************
116 * LISTBOX_GetCurrentPageSize
118 * Return the current page size
120 static INT32 LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
122 INT32 i, height;
123 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
124 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
126 if ((height += descr->items[i].height) > descr->height) break;
128 if (i == descr->top_item) return 1;
129 else return i - descr->top_item;
133 /***********************************************************************
134 * LISTBOX_GetMaxTopIndex
136 * Return the maximum possible index for the top of the listbox.
138 static INT32 LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
140 INT32 max, page;
142 if (descr->style & LBS_OWNERDRAWVARIABLE)
144 page = descr->height;
145 for (max = descr->nb_items - 1; max >= 0; max--)
146 if ((page -= descr->items[max].height) < 0) break;
147 if (max < descr->nb_items - 1) max++;
149 else if (descr->style & LBS_MULTICOLUMN)
151 if ((page = descr->width / descr->column_width) < 1) page = 1;
152 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
153 max = (max - page) * descr->page_size;
155 else
157 max = descr->nb_items - descr->page_size;
159 if (max < 0) max = 0;
160 return max;
164 /***********************************************************************
165 * LISTBOX_UpdateScroll
167 * Update the scrollbars. Should be called whenever the content
168 * of the listbox changes.
170 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
172 SCROLLINFO info;
174 if (descr->style & LBS_NOREDRAW) return;
175 info.cbSize = sizeof(info);
177 if (descr->style & LBS_MULTICOLUMN)
179 info.nMin = 0;
180 info.nMax = (descr->nb_items - 1) / descr->page_size;
181 info.nPos = descr->top_item / descr->page_size;
182 info.nPage = descr->width / descr->column_width;
183 if (info.nPage < 1) info.nPage = 1;
184 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
185 if (descr->style & LBS_DISABLENOSCROLL)
186 info.fMask |= SIF_DISABLENOSCROLL;
187 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
188 info.nMax = 0;
189 info.fMask = SIF_RANGE;
190 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
192 else
194 info.nMin = 0;
195 info.nMax = descr->nb_items - 1;
196 info.nPos = descr->top_item;
197 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
198 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
199 if (descr->style & LBS_DISABLENOSCROLL)
200 info.fMask |= SIF_DISABLENOSCROLL;
201 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
203 if (descr->horz_extent)
205 info.nMin = 0;
206 info.nMax = descr->horz_extent - 1;
207 info.nPos = descr->horz_pos;
208 info.nPage = descr->width;
209 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
210 if (descr->style & LBS_DISABLENOSCROLL)
211 info.fMask |= SIF_DISABLENOSCROLL;
212 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
218 /***********************************************************************
219 * LISTBOX_SetTopItem
221 * Set the top item of the listbox, scrolling up or down if necessary.
223 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index,
224 BOOL32 scroll )
226 INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr );
227 if (index > max) index = max;
228 if (index < 0) index = 0;
229 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
230 if (descr->top_item == index) return LB_OKAY;
231 if (descr->style & LBS_MULTICOLUMN)
233 INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width;
234 if (scroll && (abs(diff) < descr->width))
235 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
236 SW_INVALIDATE | SW_ERASE );
237 else
238 scroll = FALSE;
240 else if (scroll)
242 INT32 diff;
243 if (descr->style & LBS_OWNERDRAWVARIABLE)
245 INT32 i;
246 diff = 0;
247 if (index > descr->top_item)
249 for (i = index - 1; i >= descr->top_item; i--)
250 diff -= descr->items[i].height;
252 else
254 for (i = index; i < descr->top_item; i++)
255 diff += descr->items[i].height;
258 else
259 diff = (descr->top_item - index) * descr->item_height;
261 if (abs(diff) < descr->height)
262 ScrollWindowEx32( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
263 SW_INVALIDATE | SW_ERASE );
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 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
312 wnd->hwndSelf, descr->height,
313 descr->height - descr->height%descr->item_height );
314 SetWindowPos32( 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 TRACE(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 rect->right += descr->horz_pos;
354 if ((index >= 0) && (index < descr->nb_items))
356 if (index < descr->top_item)
358 for (i = descr->top_item-1; i >= index; i--)
359 rect->top -= descr->items[i].height;
361 else
363 for (i = descr->top_item; i < index; i++)
364 rect->top += descr->items[i].height;
366 rect->bottom = rect->top + descr->items[index].height;
370 else
372 rect->top += (index - descr->top_item) * descr->item_height;
373 rect->bottom = rect->top + descr->item_height;
374 rect->right += descr->horz_pos;
377 return ((rect->left < descr->width) && (rect->right > 0) &&
378 (rect->top < descr->height) && (rect->bottom > 0));
382 /***********************************************************************
383 * LISTBOX_GetItemFromPoint
385 * Return the item nearest from point (x,y) (in client coordinates).
387 static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
388 INT32 x, INT32 y )
390 INT32 index = descr->top_item;
392 if (!descr->nb_items) return -1; /* No items */
393 if (descr->style & LBS_OWNERDRAWVARIABLE)
395 INT32 pos = 0;
396 if (y >= 0)
398 while (index < descr->nb_items)
400 if ((pos += descr->items[index].height) > y) break;
401 index++;
404 else
406 while (index > 0)
408 index--;
409 if ((pos -= descr->items[index].height) <= y) break;
413 else if (descr->style & LBS_MULTICOLUMN)
415 if (y >= descr->item_height * descr->page_size) return -1;
416 if (y >= 0) index += y / descr->item_height;
417 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
418 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
420 else
422 index += (y / descr->item_height);
424 if (index < 0) return 0;
425 if (index >= descr->nb_items) return -1;
426 return index;
430 /***********************************************************************
431 * LISTBOX_PaintItem
433 * Paint an item.
435 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc,
436 const RECT32 *rect, INT32 index, UINT32 action )
438 LB_ITEMDATA *item = NULL;
439 if (index < descr->nb_items) item = &descr->items[index];
441 if (IS_OWNERDRAW(descr))
443 DRAWITEMSTRUCT32 dis;
444 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
446 dis.CtlType = ODT_LISTBOX;
447 dis.CtlID = id;
448 dis.hwndItem = wnd->hwndSelf;
449 dis.itemAction = action;
450 dis.hDC = hdc;
451 dis.itemID = index;
452 dis.itemState = 0;
453 if (item && item->selected) dis.itemState |= ODS_SELECTED;
454 if ((descr->focus_item == index) &&
455 (descr->caret_on) &&
456 (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
457 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
458 dis.itemData = item ? item->data : 0;
459 dis.rcItem = *rect;
460 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
461 "state=%02x rect=%d,%d-%d,%d\n",
462 wnd->hwndSelf, index, item ? item->str : "", action,
463 dis.itemState, rect->left, rect->top,
464 rect->right, rect->bottom );
465 SendMessage32A(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
467 else
469 COLORREF oldText = 0, oldBk = 0;
471 if (action == ODA_FOCUS)
473 DrawFocusRect32( hdc, rect );
474 return;
476 if (item && item->selected)
478 oldBk = SetBkColor32( hdc, GetSysColor32( COLOR_HIGHLIGHT ) );
479 oldText = SetTextColor32( hdc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
482 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
483 "rect=%d,%d-%d,%d\n",
484 wnd->hwndSelf, index, item ? item->str : "", action,
485 rect->left, rect->top, rect->right, rect->bottom );
486 if (!item)
487 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
488 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
489 else if (!(descr->style & LBS_USETABSTOPS))
490 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
491 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
492 strlen(item->str), NULL );
493 else
495 /* Output empty string to paint background in the full width. */
496 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
497 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
498 TabbedTextOut32A( hdc, rect->left + 1 , rect->top + 1,
499 item->str, strlen(item->str),
500 descr->nb_tabs, descr->tabs, 0);
502 if (item && item->selected)
504 SetBkColor32( hdc, oldBk );
505 SetTextColor32( hdc, oldText );
507 if ((descr->focus_item == index) &&
508 (descr->caret_on) &&
509 (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect );
514 /***********************************************************************
515 * LISTBOX_SetRedraw
517 * Change the redraw flag.
519 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on )
521 if (on)
523 if (!(descr->style & LBS_NOREDRAW)) return;
524 descr->style &= ~LBS_NOREDRAW;
525 LISTBOX_UpdateScroll( wnd, descr );
527 else descr->style |= LBS_NOREDRAW;
531 /***********************************************************************
532 * LISTBOX_RepaintItem
534 * Repaint a single item synchronously.
536 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index,
537 UINT32 action )
539 HDC32 hdc;
540 RECT32 rect;
541 HFONT32 oldFont = 0;
542 HBRUSH32 hbrush, oldBrush = 0;
544 if (descr->style & LBS_NOREDRAW) return;
545 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
546 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE ))) return;
547 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
548 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
549 hdc, (LPARAM)wnd->hwndSelf );
550 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
551 if (wnd->dwStyle & WS_DISABLED)
552 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
553 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
554 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
555 if (oldFont) SelectObject32( hdc, oldFont );
556 if (oldBrush) SelectObject32( hdc, oldBrush );
557 ReleaseDC32( wnd->hwndSelf, hdc );
561 /***********************************************************************
562 * LISTBOX_InitStorage
564 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items,
565 DWORD bytes )
567 LB_ITEMDATA *item;
569 nb_items += LB_ARRAY_GRANULARITY - 1;
570 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
571 if (descr->items)
572 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
573 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
574 nb_items * sizeof(LB_ITEMDATA) )))
576 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
577 return LB_ERRSPACE;
579 descr->items = item;
580 return LB_OKAY;
584 /***********************************************************************
585 * LISTBOX_SetTabStops
587 static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count,
588 LPINT32 tabs, BOOL32 short_ints )
590 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
591 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
592 if (!(descr->nb_tabs = count))
594 descr->tabs = NULL;
595 return TRUE;
597 /* FIXME: count = 1 */
598 if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0,
599 descr->nb_tabs * sizeof(INT32) )))
600 return FALSE;
601 if (short_ints)
603 INT32 i;
604 LPINT16 p = (LPINT16)tabs;
605 dbg_decl_str(listbox, 256);
607 for (i = 0; i < descr->nb_tabs; i++) {
608 descr->tabs[i] = *p++<<1; /* FIXME */
609 if(TRACE_ON(listbox))
610 dsprintf(listbox, "%hd ", descr->tabs[i]);
612 TRACE(listbox, "[%04x]: settabstops %s\n",
613 wnd->hwndSelf, dbg_str(listbox));
615 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) );
616 /* FIXME: repaint the window? */
617 return TRUE;
621 /***********************************************************************
622 * LISTBOX_GetText
624 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index,
625 LPSTR buffer )
627 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
628 if (HAS_STRINGS(descr))
630 lstrcpy32A( buffer, descr->items[index].str );
631 return strlen(buffer);
633 else
635 memcpy( buffer, &descr->items[index].data, sizeof(DWORD) );
636 return sizeof(DWORD);
641 /***********************************************************************
642 * LISTBOX_FindStringPos
644 * Find the nearest string located before a given string in sort order.
645 * If 'exact' is TRUE, return an error if we don't get an exact match.
647 static INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
648 BOOL32 exact )
650 INT32 index, min, max, res = -1;
652 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
653 min = 0;
654 max = descr->nb_items;
655 while (min != max)
657 index = (min + max) / 2;
658 if (HAS_STRINGS(descr))
659 res = lstrcmpi32A( descr->items[index].str, str );
660 else
662 COMPAREITEMSTRUCT32 cis;
663 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
665 cis.CtlType = ODT_LISTBOX;
666 cis.CtlID = id;
667 cis.hwndItem = wnd->hwndSelf;
668 cis.itemID1 = index;
669 cis.itemData1 = descr->items[index].data;
670 cis.itemID2 = -1;
671 cis.itemData2 = (DWORD)str;
672 cis.dwLocaleId = descr->locale;
673 res = SendMessage32A( descr->owner, WM_COMPAREITEM,
674 id, (LPARAM)&cis );
676 if (!res) return index;
677 if (res > 0) max = index;
678 else min = index + 1;
680 return exact ? -1 : max;
684 /***********************************************************************
685 * LISTBOX_FindFileStrPos
687 * Find the nearest string located before a given string in directory
688 * sort order (i.e. first files, then directories, then drives).
690 static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
692 INT32 min, max, res = -1;
694 if (!HAS_STRINGS(descr))
695 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
696 min = 0;
697 max = descr->nb_items;
698 while (min != max)
700 INT32 index = (min + max) / 2;
701 const char *p = descr->items[index].str;
702 if (*p == '[') /* drive or directory */
704 if (*str != '[') res = -1;
705 else if (p[1] == '-') /* drive */
707 if (str[1] == '-') res = str[2] - p[2];
708 else res = -1;
710 else /* directory */
712 if (str[1] == '-') res = 1;
713 else res = lstrcmpi32A( str, p );
716 else /* filename */
718 if (*str == '[') res = 1;
719 else res = lstrcmpi32A( str, p );
721 if (!res) return index;
722 if (res < 0) max = index;
723 else min = index + 1;
725 return max;
729 /***********************************************************************
730 * LISTBOX_FindString
732 * Find the item beginning with a given string.
734 static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start,
735 LPCSTR str, BOOL32 exact )
737 INT32 i;
738 LB_ITEMDATA *item;
740 if (start >= descr->nb_items) start = -1;
741 item = descr->items + start + 1;
742 if (HAS_STRINGS(descr))
744 if (!str) return LB_ERR;
745 if (exact)
747 for (i = start + 1; i < descr->nb_items; i++, item++)
748 if (!lstrcmpi32A( str, item->str )) return i;
749 for (i = 0, item = descr->items; i <= start; i++, item++)
750 if (!lstrcmpi32A( str, item->str )) return i;
752 else
754 /* Special case for drives and directories: ignore prefix */
755 #define CHECK_DRIVE(item) \
756 if ((item)->str[0] == '[') \
758 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
759 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
760 return i; \
763 INT32 len = strlen(str);
764 for (i = start + 1; i < descr->nb_items; i++, item++)
766 if (!lstrncmpi32A( str, item->str, len )) return i;
767 CHECK_DRIVE(item);
769 for (i = 0, item = descr->items; i <= start; i++, item++)
771 if (!lstrncmpi32A( str, item->str, len )) return i;
772 CHECK_DRIVE(item);
774 #undef CHECK_DRIVE
777 else
779 if (exact && (descr->style & LBS_SORT))
780 /* If sorted, use a WM_COMPAREITEM binary search */
781 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
783 /* Otherwise use a linear search */
784 for (i = start + 1; i < descr->nb_items; i++, item++)
785 if (item->data == (DWORD)str) return i;
786 for (i = 0, item = descr->items; i <= start; i++, item++)
787 if (item->data == (DWORD)str) return i;
789 return LB_ERR;
793 /***********************************************************************
794 * LISTBOX_GetSelCount
796 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
798 INT32 i, count;
799 LB_ITEMDATA *item = descr->items;
801 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
802 for (i = count = 0; i < descr->nb_items; i++, item++)
803 if (item->selected) count++;
804 return count;
808 /***********************************************************************
809 * LISTBOX_GetSelItems16
811 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
812 LPINT16 array )
814 INT32 i, count;
815 LB_ITEMDATA *item = descr->items;
817 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
818 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
819 if (item->selected) array[count++] = (INT16)i;
820 return count;
824 /***********************************************************************
825 * LISTBOX_GetSelItems32
827 static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max,
828 LPINT32 array )
830 INT32 i, count;
831 LB_ITEMDATA *item = descr->items;
833 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
834 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
835 if (item->selected) array[count++] = i;
836 return count;
840 /***********************************************************************
841 * LISTBOX_Paint
843 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc )
845 INT32 i, col_pos = descr->page_size - 1;
846 RECT32 rect;
847 HFONT32 oldFont = 0;
848 HBRUSH32 hbrush, oldBrush = 0;
850 SetRect32( &rect, 0, 0, descr->width, descr->height );
851 if (descr->style & LBS_NOREDRAW) return 0;
852 if (descr->style & LBS_MULTICOLUMN)
853 rect.right = rect.left + descr->column_width;
854 else if (descr->horz_pos)
856 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
857 rect.right += descr->horz_pos;
860 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
861 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
862 hdc, (LPARAM)wnd->hwndSelf );
863 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
864 if (wnd->dwStyle & WS_DISABLED)
865 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
867 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
868 (GetFocus32() == wnd->hwndSelf))
870 /* Special case for empty listbox: paint focus rect */
871 rect.bottom = rect.top + descr->item_height;
872 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
873 ODA_DRAWENTIRE );
874 rect.top = rect.bottom;
877 for (i = descr->top_item; i < descr->nb_items; i++)
879 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
880 rect.bottom = rect.top + descr->item_height;
881 else
882 rect.bottom = rect.top + descr->items[i].height;
884 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
885 rect.top = rect.bottom;
887 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
889 if (!IS_OWNERDRAW(descr))
891 /* Clear the bottom of the column */
892 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
893 if (rect.top < descr->height)
895 rect.bottom = descr->height;
896 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
897 &rect, NULL, 0, NULL );
901 /* Go to the next column */
902 rect.left += descr->column_width;
903 rect.right += descr->column_width;
904 rect.top = 0;
905 col_pos = descr->page_size - 1;
907 else
909 col_pos--;
910 if (rect.top >= descr->height) break;
914 if (!IS_OWNERDRAW(descr))
916 /* Clear the remainder of the client area */
917 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
918 if (rect.top < descr->height)
920 rect.bottom = descr->height;
921 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
922 &rect, NULL, 0, NULL );
924 if (rect.right < descr->width)
926 rect.left = rect.right;
927 rect.right = descr->width;
928 rect.top = 0;
929 rect.bottom = descr->height;
930 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
931 &rect, NULL, 0, NULL );
934 if (oldFont) SelectObject32( hdc, oldFont );
935 if (oldBrush) SelectObject32( hdc, oldBrush );
936 return 0;
940 /***********************************************************************
941 * LISTBOX_InvalidateItems
943 * Invalidate all items from a given item. If the specified item is not
944 * visible, nothing happens.
946 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index )
948 RECT32 rect;
950 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
952 rect.bottom = descr->height;
953 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
954 if (descr->style & LBS_MULTICOLUMN)
956 /* Repaint the other columns */
957 rect.left = rect.right;
958 rect.right = descr->width;
959 rect.top = 0;
960 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
966 /***********************************************************************
967 * LISTBOX_GetItemHeight
969 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index )
971 if (descr->style & LBS_OWNERDRAWVARIABLE)
973 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
974 return descr->items[index].height;
976 else return descr->item_height;
980 /***********************************************************************
981 * LISTBOX_SetItemHeight
983 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index,
984 UINT32 height )
986 if (!height) height = 1;
988 if (descr->style & LBS_OWNERDRAWVARIABLE)
990 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
991 TRACE(listbox, "[%04x]: item %d height = %d\n",
992 wnd->hwndSelf, index, height );
993 descr->items[index].height = height;
994 LISTBOX_UpdateScroll( wnd, descr );
995 LISTBOX_InvalidateItems( wnd, descr, index );
997 else if (height != descr->item_height)
999 TRACE(listbox, "[%04x]: new height = %d\n",
1000 wnd->hwndSelf, height );
1001 descr->item_height = height;
1002 LISTBOX_UpdatePage( wnd, descr );
1003 LISTBOX_UpdateScroll( wnd, descr );
1004 InvalidateRect32( wnd->hwndSelf, 0, TRUE );
1006 return LB_OKAY;
1010 /***********************************************************************
1011 * LISTBOX_SetHorizontalPos
1013 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos )
1015 INT32 diff;
1017 if (pos > descr->horz_extent - descr->width)
1018 pos = descr->horz_extent - descr->width;
1019 if (pos < 0) pos = 0;
1020 if (!(diff = descr->horz_pos - pos)) return;
1021 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1022 wnd->hwndSelf, pos );
1023 descr->horz_pos = pos;
1024 LISTBOX_UpdateScroll( wnd, descr );
1025 if (abs(diff) < descr->width)
1026 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1027 SW_INVALIDATE | SW_ERASE );
1028 else
1029 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1033 /***********************************************************************
1034 * LISTBOX_SetHorizontalExtent
1036 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1037 UINT32 extent )
1039 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1040 return LB_OKAY;
1041 if (extent <= 0) extent = 1;
1042 if (extent == descr->horz_extent) return LB_OKAY;
1043 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1044 wnd->hwndSelf, extent );
1045 descr->horz_extent = extent;
1046 if (descr->horz_pos > extent - descr->width)
1047 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1048 else
1049 LISTBOX_UpdateScroll( wnd, descr );
1050 return LB_OKAY;
1054 /***********************************************************************
1055 * LISTBOX_SetColumnWidth
1057 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width)
1059 width += 2; /* For left and right margin */
1060 if (width == descr->column_width) return LB_OKAY;
1061 TRACE(listbox, "[%04x]: new column width = %d\n",
1062 wnd->hwndSelf, width );
1063 descr->column_width = width;
1064 LISTBOX_UpdatePage( wnd, descr );
1065 return LB_OKAY;
1069 /***********************************************************************
1070 * LISTBOX_SetFont
1072 * Returns the item height.
1074 static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font )
1076 HDC32 hdc;
1077 HFONT32 oldFont = 0;
1078 TEXTMETRIC32A tm;
1080 descr->font = font;
1082 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
1084 ERR(listbox, "unable to get DC.\n" );
1085 return 16;
1087 if (font) oldFont = SelectObject32( hdc, font );
1088 GetTextMetrics32A( hdc, &tm );
1089 if (oldFont) SelectObject32( hdc, oldFont );
1090 ReleaseDC32( wnd->hwndSelf, hdc );
1091 if (!IS_OWNERDRAW(descr))
1092 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight + 2 );
1093 return tm.tmHeight + 2;
1097 /***********************************************************************
1098 * LISTBOX_MakeItemVisible
1100 * Make sure that a given item is partially or fully visible.
1102 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index,
1103 BOOL32 fully )
1105 INT32 top;
1107 if (index <= descr->top_item) top = index;
1108 else if (descr->style & LBS_MULTICOLUMN)
1110 INT32 cols = descr->width;
1111 if (!fully) cols += descr->column_width - 1;
1112 if (cols >= descr->column_width) cols /= descr->column_width;
1113 else cols = 1;
1114 if (index < descr->top_item + (descr->page_size * cols)) return;
1115 top = index - descr->page_size * (cols - 1);
1117 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1119 INT32 height = fully ? descr->items[index].height : 1;
1120 for (top = index; top > descr->top_item; top--)
1121 if ((height += descr->items[top-1].height) > descr->height) break;
1123 else
1125 if (index < descr->top_item + descr->page_size) return;
1126 if (!fully && (index == descr->top_item + descr->page_size) &&
1127 (descr->height > (descr->page_size * descr->item_height))) return;
1128 top = index - descr->page_size + 1;
1130 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1134 /***********************************************************************
1135 * LISTBOX_SelectItemRange
1137 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1139 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first,
1140 INT32 last, BOOL32 on )
1142 INT32 i;
1144 /* A few sanity checks */
1146 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1147 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1148 if (last == -1) last = descr->nb_items - 1;
1149 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1150 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1151 /* selected_item reflects last selected/unselected item on multiple sel */
1152 descr->selected_item = last;
1154 if (on) /* Turn selection on */
1156 for (i = first; i <= last; i++)
1158 if (descr->items[i].selected) continue;
1159 descr->items[i].selected = TRUE;
1160 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1163 else /* Turn selection off */
1165 for (i = first; i <= last; i++)
1167 if (!descr->items[i].selected) continue;
1168 descr->items[i].selected = FALSE;
1169 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1172 return LB_OKAY;
1176 /***********************************************************************
1177 * LISTBOX_SetCaretIndex
1179 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index,
1180 BOOL32 fully_visible )
1182 INT32 oldfocus = descr->focus_item;
1184 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1185 if (index == oldfocus) return LB_OKAY;
1186 descr->focus_item = index;
1187 if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1188 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1189 if (index != -1)
1191 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1192 if (descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1193 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1195 return LB_OKAY;
1199 /***********************************************************************
1200 * LISTBOX_SetSelection
1202 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index,
1203 BOOL32 on, BOOL32 send_notify )
1205 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1206 if (descr->style & LBS_MULTIPLESEL)
1208 if (index == -1) /* Select all items */
1209 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1210 else /* Only one item */
1211 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1213 else
1215 INT32 oldsel = descr->selected_item;
1216 if (index == oldsel) return LB_OKAY;
1217 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1218 if (index != -1) descr->items[index].selected = TRUE;
1219 descr->selected_item = index;
1220 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1221 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1222 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1223 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1225 return LB_OKAY;
1229 /***********************************************************************
1230 * LISTBOX_MoveCaret
1232 * Change the caret position and extend the selection to the new caret.
1234 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index,
1235 BOOL32 fully_visible )
1237 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1238 if (descr->style & LBS_EXTENDEDSEL)
1240 if (descr->anchor_item != -1)
1242 INT32 first = MIN( descr->focus_item, descr->anchor_item );
1243 INT32 last = MAX( descr->focus_item, descr->anchor_item );
1244 if (first > 0)
1245 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1246 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1247 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1250 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1252 /* Set selection to new caret item */
1253 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1258 /***********************************************************************
1259 * LISTBOX_InsertItem
1261 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index,
1262 LPSTR str, DWORD data )
1264 LB_ITEMDATA *item;
1265 INT32 max_items;
1267 if (index == -1) index = descr->nb_items;
1268 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1269 if (!descr->items) max_items = 0;
1270 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1271 if (descr->nb_items == max_items)
1273 /* We need to grow the array */
1274 max_items += LB_ARRAY_GRANULARITY;
1275 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1276 max_items * sizeof(LB_ITEMDATA) )))
1278 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1279 return LB_ERRSPACE;
1281 descr->items = item;
1284 /* Insert the item structure */
1286 item = &descr->items[index];
1287 if (index < descr->nb_items)
1288 RtlMoveMemory( item + 1, item,
1289 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1290 item->str = str;
1291 item->data = data;
1292 item->height = 0;
1293 item->selected = FALSE;
1294 descr->nb_items++;
1296 /* Get item height */
1298 if (descr->style & LBS_OWNERDRAWVARIABLE)
1300 MEASUREITEMSTRUCT32 mis;
1301 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1303 mis.CtlType = ODT_LISTBOX;
1304 mis.CtlID = id;
1305 mis.itemID = index;
1306 mis.itemData = descr->items[index].data;
1307 mis.itemHeight = descr->item_height;
1308 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1309 item->height = mis.itemHeight ? mis.itemHeight : 1;
1310 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1311 wnd->hwndSelf, index, str ? str : "", item->height );
1314 /* Repaint the items */
1316 LISTBOX_UpdateScroll( wnd, descr );
1317 LISTBOX_InvalidateItems( wnd, descr, index );
1319 /* Move selection and focused item */
1321 if (index <= descr->selected_item) descr->selected_item++;
1322 if (index <= descr->focus_item)
1324 descr->focus_item++;
1325 LISTBOX_MoveCaret( wnd, descr, descr->focus_item - 1, FALSE );
1328 /* If listbox was empty, set focus to the first item */
1330 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1331 return LB_OKAY;
1335 /***********************************************************************
1336 * LISTBOX_InsertString
1338 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index,
1339 LPCSTR str )
1341 LPSTR new_str = NULL;
1342 DWORD data = 0;
1343 LRESULT ret;
1345 if (HAS_STRINGS(descr))
1347 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1349 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1350 return LB_ERRSPACE;
1353 else data = (DWORD)str;
1355 if (index == -1) index = descr->nb_items;
1356 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1358 if (new_str) HeapFree( descr->heap, 0, new_str );
1359 return ret;
1362 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1363 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1364 return index;
1368 /***********************************************************************
1369 * LISTBOX_DeleteItem
1371 * Delete the content of an item. 'index' must be a valid index.
1373 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index )
1375 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1376 * while Win95 sends it for all items with user data.
1377 * It's probably better to send it too often than not
1378 * often enough, so this is what we do here.
1380 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1382 DELETEITEMSTRUCT32 dis;
1383 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1385 dis.CtlType = ODT_LISTBOX;
1386 dis.CtlID = id;
1387 dis.itemID = index;
1388 dis.hwndItem = wnd->hwndSelf;
1389 dis.itemData = descr->items[index].data;
1390 SendMessage32A( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1392 if (HAS_STRINGS(descr) && descr->items[index].str)
1393 HeapFree( descr->heap, 0, descr->items[index].str );
1397 /***********************************************************************
1398 * LISTBOX_RemoveItem
1400 * Remove an item from the listbox and delete its content.
1402 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index )
1404 LB_ITEMDATA *item;
1405 INT32 max_items;
1407 if (index == -1) index = descr->nb_items - 1;
1408 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1409 LISTBOX_DeleteItem( wnd, descr, index );
1411 /* Remove the item */
1413 item = &descr->items[index];
1414 if (index < descr->nb_items-1)
1415 RtlMoveMemory( item, item + 1,
1416 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1417 descr->nb_items--;
1418 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1420 /* Shrink the item array if possible */
1422 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1423 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1425 max_items -= LB_ARRAY_GRANULARITY;
1426 item = HeapReAlloc( descr->heap, 0, descr->items,
1427 max_items * sizeof(LB_ITEMDATA) );
1428 if (item) descr->items = item;
1431 /* Repaint the items */
1433 LISTBOX_UpdateScroll( wnd, descr );
1434 LISTBOX_InvalidateItems( wnd, descr, index );
1436 /* Move selection and focused item */
1438 if (index <= descr->selected_item) descr->selected_item--;
1439 if (index <= descr->focus_item)
1441 descr->focus_item--;
1442 LISTBOX_MoveCaret( wnd, descr, descr->focus_item + 1, FALSE );
1444 return LB_OKAY;
1448 /***********************************************************************
1449 * LISTBOX_ResetContent
1451 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1453 INT32 i;
1455 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1456 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1457 descr->nb_items = 0;
1458 descr->top_item = 0;
1459 descr->selected_item = -1;
1460 descr->focus_item = 0;
1461 descr->anchor_item = -1;
1462 descr->items = NULL;
1463 LISTBOX_UpdateScroll( wnd, descr );
1464 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1468 /***********************************************************************
1469 * LISTBOX_SetCount
1471 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count )
1473 LRESULT ret;
1475 if (HAS_STRINGS(descr)) return LB_ERR;
1476 /* FIXME: this is far from optimal... */
1477 if (count > descr->nb_items)
1479 while (count > descr->nb_items)
1480 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1481 return ret;
1483 else if (count < descr->nb_items)
1485 while (count < descr->nb_items)
1486 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1487 return ret;
1489 return LB_OKAY;
1493 /***********************************************************************
1494 * LISTBOX_Directory
1496 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib,
1497 LPCSTR filespec, BOOL32 long_names )
1499 HANDLE32 handle;
1500 LRESULT ret = LB_OKAY;
1501 WIN32_FIND_DATA32A entry;
1502 int pos;
1504 if ((handle = FindFirstFile32A(filespec,&entry)) == INVALID_HANDLE_VALUE32)
1506 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1508 else
1512 char buffer[270];
1513 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1515 if (!(attrib & DDL_DIRECTORY) ||
1516 !strcmp( entry.cAlternateFileName, "." )) continue;
1517 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1518 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1520 else /* not a directory */
1522 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1523 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1525 if ((attrib & DDL_EXCLUSIVE) &&
1526 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1527 continue;
1528 #undef ATTRIBS
1529 if (long_names) strcpy( buffer, entry.cFileName );
1530 else strcpy( buffer, entry.cAlternateFileName );
1532 if (!long_names) CharLower32A( buffer );
1533 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1534 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1535 break;
1536 } while (FindNextFile32A( handle, &entry ));
1537 FindClose32( handle );
1540 if ((ret >= 0) && (attrib & DDL_DRIVES))
1542 char buffer[] = "[-a-]";
1543 int drive;
1544 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1546 if (!DRIVE_IsValid(drive)) continue;
1547 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1548 break;
1551 return ret;
1555 /***********************************************************************
1556 * LISTBOX_HandleVScroll
1558 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1559 WPARAM32 wParam, LPARAM lParam )
1561 SCROLLINFO info;
1563 if (descr->style & LBS_MULTICOLUMN) return 0;
1564 switch(LOWORD(wParam))
1566 case SB_LINEUP:
1567 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1568 break;
1569 case SB_LINEDOWN:
1570 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1571 break;
1572 case SB_PAGEUP:
1573 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1574 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1575 break;
1576 case SB_PAGEDOWN:
1577 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1578 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1579 break;
1580 case SB_THUMBPOSITION:
1581 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1582 break;
1583 case SB_THUMBTRACK:
1584 info.cbSize = sizeof(info);
1585 info.fMask = SIF_TRACKPOS;
1586 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1587 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1588 break;
1589 case SB_TOP:
1590 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1591 break;
1592 case SB_BOTTOM:
1593 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1594 break;
1596 return 0;
1600 /***********************************************************************
1601 * LISTBOX_HandleHScroll
1603 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1604 WPARAM32 wParam, LPARAM lParam )
1606 SCROLLINFO info;
1607 INT32 page;
1609 if (descr->style & LBS_MULTICOLUMN)
1611 switch(LOWORD(wParam))
1613 case SB_LINELEFT:
1614 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1615 TRUE );
1616 break;
1617 case SB_LINERIGHT:
1618 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1619 TRUE );
1620 break;
1621 case SB_PAGELEFT:
1622 page = descr->width / descr->column_width;
1623 if (page < 1) page = 1;
1624 LISTBOX_SetTopItem( wnd, descr,
1625 descr->top_item - page * descr->page_size, TRUE );
1626 break;
1627 case SB_PAGERIGHT:
1628 page = descr->width / descr->column_width;
1629 if (page < 1) page = 1;
1630 LISTBOX_SetTopItem( wnd, descr,
1631 descr->top_item + page * descr->page_size, TRUE );
1632 break;
1633 case SB_THUMBPOSITION:
1634 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1635 TRUE );
1636 break;
1637 case SB_THUMBTRACK:
1638 info.cbSize = sizeof(info);
1639 info.fMask = SIF_TRACKPOS;
1640 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1641 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1642 TRUE );
1643 break;
1644 case SB_LEFT:
1645 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1646 break;
1647 case SB_RIGHT:
1648 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1649 break;
1652 else if (descr->horz_extent)
1654 switch(LOWORD(wParam))
1656 case SB_LINELEFT:
1657 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1658 break;
1659 case SB_LINERIGHT:
1660 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1661 break;
1662 case SB_PAGELEFT:
1663 LISTBOX_SetHorizontalPos( wnd, descr,
1664 descr->horz_pos - descr->width );
1665 break;
1666 case SB_PAGERIGHT:
1667 LISTBOX_SetHorizontalPos( wnd, descr,
1668 descr->horz_pos + descr->width );
1669 break;
1670 case SB_THUMBPOSITION:
1671 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1672 break;
1673 case SB_THUMBTRACK:
1674 info.cbSize = sizeof(info);
1675 info.fMask = SIF_TRACKPOS;
1676 GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info );
1677 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1678 break;
1679 case SB_LEFT:
1680 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1681 break;
1682 case SB_RIGHT:
1683 LISTBOX_SetHorizontalPos( wnd, descr,
1684 descr->horz_extent - descr->width );
1685 break;
1688 return 0;
1692 /***********************************************************************
1693 * LISTBOX_HandleLButtonDown
1695 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1696 WPARAM32 wParam, INT32 x, INT32 y )
1698 INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1699 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1700 wnd->hwndSelf, x, y, index );
1701 if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0;
1702 if (index != -1)
1704 if (descr->style & LBS_EXTENDEDSEL)
1706 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1707 if (wParam & MK_CONTROL)
1709 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1710 LISTBOX_SetSelection( wnd, descr, index,
1711 !descr->items[index].selected, FALSE );
1713 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1715 else
1717 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1718 LISTBOX_SetSelection( wnd, descr, index,
1719 (!(descr->style & LBS_MULTIPLESEL) ||
1720 !descr->items[index].selected), FALSE );
1724 if( !descr->lphc ) SetFocus32( wnd->hwndSelf );
1725 else SetFocus32( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1726 : descr->lphc->self->hwndSelf ) ;
1728 SetCapture32( wnd->hwndSelf );
1729 if (index != -1 && !descr->lphc)
1731 if (descr->style & LBS_NOTIFY )
1732 SendMessage32A( descr->owner, WM_LBTRACKPOINT, index,
1733 MAKELPARAM( x, y ) );
1734 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1736 POINT32 pt = { x, y };
1737 if (DragDetect32( wnd->hwndSelf, pt ))
1738 SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 );
1741 return 0;
1745 /***********************************************************************
1746 * LISTBOX_HandleLButtonUp
1748 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1750 if (LISTBOX_Timer != LB_TIMER_NONE)
1751 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1752 LISTBOX_Timer = LB_TIMER_NONE;
1753 if (GetCapture32() == wnd->hwndSelf)
1755 ReleaseCapture();
1756 if (descr->style & LBS_NOTIFY)
1757 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1759 return 0;
1763 /***********************************************************************
1764 * LISTBOX_HandleTimer
1766 * Handle scrolling upon a timer event.
1767 * Return TRUE if scrolling should continue.
1769 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1770 INT32 index, TIMER_DIRECTION dir )
1772 switch(dir)
1774 case LB_TIMER_UP:
1775 if (descr->top_item) index = descr->top_item - 1;
1776 else index = 0;
1777 break;
1778 case LB_TIMER_LEFT:
1779 if (descr->top_item) index -= descr->page_size;
1780 break;
1781 case LB_TIMER_DOWN:
1782 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1783 if (index == descr->focus_item) index++;
1784 if (index >= descr->nb_items) index = descr->nb_items - 1;
1785 break;
1786 case LB_TIMER_RIGHT:
1787 if (index + descr->page_size < descr->nb_items)
1788 index += descr->page_size;
1789 break;
1790 case LB_TIMER_NONE:
1791 break;
1793 if (index == descr->focus_item) return FALSE;
1794 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1795 return TRUE;
1799 /***********************************************************************
1800 * LISTBOX_HandleSystemTimer
1802 * WM_SYSTIMER handler.
1804 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1806 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1808 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1809 LISTBOX_Timer = LB_TIMER_NONE;
1811 return 0;
1815 /***********************************************************************
1816 * LISTBOX_HandleMouseMove
1818 * WM_MOUSEMOVE handler.
1820 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1821 INT32 x, INT32 y )
1823 INT32 index;
1824 TIMER_DIRECTION dir;
1826 if (descr->style & LBS_MULTICOLUMN)
1828 if (y < 0) y = 0;
1829 else if (y >= descr->item_height * descr->page_size)
1830 y = descr->item_height * descr->page_size - 1;
1832 if (x < 0)
1834 dir = LB_TIMER_LEFT;
1835 x = 0;
1837 else if (x >= descr->width)
1839 dir = LB_TIMER_RIGHT;
1840 x = descr->width - 1;
1842 else dir = LB_TIMER_NONE; /* inside */
1844 else
1846 if (y < 0) dir = LB_TIMER_UP; /* above */
1847 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1848 else dir = LB_TIMER_NONE; /* inside */
1851 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1852 if (index == -1) index = descr->focus_item;
1853 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1855 /* Start/stop the system timer */
1857 if (dir != LB_TIMER_NONE)
1858 SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1859 else if (LISTBOX_Timer != LB_TIMER_NONE)
1860 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1861 LISTBOX_Timer = dir;
1865 /***********************************************************************
1866 * LISTBOX_HandleKeyDown
1868 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM32 wParam )
1870 INT32 caret = -1;
1871 if (descr->style & LBS_WANTKEYBOARDINPUT)
1873 caret = SendMessage32A( descr->owner, WM_VKEYTOITEM,
1874 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1875 wnd->hwndSelf );
1876 if (caret == -2) return 0;
1878 if (caret == -1) switch(wParam)
1880 case VK_LEFT:
1881 if (descr->style & LBS_MULTICOLUMN)
1883 if (descr->focus_item >= descr->page_size)
1884 caret = descr->focus_item - descr->page_size;
1885 break;
1887 /* fall through */
1888 case VK_UP:
1889 caret = descr->focus_item - 1;
1890 if (caret < 0) caret = 0;
1891 break;
1892 case VK_RIGHT:
1893 if (descr->style & LBS_MULTICOLUMN)
1895 if (descr->focus_item + descr->page_size < descr->nb_items)
1896 caret = descr->focus_item + descr->page_size;
1897 break;
1899 /* fall through */
1900 case VK_DOWN:
1901 caret = descr->focus_item + 1;
1902 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1903 break;
1904 case VK_PRIOR:
1905 if (descr->style & LBS_MULTICOLUMN)
1907 INT32 page = descr->width / descr->column_width;
1908 if (page < 1) page = 1;
1909 caret = descr->focus_item - (page * descr->page_size) + 1;
1911 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1912 if (caret < 0) caret = 0;
1913 break;
1914 case VK_NEXT:
1915 if (descr->style & LBS_MULTICOLUMN)
1917 INT32 page = descr->width / descr->column_width;
1918 if (page < 1) page = 1;
1919 caret = descr->focus_item + (page * descr->page_size) - 1;
1921 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1922 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1923 break;
1924 case VK_HOME:
1925 caret = 0;
1926 break;
1927 case VK_END:
1928 caret = descr->nb_items - 1;
1929 break;
1930 case VK_SPACE:
1931 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1932 else if (descr->style & LBS_MULTIPLESEL)
1934 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1935 !descr->items[descr->focus_item].selected,
1936 (descr->style & LBS_NOTIFY) != 0 );
1938 else if (descr->selected_item == -1)
1940 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1941 (descr->style & LBS_NOTIFY) != 0 );
1943 break;
1945 if (caret >= 0)
1947 if ((descr->style & LBS_EXTENDEDSEL) &&
1948 !(GetKeyState32( VK_SHIFT ) & 0x8000))
1949 descr->anchor_item = caret;
1950 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1951 if (descr->style & LBS_NOTIFY)
1953 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1955 /* make sure that combo parent doesn't hide us */
1956 descr->lphc->wState |= CBF_NOROLLUP;
1958 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1961 return 0;
1965 /***********************************************************************
1966 * LISTBOX_HandleChar
1968 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1969 WPARAM32 wParam )
1971 INT32 caret = -1;
1972 char str[2] = { wParam & 0xff, '\0' };
1974 if (descr->style & LBS_WANTKEYBOARDINPUT)
1976 caret = SendMessage32A( descr->owner, WM_CHARTOITEM,
1977 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1978 wnd->hwndSelf );
1979 if (caret == -2) return 0;
1981 if (caret == -1)
1982 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
1983 if (caret != -1)
1985 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1986 if (descr->style & LBS_NOTIFY)
1987 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1989 return 0;
1993 /***********************************************************************
1994 * LISTBOX_Create
1996 static BOOL32 LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
1998 LB_DESCR *descr;
1999 MEASUREITEMSTRUCT32 mis;
2000 RECT32 rect;
2002 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2003 return FALSE;
2004 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2006 HeapFree( GetProcessHeap(), 0, descr );
2007 return FALSE;
2009 GetClientRect32( wnd->hwndSelf, &rect );
2010 descr->owner = GetParent32( wnd->hwndSelf );
2011 descr->style = wnd->dwStyle;
2012 descr->width = rect.right - rect.left;
2013 descr->height = rect.bottom - rect.top;
2014 descr->items = NULL;
2015 descr->nb_items = 0;
2016 descr->top_item = 0;
2017 descr->selected_item = -1;
2018 descr->focus_item = 0;
2019 descr->anchor_item = -1;
2020 descr->item_height = 1;
2021 descr->page_size = 1;
2022 descr->column_width = 150;
2023 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2024 descr->horz_pos = 0;
2025 descr->nb_tabs = 0;
2026 descr->tabs = NULL;
2027 descr->caret_on = TRUE;
2028 descr->font = 0;
2029 descr->locale = 0; /* FIXME */
2030 descr->lphc = lphc;
2032 if( lphc )
2034 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2035 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2036 descr->owner = lphc->self->hwndSelf;
2039 *(LB_DESCR **)wnd->wExtra = descr;
2041 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2043 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2044 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2045 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2046 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2048 if (descr->style & LBS_OWNERDRAWFIXED)
2050 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2052 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2053 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2055 else
2057 UINT32 id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2059 mis.CtlType = ODT_LISTBOX;
2060 mis.CtlID = id;
2061 mis.itemID = -1;
2062 mis.itemWidth = 0;
2063 mis.itemData = 0;
2064 mis.itemHeight = descr->item_height;
2065 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2066 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2070 return TRUE;
2074 /***********************************************************************
2075 * LISTBOX_Destroy
2077 static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2079 LISTBOX_ResetContent( wnd, descr );
2080 HeapDestroy( descr->heap );
2081 HeapFree( GetProcessHeap(), 0, descr );
2082 wnd->wExtra[0] = 0;
2083 return TRUE;
2087 /***********************************************************************
2088 * ListBoxWndProc
2090 LRESULT WINAPI ListBoxWndProc( HWND32 hwnd, UINT32 msg,
2091 WPARAM32 wParam, LPARAM lParam )
2093 LRESULT ret;
2094 LB_DESCR *descr;
2095 WND *wnd = WIN_FindWndPtr( hwnd );
2097 if (!wnd) return 0;
2098 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2100 if (msg == WM_CREATE)
2102 if (!LISTBOX_Create( wnd, NULL )) return -1;
2103 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2104 hwnd, *(LB_DESCR **)wnd->wExtra );
2105 return 0;
2107 /* Ignore all other messages before we get a WM_CREATE */
2108 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2111 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2112 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2113 switch(msg)
2115 case LB_RESETCONTENT16:
2116 case LB_RESETCONTENT32:
2117 LISTBOX_ResetContent( wnd, descr );
2118 return 0;
2120 case LB_ADDSTRING16:
2121 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2122 /* fall through */
2123 case LB_ADDSTRING32:
2124 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2125 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2127 case LB_INSERTSTRING16:
2128 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2129 wParam = (INT32)(INT16)wParam;
2130 /* fall through */
2131 case LB_INSERTSTRING32:
2132 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2134 case LB_ADDFILE16:
2135 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2136 /* fall through */
2137 case LB_ADDFILE32:
2138 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2139 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2141 case LB_DELETESTRING16:
2142 case LB_DELETESTRING32:
2143 return LISTBOX_RemoveItem( wnd, descr, wParam );
2145 case LB_GETITEMDATA16:
2146 case LB_GETITEMDATA32:
2147 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2148 return LB_ERR;
2149 return descr->items[wParam].data;
2151 case LB_SETITEMDATA16:
2152 case LB_SETITEMDATA32:
2153 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2154 return LB_ERR;
2155 descr->items[wParam].data = (DWORD)lParam;
2156 return LB_OKAY;
2158 case LB_GETCOUNT16:
2159 case LB_GETCOUNT32:
2160 return descr->nb_items;
2162 case LB_GETTEXT16:
2163 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2164 /* fall through */
2165 case LB_GETTEXT32:
2166 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2168 case LB_GETTEXTLEN16:
2169 /* fall through */
2170 case LB_GETTEXTLEN32:
2171 if (wParam >= descr->nb_items) return LB_ERR;
2172 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2173 : sizeof(DWORD));
2175 case LB_GETCURSEL16:
2176 case LB_GETCURSEL32:
2177 return descr->selected_item;
2179 case LB_GETTOPINDEX16:
2180 case LB_GETTOPINDEX32:
2181 return descr->top_item;
2183 case LB_GETITEMHEIGHT16:
2184 case LB_GETITEMHEIGHT32:
2185 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2187 case LB_SETITEMHEIGHT16:
2188 lParam = LOWORD(lParam);
2189 /* fall through */
2190 case LB_SETITEMHEIGHT32:
2191 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2193 case LB_ITEMFROMPOINT32:
2195 POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
2196 RECT32 rect = { 0, 0, descr->width, descr->height };
2197 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2198 PtInRect32( &rect, pt ) );
2201 case LB_SETCARETINDEX16:
2202 case LB_SETCARETINDEX32:
2203 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2205 case LB_GETCARETINDEX16:
2206 case LB_GETCARETINDEX32:
2207 return descr->focus_item;
2209 case LB_SETTOPINDEX16:
2210 case LB_SETTOPINDEX32:
2211 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2213 case LB_SETCOLUMNWIDTH16:
2214 case LB_SETCOLUMNWIDTH32:
2215 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2217 case LB_GETITEMRECT16:
2219 RECT32 rect;
2220 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2221 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2223 return ret;
2225 case LB_GETITEMRECT32:
2226 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam );
2228 case LB_FINDSTRING16:
2229 wParam = (INT32)(INT16)wParam;
2230 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2231 /* fall through */
2232 case LB_FINDSTRING32:
2233 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2235 case LB_FINDSTRINGEXACT16:
2236 wParam = (INT32)(INT16)wParam;
2237 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2238 /* fall through */
2239 case LB_FINDSTRINGEXACT32:
2240 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2242 case LB_SELECTSTRING16:
2243 wParam = (INT32)(INT16)wParam;
2244 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2245 /* fall through */
2246 case LB_SELECTSTRING32:
2248 INT32 index = LISTBOX_FindString( wnd, descr, wParam,
2249 (LPCSTR)lParam, FALSE );
2250 if (index == LB_ERR) return LB_ERR;
2251 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2252 return index;
2255 case LB_GETSEL16:
2256 wParam = (INT32)(INT16)wParam;
2257 /* fall through */
2258 case LB_GETSEL32:
2259 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2260 return LB_ERR;
2261 return descr->items[wParam].selected;
2263 case LB_SETSEL16:
2264 lParam = (INT32)(INT16)lParam;
2265 /* fall through */
2266 case LB_SETSEL32:
2267 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2269 case LB_SETCURSEL16:
2270 wParam = (INT32)(INT16)wParam;
2271 /* fall through */
2272 case LB_SETCURSEL32:
2273 if (wParam != -1) LISTBOX_MakeItemVisible( wnd, descr, wParam, TRUE );
2274 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2276 case LB_GETSELCOUNT16:
2277 case LB_GETSELCOUNT32:
2278 return LISTBOX_GetSelCount( wnd, descr );
2280 case LB_GETSELITEMS16:
2281 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2282 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2284 case LB_GETSELITEMS32:
2285 return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam );
2287 case LB_SELITEMRANGE16:
2288 case LB_SELITEMRANGE32:
2289 if (LOWORD(lParam) <= HIWORD(lParam))
2290 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2291 HIWORD(lParam), wParam );
2292 else
2293 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2294 LOWORD(lParam), wParam );
2296 case LB_SELITEMRANGEEX16:
2297 case LB_SELITEMRANGEEX32:
2298 if ((INT32)lParam >= (INT32)wParam)
2299 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2300 else
2301 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2303 case LB_GETHORIZONTALEXTENT16:
2304 case LB_GETHORIZONTALEXTENT32:
2305 return descr->horz_extent;
2307 case LB_SETHORIZONTALEXTENT16:
2308 case LB_SETHORIZONTALEXTENT32:
2309 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2311 case LB_GETANCHORINDEX16:
2312 case LB_GETANCHORINDEX32:
2313 return descr->anchor_item;
2315 case LB_SETANCHORINDEX16:
2316 wParam = (INT32)(INT16)wParam;
2317 /* fall through */
2318 case LB_SETANCHORINDEX32:
2319 if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items))
2320 return LB_ERR;
2321 descr->anchor_item = (INT32)wParam;
2322 return LB_OKAY;
2324 case LB_DIR16:
2325 return LISTBOX_Directory( wnd, descr, wParam,
2326 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2328 case LB_DIR32:
2329 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2331 case LB_GETLOCALE32:
2332 return descr->locale;
2334 case LB_SETLOCALE32:
2335 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2336 return LB_OKAY;
2338 case LB_INITSTORAGE32:
2339 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2341 case LB_SETCOUNT32:
2342 return LISTBOX_SetCount( wnd, descr, (INT32)wParam );
2344 case LB_SETTABSTOPS16:
2345 return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam,
2346 (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE );
2348 case LB_SETTABSTOPS32:
2349 return LISTBOX_SetTabStops( wnd, descr, wParam,
2350 (LPINT32)lParam, FALSE );
2352 case LB_CARETON16:
2353 case LB_CARETON32:
2354 if (descr->caret_on) return LB_OKAY;
2355 descr->caret_on = TRUE;
2356 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2357 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2358 return LB_OKAY;
2360 case LB_CARETOFF16:
2361 case LB_CARETOFF32:
2362 if (!descr->caret_on) return LB_OKAY;
2363 descr->caret_on = FALSE;
2364 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2365 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2366 return LB_OKAY;
2368 case WM_DESTROY:
2369 return LISTBOX_Destroy( wnd, descr );
2371 case WM_ENABLE:
2372 InvalidateRect32( hwnd, NULL, TRUE );
2373 return 0;
2375 case WM_SETREDRAW:
2376 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2377 return 0;
2379 case WM_GETDLGCODE:
2380 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2382 case WM_PAINT:
2384 PAINTSTRUCT32 ps;
2385 HDC32 hdc = ( wParam ) ? ((HDC32)wParam)
2386 : BeginPaint32( hwnd, &ps );
2387 ret = LISTBOX_Paint( wnd, descr, hdc );
2388 if( !wParam ) EndPaint32( hwnd, &ps );
2390 return ret;
2392 case WM_SIZE:
2393 LISTBOX_UpdateSize( wnd, descr );
2394 return 0;
2396 case WM_GETFONT:
2397 return descr->font;
2399 case WM_SETFONT:
2400 LISTBOX_SetFont( wnd, descr, (HFONT32)wParam );
2401 if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE );
2402 return 0;
2404 case WM_SETFOCUS:
2405 descr->caret_on = TRUE;
2406 if (descr->focus_item != -1)
2407 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2408 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2409 return 0;
2411 case WM_KILLFOCUS:
2412 if ((descr->focus_item != -1) && descr->caret_on)
2413 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2414 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2415 return 0;
2417 case WM_HSCROLL:
2418 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2420 case WM_VSCROLL:
2421 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2423 case WM_LBUTTONDOWN:
2424 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2425 (INT16)LOWORD(lParam),
2426 (INT16)HIWORD(lParam) );
2428 case WM_LBUTTONDBLCLK:
2429 if (descr->style & LBS_NOTIFY)
2430 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2431 return 0;
2433 case WM_MOUSEMOVE:
2434 if (GetCapture32() == hwnd)
2435 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2436 (INT16)HIWORD(lParam) );
2437 return 0;
2439 case WM_LBUTTONUP:
2440 return LISTBOX_HandleLButtonUp( wnd, descr );
2442 case WM_KEYDOWN:
2443 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2445 case WM_CHAR:
2446 return LISTBOX_HandleChar( wnd, descr, wParam );
2448 case WM_SYSTIMER:
2449 return LISTBOX_HandleSystemTimer( wnd, descr );
2451 case WM_ERASEBKGND:
2452 if (IS_OWNERDRAW(descr))
2454 RECT32 rect = { 0, 0, descr->width, descr->height };
2455 HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
2456 wParam, (LPARAM)wnd->hwndSelf );
2457 if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush );
2459 return 1;
2461 case WM_DROPFILES:
2462 if( !descr->lphc )
2463 return SendMessage32A( descr->owner, msg, wParam, lParam );
2464 break;
2466 case WM_DROPOBJECT:
2467 case WM_QUERYDROPOBJECT:
2468 case WM_DRAGSELECT:
2469 case WM_DRAGMOVE:
2470 if( !descr->lphc )
2472 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2473 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2474 dragInfo->pt.y );
2475 return SendMessage32A( descr->owner, msg, wParam, lParam );
2477 break;
2479 default:
2480 if ((msg >= WM_USER) && (msg < 0xc000))
2481 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2482 hwnd, msg, wParam, lParam );
2483 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2485 return 0;
2488 /***********************************************************************
2489 * COMBO_Directory
2491 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT32 attrib, LPSTR dir, BOOL32 bLong)
2493 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2495 if( wnd )
2497 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2498 if( descr )
2500 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2502 RedrawWindow32( lphc->self->hwndSelf, NULL, 0,
2503 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2504 return lRet;
2507 return CB_ERR;
2510 /***********************************************************************
2511 * ComboLBWndProc
2513 * NOTE: in Windows, winproc address of the ComboLBox is the same
2514 * as that of the Listbox.
2516 LRESULT WINAPI ComboLBWndProc( HWND32 hwnd, UINT32 msg,
2517 WPARAM32 wParam, LPARAM lParam )
2519 LRESULT lRet = 0;
2520 WND *wnd = WIN_FindWndPtr( hwnd );
2522 if (wnd)
2524 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2526 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2527 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2529 if( descr || msg == WM_CREATE )
2531 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2533 switch( msg )
2535 case WM_CREATE:
2536 #define lpcs ((LPCREATESTRUCT32A)lParam)
2537 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2538 (UINT32)lpcs->lpCreateParams);
2540 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2541 #undef lpcs
2542 return LISTBOX_Create( wnd, lphc );
2544 case WM_LBUTTONDOWN:
2545 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2546 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2548 /* avoid activation at all costs */
2550 case WM_MOUSEACTIVATE:
2551 return MA_NOACTIVATE;
2553 case WM_NCACTIVATE:
2554 return FALSE;
2556 case WM_KEYDOWN:
2557 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2559 /* for some reason(?) Windows makes it possible to
2560 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2562 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2563 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2564 && (wParam == VK_DOWN || wParam == VK_UP)) )
2566 COMBO_FlipListbox( lphc, FALSE );
2567 return 0;
2570 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2572 case WM_NCDESTROY:
2573 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2574 lphc->hWndLBox = 0;
2575 /* fall through */
2577 default:
2578 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2581 lRet = DefWindowProc32A( hwnd, msg, wParam, lParam );
2583 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2586 return lRet;