Release 971012
[wine/multimedia.git] / controls / listbox.c
blob165f483e1412887a8bd8a15fe6703dadb1f06b61
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 "stddebug.h"
17 #include "debug.h"
19 /* Unimplemented yet:
20 * - LBS_NOSEL
21 * - LBS_USETABSTOPS
22 * - Unicode
23 * - Locale handling
26 /* Items array granularity */
27 #define LB_ARRAY_GRANULARITY 16
29 /* Scrolling timeout in ms */
30 #define LB_SCROLL_TIMEOUT 50
32 /* Listbox system timer id */
33 #define LB_TIMER_ID 2
35 /* Item structure */
36 typedef struct
38 LPSTR str; /* Item text */
39 BOOL32 selected; /* Is item selected? */
40 UINT32 height; /* Item height (only for OWNERDRAWVARIABLE) */
41 DWORD data; /* User data */
42 } LB_ITEMDATA;
44 /* Listbox structure */
45 typedef struct
47 HANDLE32 heap; /* Heap for this listbox */
48 HWND32 owner; /* Owner window to send notifications to */
49 UINT32 style; /* Window style */
50 INT32 width; /* Window width */
51 INT32 height; /* Window height */
52 LB_ITEMDATA *items; /* Array of items */
53 INT32 nb_items; /* Number of items */
54 INT32 top_item; /* Top visible item */
55 INT32 selected_item; /* Selected item */
56 INT32 focus_item; /* Item that has the focus */
57 INT32 anchor_item; /* Anchor item for extended selection */
58 INT32 item_height; /* Default item height */
59 INT32 page_size; /* Items per listbox page */
60 INT32 column_width; /* Column width for multi-column listboxes */
61 INT32 horz_extent; /* Horizontal extent (0 if no hscroll) */
62 INT32 horz_pos; /* Horizontal position */
63 INT32 nb_tabs; /* Number of tabs in array */
64 INT32 *tabs; /* Array of tabs */
65 BOOL32 caret_on; /* Is caret on? */
66 HFONT32 font; /* Current font */
67 LCID locale; /* Current locale for string comparisons */
68 LPHEADCOMBO lphc; /* ComboLBox */
69 } LB_DESCR;
72 #define IS_OWNERDRAW(descr) \
73 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
75 #define HAS_STRINGS(descr) \
76 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
78 #define SEND_NOTIFICATION(wnd,descr,code) \
79 (SendMessage32A( (descr)->owner, WM_COMMAND, \
80 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
82 /* Current timer status */
83 typedef enum
85 LB_TIMER_NONE,
86 LB_TIMER_UP,
87 LB_TIMER_LEFT,
88 LB_TIMER_DOWN,
89 LB_TIMER_RIGHT
90 } TIMER_DIRECTION;
92 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
95 /***********************************************************************
96 * LISTBOX_Dump
98 void LISTBOX_Dump( WND *wnd )
100 INT32 i;
101 LB_ITEMDATA *item;
102 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
104 printf( "Listbox:\n" );
105 printf( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
106 wnd->hwndSelf, (UINT32)descr, descr->heap, descr->nb_items,
107 descr->top_item );
108 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
110 printf( "%4d: %-40s %d %08lx %3d\n",
111 i, item->str, item->selected, item->data, item->height );
116 /***********************************************************************
117 * LISTBOX_GetCurrentPageSize
119 * Return the current page size
121 static INT32 LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
123 INT32 i, height;
124 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
125 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
127 if ((height += descr->items[i].height) > descr->height) break;
129 if (i == descr->top_item) return 1;
130 else return i - descr->top_item;
134 /***********************************************************************
135 * LISTBOX_GetMaxTopIndex
137 * Return the maximum possible index for the top of the listbox.
139 static INT32 LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
141 INT32 max, page;
143 if (descr->style & LBS_OWNERDRAWVARIABLE)
145 page = descr->height;
146 for (max = descr->nb_items - 1; max >= 0; max--)
147 if ((page -= descr->items[max].height) < 0) break;
148 if (max < descr->nb_items - 1) max++;
150 else if (descr->style & LBS_MULTICOLUMN)
152 if ((page = descr->width / descr->column_width) < 1) page = 1;
153 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
154 max = (max - page) * descr->page_size;
156 else
158 max = descr->nb_items - descr->page_size;
160 if (max < 0) max = 0;
161 return max;
165 /***********************************************************************
166 * LISTBOX_UpdateScroll
168 * Update the scrollbars. Should be called whenever the content
169 * of the listbox changes.
171 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
173 SCROLLINFO info;
175 if (descr->style & LBS_NOREDRAW) return;
176 info.cbSize = sizeof(info);
178 if (descr->style & LBS_MULTICOLUMN)
180 info.nMin = 0;
181 info.nMax = (descr->nb_items - 1) / descr->page_size;
182 info.nPos = descr->top_item / descr->page_size;
183 info.nPage = descr->width / descr->column_width;
184 if (info.nPage < 1) info.nPage = 1;
185 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
186 if (descr->style & LBS_DISABLENOSCROLL)
187 info.fMask |= SIF_DISABLENOSCROLL;
188 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
189 info.nMax = 0;
190 info.fMask = SIF_RANGE;
191 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
193 else
195 info.nMin = 0;
196 info.nMax = descr->nb_items - 1;
197 info.nPos = descr->top_item;
198 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
199 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
200 if (descr->style & LBS_DISABLENOSCROLL)
201 info.fMask |= SIF_DISABLENOSCROLL;
202 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
204 if (descr->horz_extent)
206 info.nMin = 0;
207 info.nMax = descr->horz_extent - 1;
208 info.nPos = descr->horz_pos;
209 info.nPage = descr->width;
210 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
211 if (descr->style & LBS_DISABLENOSCROLL)
212 info.fMask |= SIF_DISABLENOSCROLL;
213 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
219 /***********************************************************************
220 * LISTBOX_SetTopItem
222 * Set the top item of the listbox, scrolling up or down if necessary.
224 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index,
225 BOOL32 scroll )
227 INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr );
228 if (index > max) index = max;
229 if (index < 0) index = 0;
230 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
231 if (descr->top_item == index) return LB_OKAY;
232 if (descr->style & LBS_MULTICOLUMN)
234 INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width;
235 if (scroll && (abs(diff) < descr->width))
236 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
237 SW_INVALIDATE | SW_ERASE );
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 ScrollWindowEx32( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
264 SW_INVALIDATE | SW_ERASE );
265 else
266 scroll = FALSE;
268 if (!scroll) InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
269 descr->top_item = index;
270 LISTBOX_UpdateScroll( wnd, descr );
271 return LB_OKAY;
275 /***********************************************************************
276 * LISTBOX_UpdatePage
278 * Update the page size. Should be called when the size of
279 * the client area or the item height changes.
281 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
283 INT32 page_size;
285 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
286 if (page_size == descr->page_size) return;
287 descr->page_size = page_size;
288 if (descr->style & LBS_MULTICOLUMN)
289 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
290 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
294 /***********************************************************************
295 * LISTBOX_UpdateSize
297 * Update the size of the listbox. Should be called when the size of
298 * the client area changes.
300 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
302 RECT32 rect;
304 GetClientRect32( wnd->hwndSelf, &rect );
305 descr->width = rect.right - rect.left;
306 descr->height = rect.bottom - rect.top;
307 if (!(descr->style & LBS_NOINTEGRALHEIGHT))
309 if ((descr->height > descr->item_height) &&
310 (descr->height % descr->item_height))
312 dprintf_listbox(stddeb, "Listbox %04x: changing height %d -> %d\n",
313 wnd->hwndSelf, descr->height,
314 descr->height - descr->height%descr->item_height );
315 SetWindowPos32( wnd->hwndSelf, 0, 0, 0,
316 wnd->rectWindow.right - wnd->rectWindow.left,
317 wnd->rectWindow.bottom - wnd->rectWindow.top -
318 (descr->height % descr->item_height),
319 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
320 return;
323 dprintf_listbox( stddeb, "Listbox %04x: new size = %d,%d\n",
324 wnd->hwndSelf, descr->width, descr->height );
325 LISTBOX_UpdatePage( wnd, descr );
326 LISTBOX_UpdateScroll( wnd, descr );
330 /***********************************************************************
331 * LISTBOX_GetItemRect
333 * Get the rectangle enclosing an item, in listbox client coordinates.
334 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
336 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT32 index,
337 RECT32 *rect )
339 /* Index <= 0 is legal even on empty listboxes */
340 if (index && (index >= descr->nb_items)) return -1;
341 SetRect32( rect, 0, 0, descr->width, descr->height );
342 if (descr->style & LBS_MULTICOLUMN)
344 INT32 col = (index / descr->page_size) -
345 (descr->top_item / descr->page_size);
346 rect->left += col * descr->column_width;
347 rect->right = rect->left + descr->column_width;
348 rect->top += (index % descr->page_size) * descr->item_height;
349 rect->bottom = rect->top + descr->item_height;
351 else if (descr->style & LBS_OWNERDRAWVARIABLE)
353 INT32 i;
354 rect->right += descr->horz_pos;
355 if ((index >= 0) && (index < descr->nb_items))
357 if (index < descr->top_item)
359 for (i = descr->top_item-1; i >= index; i--)
360 rect->top -= descr->items[i].height;
362 else
364 for (i = descr->top_item; i < index; i++)
365 rect->top += descr->items[i].height;
367 rect->bottom = rect->top + descr->items[index].height;
371 else
373 rect->top += (index - descr->top_item) * descr->item_height;
374 rect->bottom = rect->top + descr->item_height;
375 rect->right += descr->horz_pos;
378 return ((rect->left < descr->width) && (rect->right > 0) &&
379 (rect->top < descr->height) && (rect->bottom > 0));
383 /***********************************************************************
384 * LISTBOX_GetItemFromPoint
386 * Return the item nearest from point (x,y) (in client coordinates).
388 static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
389 INT32 x, INT32 y )
391 INT32 index = descr->top_item;
393 if (!descr->nb_items) return -1; /* No items */
394 if (descr->style & LBS_OWNERDRAWVARIABLE)
396 INT32 pos = 0;
397 if (y >= 0)
399 while (index < descr->nb_items)
401 if ((pos += descr->items[index].height) > y) break;
402 index++;
405 else
407 while (index > 0)
409 index--;
410 if ((pos -= descr->items[index].height) <= y) break;
414 else if (descr->style & LBS_MULTICOLUMN)
416 if (y >= descr->item_height * descr->page_size) return -1;
417 if (y >= 0) index += y / descr->item_height;
418 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
419 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
421 else
423 index += (y / descr->item_height);
425 if (index < 0) return 0;
426 if (index >= descr->nb_items) return -1;
427 return index;
431 /***********************************************************************
432 * LISTBOX_PaintItem
434 * Paint an item.
436 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc,
437 const RECT32 *rect, INT32 index, UINT32 action )
439 LB_ITEMDATA *item = NULL;
440 if (index < descr->nb_items) item = &descr->items[index];
442 if (IS_OWNERDRAW(descr))
444 DRAWITEMSTRUCT32 dis;
445 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
447 dis.CtlType = ODT_LISTBOX;
448 dis.CtlID = id;
449 dis.hwndItem = wnd->hwndSelf;
450 dis.itemAction = action;
451 dis.hDC = hdc;
452 dis.itemID = index;
453 dis.itemState = 0;
454 if (item && item->selected) dis.itemState |= ODS_SELECTED;
455 if ((descr->focus_item == index) &&
456 (descr->caret_on) &&
457 (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
458 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
459 dis.itemData = item ? item->data : 0;
460 dis.rcItem = *rect;
461 dprintf_listbox( stddeb, "Listbox %04x: drawitem %d (%s) action=%02x "
462 "state=%02x rect=%d,%d-%d,%d\n",
463 wnd->hwndSelf, index, item ? item->str : "", action,
464 dis.itemState, rect->left, rect->top,
465 rect->right, rect->bottom );
466 SendMessage32A(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
468 else
470 COLORREF oldText = 0, oldBk = 0;
472 if (action == ODA_FOCUS)
474 DrawFocusRect32( hdc, rect );
475 return;
477 if (item && item->selected)
479 oldBk = SetBkColor32( hdc, GetSysColor32( COLOR_HIGHLIGHT ) );
480 oldText = SetTextColor32( hdc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
483 dprintf_listbox( stddeb, "Listbox %04x: painting %d (%s) action=%02x "
484 "rect=%d,%d-%d,%d\n",
485 wnd->hwndSelf, index, item ? item->str : "", action,
486 rect->left, rect->top, rect->right, rect->bottom );
487 if (!item)
488 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
489 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
490 else if (!(descr->style & LBS_USETABSTOPS))
491 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
492 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
493 strlen(item->str), NULL );
494 else
495 TabbedTextOut32A( hdc, rect->left + 1 , rect->top + 1,
496 item->str, strlen(item->str),
497 descr->nb_tabs, descr->tabs, 0);
498 if (item && item->selected)
500 SetBkColor32( hdc, oldBk );
501 SetTextColor32( hdc, oldText );
503 if ((descr->focus_item == index) &&
504 (descr->caret_on) &&
505 (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect );
510 /***********************************************************************
511 * LISTBOX_SetRedraw
513 * Change the redraw flag.
515 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on )
517 if (on)
519 if (!(descr->style & LBS_NOREDRAW)) return;
520 descr->style &= ~LBS_NOREDRAW;
521 LISTBOX_UpdateScroll( wnd, descr );
523 else descr->style |= LBS_NOREDRAW;
527 /***********************************************************************
528 * LISTBOX_RepaintItem
530 * Repaint a single item synchronously.
532 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index,
533 UINT32 action )
535 HDC32 hdc;
536 RECT32 rect;
537 HFONT32 oldFont = 0;
538 HBRUSH32 hbrush, oldBrush = 0;
540 if (descr->style & LBS_NOREDRAW) return;
541 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
542 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE ))) return;
543 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
544 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
545 hdc, (LPARAM)wnd->hwndSelf );
546 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
547 if (wnd->dwStyle & WS_DISABLED)
548 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
549 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
550 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
551 if (oldFont) SelectObject32( hdc, oldFont );
552 if (oldBrush) SelectObject32( hdc, oldBrush );
553 ReleaseDC32( wnd->hwndSelf, hdc );
557 /***********************************************************************
558 * LISTBOX_InitStorage
560 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items,
561 DWORD bytes )
563 LB_ITEMDATA *item;
565 nb_items += LB_ARRAY_GRANULARITY - 1;
566 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
567 if (descr->items)
568 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
569 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
570 nb_items * sizeof(LB_ITEMDATA) )))
572 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
573 return LB_ERRSPACE;
575 descr->items = item;
576 return LB_OKAY;
580 /***********************************************************************
581 * LISTBOX_SetTabStops
583 static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count,
584 LPINT32 tabs, BOOL32 short_ints )
586 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
587 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
588 if (!(descr->nb_tabs = count))
590 descr->tabs = NULL;
591 return TRUE;
593 /* FIXME: count = 1 */
594 if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0,
595 descr->nb_tabs * sizeof(INT32) )))
596 return FALSE;
597 if (short_ints)
599 INT32 i;
600 LPINT16 p = (LPINT16)tabs;
601 dprintf_listbox( stddeb, "Listbox %04x: settabstops ", wnd->hwndSelf);
602 for (i = 0; i < descr->nb_tabs; i++) {
603 descr->tabs[i] = *p++<<1; /* FIXME */
604 dprintf_listbox( stddeb, "%hd ", descr->tabs[i]);
606 dprintf_listbox( stddeb, "\n");
608 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) );
609 /* FIXME: repaint the window? */
610 return TRUE;
614 /***********************************************************************
615 * LISTBOX_GetText
617 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index,
618 LPSTR buffer )
620 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
621 if (HAS_STRINGS(descr))
623 lstrcpy32A( buffer, descr->items[index].str );
624 return strlen(buffer);
626 else
628 memcpy( buffer, &descr->items[index].data, sizeof(DWORD) );
629 return sizeof(DWORD);
634 /***********************************************************************
635 * LISTBOX_FindStringPos
637 * Find the nearest string located before a given string in sort order.
638 * If 'exact' is TRUE, return an error if we don't get an exact match.
640 static INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
641 BOOL32 exact )
643 INT32 index, min, max, res = -1;
645 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
646 min = 0;
647 max = descr->nb_items;
648 while (min != max)
650 index = (min + max) / 2;
651 if (HAS_STRINGS(descr))
652 res = lstrcmpi32A( descr->items[index].str, str );
653 else
655 COMPAREITEMSTRUCT32 cis;
656 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
658 cis.CtlType = ODT_LISTBOX;
659 cis.CtlID = id;
660 cis.hwndItem = wnd->hwndSelf;
661 cis.itemID1 = index;
662 cis.itemData1 = descr->items[index].data;
663 cis.itemID2 = -1;
664 cis.itemData2 = (DWORD)str;
665 cis.dwLocaleId = descr->locale;
666 res = SendMessage32A( descr->owner, WM_COMPAREITEM,
667 id, (LPARAM)&cis );
669 if (!res) return index;
670 if (res > 0) max = index;
671 else min = index + 1;
673 return exact ? -1 : max;
677 /***********************************************************************
678 * LISTBOX_FindFileStrPos
680 * Find the nearest string located before a given string in directory
681 * sort order (i.e. first files, then directories, then drives).
683 static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
685 INT32 min, max, res = -1;
687 if (!HAS_STRINGS(descr))
688 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
689 min = 0;
690 max = descr->nb_items;
691 while (min != max)
693 INT32 index = (min + max) / 2;
694 const char *p = descr->items[index].str;
695 if (*p == '[') /* drive or directory */
697 if (*str != '[') res = -1;
698 else if (p[1] == '-') /* drive */
700 if (str[1] == '-') res = str[2] - p[2];
701 else res = -1;
703 else /* directory */
705 if (str[1] == '-') res = 1;
706 else res = lstrcmpi32A( str, p );
709 else /* filename */
711 if (*str == '[') res = 1;
712 else res = lstrcmpi32A( str, p );
714 if (!res) return index;
715 if (res < 0) max = index;
716 else min = index + 1;
718 return max;
722 /***********************************************************************
723 * LISTBOX_FindString
725 * Find the item beginning with a given string.
727 static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start,
728 LPCSTR str, BOOL32 exact )
730 INT32 i;
731 LB_ITEMDATA *item;
733 if (start >= descr->nb_items) start = -1;
734 item = descr->items + start + 1;
735 if (HAS_STRINGS(descr))
737 if (!str) return LB_ERR;
738 if (exact)
740 for (i = start + 1; i < descr->nb_items; i++, item++)
741 if (!lstrcmpi32A( str, item->str )) return i;
742 for (i = 0, item = descr->items; i <= start; i++, item++)
743 if (!lstrcmpi32A( str, item->str )) return i;
745 else
747 /* Special case for drives and directories: ignore prefix */
748 #define CHECK_DRIVE(item) \
749 if ((item)->str[0] == '[') \
751 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
752 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
753 return i; \
756 INT32 len = strlen(str);
757 for (i = start + 1; i < descr->nb_items; i++, item++)
759 if (!lstrncmpi32A( str, item->str, len )) return i;
760 CHECK_DRIVE(item);
762 for (i = 0, item = descr->items; i <= start; i++, item++)
764 if (!lstrncmpi32A( str, item->str, len )) return i;
765 CHECK_DRIVE(item);
767 #undef CHECK_DRIVE
770 else
772 if (exact && (descr->style & LBS_SORT))
773 /* If sorted, use a WM_COMPAREITEM binary search */
774 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
776 /* Otherwise use a linear search */
777 for (i = start + 1; i < descr->nb_items; i++, item++)
778 if (item->data == (DWORD)str) return i;
779 for (i = 0, item = descr->items; i <= start; i++, item++)
780 if (item->data == (DWORD)str) return i;
782 return LB_ERR;
786 /***********************************************************************
787 * LISTBOX_GetSelCount
789 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
791 INT32 i, count;
792 LB_ITEMDATA *item = descr->items;
794 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
795 for (i = count = 0; i < descr->nb_items; i++, item++)
796 if (item->selected) count++;
797 return count;
801 /***********************************************************************
802 * LISTBOX_GetSelItems16
804 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
805 LPINT16 array )
807 INT32 i, count;
808 LB_ITEMDATA *item = descr->items;
810 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
811 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
812 if (item->selected) array[count++] = (INT16)i;
813 return count;
817 /***********************************************************************
818 * LISTBOX_GetSelItems32
820 static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max,
821 LPINT32 array )
823 INT32 i, count;
824 LB_ITEMDATA *item = descr->items;
826 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
827 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
828 if (item->selected) array[count++] = i;
829 return count;
833 /***********************************************************************
834 * LISTBOX_Paint
836 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc )
838 INT32 i, col_pos = descr->page_size - 1;
839 RECT32 rect;
840 HFONT32 oldFont = 0;
841 HBRUSH32 hbrush, oldBrush = 0;
843 SetRect32( &rect, 0, 0, descr->width, descr->height );
844 if (descr->style & LBS_NOREDRAW) return 0;
845 if (descr->style & LBS_MULTICOLUMN)
846 rect.right = rect.left + descr->column_width;
847 else if (descr->horz_pos)
849 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
850 rect.right += descr->horz_pos;
853 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
854 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
855 hdc, (LPARAM)wnd->hwndSelf );
856 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
857 if (wnd->dwStyle & WS_DISABLED)
858 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
860 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
861 (GetFocus32() == wnd->hwndSelf))
863 /* Special case for empty listbox: paint focus rect */
864 rect.bottom = rect.top + descr->item_height;
865 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
866 ODA_DRAWENTIRE );
867 rect.top = rect.bottom;
870 for (i = descr->top_item; i < descr->nb_items; i++)
872 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
873 rect.bottom = rect.top + descr->item_height;
874 else
875 rect.bottom = rect.top + descr->items[i].height;
877 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
878 rect.top = rect.bottom;
880 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
882 if (!IS_OWNERDRAW(descr))
884 /* Clear the bottom of the column */
885 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
886 if (rect.top < descr->height)
888 rect.bottom = descr->height;
889 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
890 &rect, NULL, 0, NULL );
894 /* Go to the next column */
895 rect.left += descr->column_width;
896 rect.right += descr->column_width;
897 rect.top = 0;
898 col_pos = descr->page_size - 1;
900 else
902 col_pos--;
903 if (rect.top >= descr->height) break;
907 if (!IS_OWNERDRAW(descr))
909 /* Clear the remainder of the client area */
910 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
911 if (rect.top < descr->height)
913 rect.bottom = descr->height;
914 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
915 &rect, NULL, 0, NULL );
917 if (rect.right < descr->width)
919 rect.left = rect.right;
920 rect.right = descr->width;
921 rect.top = 0;
922 rect.bottom = descr->height;
923 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
924 &rect, NULL, 0, NULL );
927 if (oldFont) SelectObject32( hdc, oldFont );
928 if (oldBrush) SelectObject32( hdc, oldBrush );
929 return 0;
933 /***********************************************************************
934 * LISTBOX_InvalidateItems
936 * Invalidate all items from a given item. If the specified item is not
937 * visible, nothing happens.
939 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index )
941 RECT32 rect;
943 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
945 rect.bottom = descr->height;
946 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
947 if (descr->style & LBS_MULTICOLUMN)
949 /* Repaint the other columns */
950 rect.left = rect.right;
951 rect.right = descr->width;
952 rect.top = 0;
953 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
959 /***********************************************************************
960 * LISTBOX_GetItemHeight
962 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index )
964 if (descr->style & LBS_OWNERDRAWVARIABLE)
966 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
967 return descr->items[index].height;
969 else return descr->item_height;
973 /***********************************************************************
974 * LISTBOX_SetItemHeight
976 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index,
977 UINT32 height )
979 if (!height) height = 1;
981 if (descr->style & LBS_OWNERDRAWVARIABLE)
983 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
984 dprintf_listbox( stddeb, "Listbox %04x: item %d height = %d\n",
985 wnd->hwndSelf, index, height );
986 descr->items[index].height = height;
987 LISTBOX_UpdateScroll( wnd, descr );
988 LISTBOX_InvalidateItems( wnd, descr, index );
990 else if (height != descr->item_height)
992 dprintf_listbox( stddeb, "Listbox %04x: new height = %d\n",
993 wnd->hwndSelf, height );
994 descr->item_height = height;
995 LISTBOX_UpdatePage( wnd, descr );
996 LISTBOX_UpdateScroll( wnd, descr );
997 InvalidateRect32( wnd->hwndSelf, 0, TRUE );
999 return LB_OKAY;
1003 /***********************************************************************
1004 * LISTBOX_SetHorizontalPos
1006 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos )
1008 INT32 diff;
1010 if (pos > descr->horz_extent - descr->width)
1011 pos = descr->horz_extent - descr->width;
1012 if (pos < 0) pos = 0;
1013 if (!(diff = descr->horz_pos - pos)) return;
1014 dprintf_listbox( stddeb, "Listbox %04x: new horz pos = %d\n",
1015 wnd->hwndSelf, pos );
1016 descr->horz_pos = pos;
1017 LISTBOX_UpdateScroll( wnd, descr );
1018 if (abs(diff) < descr->width)
1019 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1020 SW_INVALIDATE | SW_ERASE );
1021 else
1022 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1026 /***********************************************************************
1027 * LISTBOX_SetHorizontalExtent
1029 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1030 UINT32 extent )
1032 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1033 return LB_OKAY;
1034 if (extent <= 0) extent = 1;
1035 if (extent == descr->horz_extent) return LB_OKAY;
1036 dprintf_listbox( stddeb, "Listbox %04x: new horz extent = %d\n",
1037 wnd->hwndSelf, extent );
1038 descr->horz_extent = extent;
1039 if (descr->horz_pos > extent - descr->width)
1040 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1041 else
1042 LISTBOX_UpdateScroll( wnd, descr );
1043 return LB_OKAY;
1047 /***********************************************************************
1048 * LISTBOX_SetColumnWidth
1050 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width)
1052 width += 2; /* For left and right margin */
1053 if (width == descr->column_width) return LB_OKAY;
1054 dprintf_listbox( stddeb, "Listbox %04x: new column width = %d\n",
1055 wnd->hwndSelf, width );
1056 descr->column_width = width;
1057 LISTBOX_UpdatePage( wnd, descr );
1058 return LB_OKAY;
1062 /***********************************************************************
1063 * LISTBOX_SetFont
1065 * Returns the item height.
1067 static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font )
1069 HDC32 hdc;
1070 HFONT32 oldFont = 0;
1071 TEXTMETRIC32A tm;
1073 descr->font = font;
1075 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
1077 fprintf( stderr, "LISTBOX_SetFont: unable to get DC\n" );
1078 return 16;
1080 if (font) oldFont = SelectObject32( hdc, font );
1081 GetTextMetrics32A( hdc, &tm );
1082 if (oldFont) SelectObject32( hdc, oldFont );
1083 ReleaseDC32( wnd->hwndSelf, hdc );
1084 if (!IS_OWNERDRAW(descr))
1085 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight + 2 );
1086 return tm.tmHeight + 2;
1090 /***********************************************************************
1091 * LISTBOX_MakeItemVisible
1093 * Make sure that a given item is partially or fully visible.
1095 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index,
1096 BOOL32 fully )
1098 INT32 top;
1100 if (index <= descr->top_item) top = index;
1101 else if (descr->style & LBS_MULTICOLUMN)
1103 INT32 cols = descr->width;
1104 if (!fully) cols += descr->column_width - 1;
1105 if (cols >= descr->column_width) cols /= descr->column_width;
1106 else cols = 1;
1107 if (index < descr->top_item + (descr->page_size * cols)) return;
1108 top = index - descr->page_size * (cols - 1);
1110 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1112 INT32 height = fully ? descr->items[index].height : 1;
1113 for (top = index; top > descr->top_item; top--)
1114 if ((height += descr->items[top-1].height) > descr->height) break;
1116 else
1118 if (index < descr->top_item + descr->page_size) return;
1119 if (!fully && (index == descr->top_item + descr->page_size) &&
1120 (descr->height > (descr->page_size * descr->item_height))) return;
1121 top = index - descr->page_size + 1;
1123 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1127 /***********************************************************************
1128 * LISTBOX_SelectItemRange
1130 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1132 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first,
1133 INT32 last, BOOL32 on )
1135 INT32 i;
1137 /* A few sanity checks */
1139 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1140 if (last == -1) last = descr->nb_items - 1;
1141 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1142 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1143 /* selected_item reflects last selected/unselected item on multiple sel */
1144 descr->selected_item = last;
1146 if (on) /* Turn selection on */
1148 for (i = first; i <= last; i++)
1150 if (descr->items[i].selected) continue;
1151 descr->items[i].selected = TRUE;
1152 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1155 else /* Turn selection off */
1157 for (i = first; i <= last; i++)
1159 if (!descr->items[i].selected) continue;
1160 descr->items[i].selected = FALSE;
1161 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1164 return LB_OKAY;
1168 /***********************************************************************
1169 * LISTBOX_SetCaretIndex
1171 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index,
1172 BOOL32 fully_visible )
1174 INT32 oldfocus = descr->focus_item;
1176 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1177 if (index == oldfocus) return LB_OKAY;
1178 descr->focus_item = index;
1179 if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1180 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1181 if (index != -1)
1183 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1184 if (descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1185 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1187 return LB_OKAY;
1191 /***********************************************************************
1192 * LISTBOX_SetSelection
1194 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index,
1195 BOOL32 on, BOOL32 send_notify )
1197 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1198 if (descr->style & LBS_MULTIPLESEL)
1200 if (index == -1) /* Select all items */
1201 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1202 else /* Only one item */
1203 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1205 else
1207 INT32 oldsel = descr->selected_item;
1208 if (index == oldsel) return LB_OKAY;
1209 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1210 if (index != -1) descr->items[index].selected = TRUE;
1211 descr->selected_item = index;
1212 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1213 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1214 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1215 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1217 return LB_OKAY;
1221 /***********************************************************************
1222 * LISTBOX_MoveCaret
1224 * Change the caret position and extend the selection to the new caret.
1226 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index,
1227 BOOL32 fully_visible )
1229 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1230 if (descr->style & LBS_EXTENDEDSEL)
1232 if (descr->anchor_item != -1)
1234 INT32 first = MIN( descr->focus_item, descr->anchor_item );
1235 INT32 last = MAX( descr->focus_item, descr->anchor_item );
1236 if (first > 0)
1237 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1238 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1239 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1242 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1244 /* Set selection to new caret item */
1245 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1250 /***********************************************************************
1251 * LISTBOX_InsertItem
1253 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index,
1254 LPSTR str, DWORD data )
1256 LB_ITEMDATA *item;
1257 INT32 max_items;
1259 if (index == -1) index = descr->nb_items;
1260 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1261 if (!descr->items) max_items = 0;
1262 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1263 if (descr->nb_items == max_items)
1265 /* We need to grow the array */
1266 max_items += LB_ARRAY_GRANULARITY;
1267 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1268 max_items * sizeof(LB_ITEMDATA) )))
1270 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1271 return LB_ERRSPACE;
1273 descr->items = item;
1276 /* Insert the item structure */
1278 item = &descr->items[index];
1279 if (index < descr->nb_items)
1280 RtlMoveMemory( item + 1, item,
1281 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1282 item->str = str;
1283 item->data = data;
1284 item->height = 0;
1285 item->selected = FALSE;
1286 descr->nb_items++;
1288 /* Get item height */
1290 if (descr->style & LBS_OWNERDRAWVARIABLE)
1292 MEASUREITEMSTRUCT32 mis;
1293 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1295 mis.CtlType = ODT_LISTBOX;
1296 mis.CtlID = id;
1297 mis.itemID = index;
1298 mis.itemData = descr->items[index].data;
1299 mis.itemHeight = descr->item_height;
1300 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1301 item->height = mis.itemHeight ? mis.itemHeight : 1;
1302 dprintf_listbox( stddeb, "Listbox %04x: measure item %d (%s) = %d\n",
1303 wnd->hwndSelf, index, str ? str : "", item->height );
1306 /* Repaint the items */
1308 LISTBOX_UpdateScroll( wnd, descr );
1309 LISTBOX_InvalidateItems( wnd, descr, index );
1311 /* Move selection and focused item */
1313 if (index <= descr->selected_item) descr->selected_item++;
1314 if (index <= descr->focus_item)
1316 descr->focus_item++;
1317 LISTBOX_MoveCaret( wnd, descr, descr->focus_item - 1, FALSE );
1320 /* If listbox was empty, set focus to the first item */
1322 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1323 return LB_OKAY;
1327 /***********************************************************************
1328 * LISTBOX_InsertString
1330 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index,
1331 LPCSTR str )
1333 LPSTR new_str = NULL;
1334 DWORD data = 0;
1335 LRESULT ret;
1337 if (HAS_STRINGS(descr))
1339 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1341 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1342 return LB_ERRSPACE;
1345 else data = (DWORD)str;
1347 if (index == -1) index = descr->nb_items;
1348 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1350 if (new_str) HeapFree( descr->heap, 0, new_str );
1351 return ret;
1354 dprintf_listbox( stddeb, "Listbox %04x: added item %d '%s'\n",
1355 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1356 return index;
1360 /***********************************************************************
1361 * LISTBOX_DeleteItem
1363 * Delete the content of an item. 'index' must be a valid index.
1365 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index )
1367 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1368 * while Win95 sends it for all items with user data.
1369 * It's probably better to send it too often than not
1370 * often enough, so this is what we do here.
1372 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1374 DELETEITEMSTRUCT32 dis;
1375 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1377 dis.CtlType = ODT_LISTBOX;
1378 dis.CtlID = id;
1379 dis.itemID = index;
1380 dis.hwndItem = wnd->hwndSelf;
1381 dis.itemData = descr->items[index].data;
1382 SendMessage32A( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1384 if (HAS_STRINGS(descr) && descr->items[index].str)
1385 HeapFree( descr->heap, 0, descr->items[index].str );
1389 /***********************************************************************
1390 * LISTBOX_RemoveItem
1392 * Remove an item from the listbox and delete its content.
1394 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index )
1396 LB_ITEMDATA *item;
1397 INT32 max_items;
1399 if (index == -1) index = descr->nb_items - 1;
1400 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1401 LISTBOX_DeleteItem( wnd, descr, index );
1403 /* Remove the item */
1405 item = &descr->items[index];
1406 if (index < descr->nb_items-1)
1407 RtlMoveMemory( item, item + 1,
1408 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1409 descr->nb_items--;
1410 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1412 /* Shrink the item array if possible */
1414 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1415 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1417 max_items -= LB_ARRAY_GRANULARITY;
1418 item = HeapReAlloc( descr->heap, 0, descr->items,
1419 max_items * sizeof(LB_ITEMDATA) );
1420 if (item) descr->items = item;
1423 /* Repaint the items */
1425 LISTBOX_UpdateScroll( wnd, descr );
1426 LISTBOX_InvalidateItems( wnd, descr, index );
1428 /* Move selection and focused item */
1430 if (index <= descr->selected_item) descr->selected_item--;
1431 if (index <= descr->focus_item)
1433 descr->focus_item--;
1434 LISTBOX_MoveCaret( wnd, descr, descr->focus_item + 1, FALSE );
1436 return LB_OKAY;
1440 /***********************************************************************
1441 * LISTBOX_ResetContent
1443 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1445 INT32 i;
1447 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1448 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1449 descr->nb_items = 0;
1450 descr->top_item = 0;
1451 descr->selected_item = -1;
1452 descr->focus_item = 0;
1453 descr->anchor_item = -1;
1454 descr->items = NULL;
1455 LISTBOX_UpdateScroll( wnd, descr );
1456 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1460 /***********************************************************************
1461 * LISTBOX_SetCount
1463 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count )
1465 LRESULT ret;
1467 if (HAS_STRINGS(descr)) return LB_ERR;
1468 /* FIXME: this is far from optimal... */
1469 if (count > descr->nb_items)
1471 while (count > descr->nb_items)
1472 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1473 return ret;
1475 else if (count < descr->nb_items)
1477 while (count < descr->nb_items)
1478 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1479 return ret;
1481 return LB_OKAY;
1485 /***********************************************************************
1486 * LISTBOX_Directory
1488 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib,
1489 LPCSTR filespec, BOOL32 long_names )
1491 HANDLE32 handle;
1492 LRESULT ret = LB_OKAY;
1493 WIN32_FIND_DATA32A entry;
1494 int pos;
1496 if ((handle = FindFirstFile32A(filespec,&entry)) == INVALID_HANDLE_VALUE32)
1498 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1500 else
1504 char buffer[270];
1505 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1507 if (!(attrib & DDL_DIRECTORY) ||
1508 !strcmp( entry.cAlternateFileName, "." )) continue;
1509 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1510 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1512 else /* not a directory */
1514 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1515 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1517 if ((attrib & DDL_EXCLUSIVE) &&
1518 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1519 continue;
1520 #undef ATTRIBS
1521 if (long_names) strcpy( buffer, entry.cFileName );
1522 else strcpy( buffer, entry.cAlternateFileName );
1524 if (!long_names) CharLower32A( buffer );
1525 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1526 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1527 break;
1528 } while (FindNextFile32A( handle, &entry ));
1529 FindClose32( handle );
1532 if ((ret >= 0) && (attrib & DDL_DRIVES))
1534 char buffer[] = "[-a-]";
1535 int drive;
1536 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1538 if (!DRIVE_IsValid(drive)) continue;
1539 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1540 break;
1543 return ret;
1547 /***********************************************************************
1548 * LISTBOX_HandleVScroll
1550 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1551 WPARAM32 wParam, LPARAM lParam )
1553 SCROLLINFO info;
1555 if (descr->style & LBS_MULTICOLUMN) return 0;
1556 switch(LOWORD(wParam))
1558 case SB_LINEUP:
1559 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1560 break;
1561 case SB_LINEDOWN:
1562 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1563 break;
1564 case SB_PAGEUP:
1565 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1566 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1567 break;
1568 case SB_PAGEDOWN:
1569 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1570 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1571 break;
1572 case SB_THUMBPOSITION:
1573 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1574 break;
1575 case SB_THUMBTRACK:
1576 info.cbSize = sizeof(info);
1577 info.fMask = SIF_TRACKPOS;
1578 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1579 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1580 break;
1581 case SB_TOP:
1582 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1583 break;
1584 case SB_BOTTOM:
1585 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1586 break;
1588 return 0;
1592 /***********************************************************************
1593 * LISTBOX_HandleHScroll
1595 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1596 WPARAM32 wParam, LPARAM lParam )
1598 SCROLLINFO info;
1599 INT32 page;
1601 if (descr->style & LBS_MULTICOLUMN)
1603 switch(LOWORD(wParam))
1605 case SB_LINELEFT:
1606 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1607 TRUE );
1608 break;
1609 case SB_LINERIGHT:
1610 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1611 TRUE );
1612 break;
1613 case SB_PAGELEFT:
1614 page = descr->width / descr->column_width;
1615 if (page < 1) page = 1;
1616 LISTBOX_SetTopItem( wnd, descr,
1617 descr->top_item - page * descr->page_size, TRUE );
1618 break;
1619 case SB_PAGERIGHT:
1620 page = descr->width / descr->column_width;
1621 if (page < 1) page = 1;
1622 LISTBOX_SetTopItem( wnd, descr,
1623 descr->top_item + page * descr->page_size, TRUE );
1624 break;
1625 case SB_THUMBPOSITION:
1626 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1627 TRUE );
1628 break;
1629 case SB_THUMBTRACK:
1630 info.cbSize = sizeof(info);
1631 info.fMask = SIF_TRACKPOS;
1632 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1633 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1634 TRUE );
1635 break;
1636 case SB_LEFT:
1637 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1638 break;
1639 case SB_RIGHT:
1640 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1641 break;
1644 else if (descr->horz_extent)
1646 switch(LOWORD(wParam))
1648 case SB_LINELEFT:
1649 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1650 break;
1651 case SB_LINERIGHT:
1652 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1653 break;
1654 case SB_PAGELEFT:
1655 LISTBOX_SetHorizontalPos( wnd, descr,
1656 descr->horz_pos - descr->width );
1657 break;
1658 case SB_PAGERIGHT:
1659 LISTBOX_SetHorizontalPos( wnd, descr,
1660 descr->horz_pos + descr->width );
1661 break;
1662 case SB_THUMBPOSITION:
1663 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1664 break;
1665 case SB_THUMBTRACK:
1666 info.cbSize = sizeof(info);
1667 info.fMask = SIF_TRACKPOS;
1668 GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info );
1669 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1670 break;
1671 case SB_LEFT:
1672 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1673 break;
1674 case SB_RIGHT:
1675 LISTBOX_SetHorizontalPos( wnd, descr,
1676 descr->horz_extent - descr->width );
1677 break;
1680 return 0;
1684 /***********************************************************************
1685 * LISTBOX_HandleLButtonDown
1687 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1688 WPARAM32 wParam, INT32 x, INT32 y )
1690 INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1691 dprintf_listbox( stddeb, "Listbox %04x: lbuttondown %d,%d item %d\n",
1692 wnd->hwndSelf, x, y, index );
1693 if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0;
1694 if (index != -1)
1696 if (descr->style & LBS_EXTENDEDSEL)
1698 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1699 if (wParam & MK_CONTROL)
1701 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1702 LISTBOX_SetSelection( wnd, descr, index,
1703 !descr->items[index].selected, FALSE );
1705 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1707 else
1709 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1710 LISTBOX_SetSelection( wnd, descr, index,
1711 (!(descr->style & LBS_MULTIPLESEL) ||
1712 !descr->items[index].selected), FALSE );
1716 if( !descr->lphc ) SetFocus32( wnd->hwndSelf );
1717 else SetFocus32( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1718 : descr->lphc->self->hwndSelf ) ;
1720 SetCapture32( wnd->hwndSelf );
1721 if (index != -1 && !descr->lphc)
1723 if (descr->style & LBS_NOTIFY )
1724 SendMessage32A( descr->owner, WM_LBTRACKPOINT, index,
1725 MAKELPARAM( x, y ) );
1726 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1728 POINT32 pt = { x, y };
1729 if (DragDetect32( wnd->hwndSelf, pt ))
1730 SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 );
1733 return 0;
1737 /***********************************************************************
1738 * LISTBOX_HandleLButtonUp
1740 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1742 if (LISTBOX_Timer != LB_TIMER_NONE)
1743 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1744 LISTBOX_Timer = LB_TIMER_NONE;
1745 if (GetCapture32() == wnd->hwndSelf)
1747 ReleaseCapture();
1748 if (descr->style & LBS_NOTIFY)
1749 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1751 return 0;
1755 /***********************************************************************
1756 * LISTBOX_HandleTimer
1758 * Handle scrolling upon a timer event.
1759 * Return TRUE if scrolling should continue.
1761 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1762 INT32 index, TIMER_DIRECTION dir )
1764 switch(dir)
1766 case LB_TIMER_UP:
1767 if (descr->top_item) index = descr->top_item - 1;
1768 else index = 0;
1769 break;
1770 case LB_TIMER_LEFT:
1771 if (descr->top_item) index -= descr->page_size;
1772 break;
1773 case LB_TIMER_DOWN:
1774 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1775 if (index == descr->focus_item) index++;
1776 if (index >= descr->nb_items) index = descr->nb_items - 1;
1777 break;
1778 case LB_TIMER_RIGHT:
1779 if (index + descr->page_size < descr->nb_items)
1780 index += descr->page_size;
1781 break;
1782 case LB_TIMER_NONE:
1783 break;
1785 if (index == descr->focus_item) return FALSE;
1786 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1787 return TRUE;
1791 /***********************************************************************
1792 * LISTBOX_HandleSystemTimer
1794 * WM_SYSTIMER handler.
1796 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1798 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1800 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1801 LISTBOX_Timer = LB_TIMER_NONE;
1803 return 0;
1807 /***********************************************************************
1808 * LISTBOX_HandleMouseMove
1810 * WM_MOUSEMOVE handler.
1812 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1813 INT32 x, INT32 y )
1815 INT32 index;
1816 TIMER_DIRECTION dir;
1818 if (descr->style & LBS_MULTICOLUMN)
1820 if (y < 0) y = 0;
1821 else if (y >= descr->item_height * descr->page_size)
1822 y = descr->item_height * descr->page_size - 1;
1824 if (x < 0)
1826 dir = LB_TIMER_LEFT;
1827 x = 0;
1829 else if (x >= descr->width)
1831 dir = LB_TIMER_RIGHT;
1832 x = descr->width - 1;
1834 else dir = LB_TIMER_NONE; /* inside */
1836 else
1838 if (y < 0) dir = LB_TIMER_UP; /* above */
1839 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1840 else dir = LB_TIMER_NONE; /* inside */
1843 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1844 if (index == -1) index = descr->focus_item;
1845 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1847 /* Start/stop the system timer */
1849 if (dir != LB_TIMER_NONE)
1850 SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1851 else if (LISTBOX_Timer != LB_TIMER_NONE)
1852 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1853 LISTBOX_Timer = dir;
1857 /***********************************************************************
1858 * LISTBOX_HandleKeyDown
1860 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM32 wParam )
1862 INT32 caret = -1;
1863 if (descr->style & LBS_WANTKEYBOARDINPUT)
1865 caret = SendMessage32A( descr->owner, WM_VKEYTOITEM,
1866 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1867 wnd->hwndSelf );
1868 if (caret == -2) return 0;
1870 if (caret == -1) switch(wParam)
1872 case VK_LEFT:
1873 if (descr->style & LBS_MULTICOLUMN)
1875 if (descr->focus_item >= descr->page_size)
1876 caret = descr->focus_item - descr->page_size;
1877 break;
1879 /* fall through */
1880 case VK_UP:
1881 caret = descr->focus_item - 1;
1882 if (caret < 0) caret = 0;
1883 break;
1884 case VK_RIGHT:
1885 if (descr->style & LBS_MULTICOLUMN)
1887 if (descr->focus_item + descr->page_size < descr->nb_items)
1888 caret = descr->focus_item + descr->page_size;
1889 break;
1891 /* fall through */
1892 case VK_DOWN:
1893 caret = descr->focus_item + 1;
1894 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1895 break;
1896 case VK_PRIOR:
1897 if (descr->style & LBS_MULTICOLUMN)
1899 INT32 page = descr->width / descr->column_width;
1900 if (page < 1) page = 1;
1901 caret = descr->focus_item - (page * descr->page_size) + 1;
1903 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
1904 if (caret < 0) caret = 0;
1905 break;
1906 case VK_NEXT:
1907 if (descr->style & LBS_MULTICOLUMN)
1909 INT32 page = descr->width / descr->column_width;
1910 if (page < 1) page = 1;
1911 caret = descr->focus_item + (page * descr->page_size) - 1;
1913 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1914 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1915 break;
1916 case VK_HOME:
1917 caret = 0;
1918 break;
1919 case VK_END:
1920 caret = descr->nb_items - 1;
1921 break;
1922 case VK_SPACE:
1923 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1924 else if (descr->style & LBS_MULTIPLESEL)
1926 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1927 !descr->items[descr->focus_item].selected,
1928 (descr->style & LBS_NOTIFY) != 0 );
1930 else if (descr->selected_item == -1)
1932 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1933 (descr->style & LBS_NOTIFY) != 0 );
1935 break;
1937 if (caret >= 0)
1939 if ((descr->style & LBS_EXTENDEDSEL) &&
1940 !(GetKeyState32( VK_SHIFT ) & 0x8000))
1941 descr->anchor_item = caret;
1942 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1943 if (descr->style & LBS_NOTIFY)
1945 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1947 /* make sure that combo parent doesn't hide us */
1948 descr->lphc->wState |= CBF_NOROLLUP;
1950 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1953 return 0;
1957 /***********************************************************************
1958 * LISTBOX_HandleChar
1960 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1961 WPARAM32 wParam )
1963 INT32 caret = -1;
1964 char str[2] = { wParam & 0xff, '\0' };
1966 if (descr->style & LBS_WANTKEYBOARDINPUT)
1968 caret = SendMessage32A( descr->owner, WM_CHARTOITEM,
1969 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1970 wnd->hwndSelf );
1971 if (caret == -2) return 0;
1973 if (caret == -1)
1974 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
1975 if (caret != -1)
1977 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1978 if (descr->style & LBS_NOTIFY)
1979 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1981 return 0;
1985 /***********************************************************************
1986 * LISTBOX_Create
1988 static BOOL32 LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
1990 LB_DESCR *descr;
1991 MEASUREITEMSTRUCT32 mis;
1992 RECT32 rect;
1994 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
1995 return FALSE;
1996 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
1998 HeapFree( GetProcessHeap(), 0, descr );
1999 return FALSE;
2001 GetClientRect32( wnd->hwndSelf, &rect );
2002 descr->owner = GetParent32( wnd->hwndSelf );
2003 descr->style = wnd->dwStyle;
2004 descr->width = rect.right - rect.left;
2005 descr->height = rect.bottom - rect.top;
2006 descr->items = NULL;
2007 descr->nb_items = 0;
2008 descr->top_item = 0;
2009 descr->selected_item = -1;
2010 descr->focus_item = 0;
2011 descr->anchor_item = -1;
2012 descr->item_height = 1;
2013 descr->page_size = 1;
2014 descr->column_width = 150;
2015 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2016 descr->horz_pos = 0;
2017 descr->nb_tabs = 0;
2018 descr->tabs = NULL;
2019 descr->caret_on = TRUE;
2020 descr->font = 0;
2021 descr->locale = 0; /* FIXME */
2022 descr->lphc = lphc;
2024 if( lphc )
2026 dprintf_combo(stddeb,"ComboLBox [%04x]: resetting owner %04x -> %04x\n",
2027 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2028 descr->owner = lphc->self->hwndSelf;
2031 *(LB_DESCR **)wnd->wExtra = descr;
2033 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2035 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2036 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2037 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2038 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2040 if (descr->style & LBS_OWNERDRAWFIXED)
2042 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2044 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2045 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2047 else
2049 UINT32 id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2051 mis.CtlType = ODT_LISTBOX;
2052 mis.CtlID = id;
2053 mis.itemID = -1;
2054 mis.itemWidth = 0;
2055 mis.itemData = 0;
2056 mis.itemHeight = descr->item_height;
2057 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2058 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2062 return TRUE;
2066 /***********************************************************************
2067 * LISTBOX_Destroy
2069 static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2071 LISTBOX_ResetContent( wnd, descr );
2072 HeapDestroy( descr->heap );
2073 HeapFree( GetProcessHeap(), 0, descr );
2074 wnd->wExtra[0] = 0;
2075 return TRUE;
2079 /***********************************************************************
2080 * ListBoxWndProc
2082 LRESULT WINAPI ListBoxWndProc( HWND32 hwnd, UINT32 msg,
2083 WPARAM32 wParam, LPARAM lParam )
2085 LRESULT ret;
2086 LB_DESCR *descr;
2087 WND *wnd = WIN_FindWndPtr( hwnd );
2089 if (!wnd) return 0;
2090 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2092 if (msg == WM_CREATE)
2094 if (!LISTBOX_Create( wnd, NULL )) return -1;
2095 dprintf_listbox( stddeb, "Listbox: creating wnd=%04x descr=%p\n",
2096 hwnd, *(LB_DESCR **)wnd->wExtra );
2097 return 0;
2099 /* Ignore all other messages before we get a WM_CREATE */
2100 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2103 dprintf_listbox( stddeb, "Listbox %04x: msg %s wp %08x lp %08lx\n",
2104 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2105 switch(msg)
2107 case LB_RESETCONTENT16:
2108 case LB_RESETCONTENT32:
2109 LISTBOX_ResetContent( wnd, descr );
2110 return 0;
2112 case LB_ADDSTRING16:
2113 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2114 /* fall through */
2115 case LB_ADDSTRING32:
2116 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2117 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2119 case LB_INSERTSTRING16:
2120 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2121 wParam = (INT32)(INT16)wParam;
2122 /* fall through */
2123 case LB_INSERTSTRING32:
2124 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2126 case LB_ADDFILE16:
2127 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2128 /* fall through */
2129 case LB_ADDFILE32:
2130 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2131 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2133 case LB_DELETESTRING16:
2134 case LB_DELETESTRING32:
2135 return LISTBOX_RemoveItem( wnd, descr, wParam );
2137 case LB_GETITEMDATA16:
2138 case LB_GETITEMDATA32:
2139 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2140 return LB_ERR;
2141 return descr->items[wParam].data;
2143 case LB_SETITEMDATA16:
2144 case LB_SETITEMDATA32:
2145 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2146 return LB_ERR;
2147 descr->items[wParam].data = (DWORD)lParam;
2148 return LB_OKAY;
2150 case LB_GETCOUNT16:
2151 case LB_GETCOUNT32:
2152 return descr->nb_items;
2154 case LB_GETTEXT16:
2155 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2156 /* fall through */
2157 case LB_GETTEXT32:
2158 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2160 case LB_GETTEXTLEN16:
2161 /* fall through */
2162 case LB_GETTEXTLEN32:
2163 if (wParam >= descr->nb_items) return LB_ERR;
2164 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2165 : sizeof(DWORD));
2167 case LB_GETCURSEL16:
2168 case LB_GETCURSEL32:
2169 return descr->selected_item;
2171 case LB_GETTOPINDEX16:
2172 case LB_GETTOPINDEX32:
2173 return descr->top_item;
2175 case LB_GETITEMHEIGHT16:
2176 case LB_GETITEMHEIGHT32:
2177 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2179 case LB_SETITEMHEIGHT16:
2180 lParam = LOWORD(lParam);
2181 /* fall through */
2182 case LB_SETITEMHEIGHT32:
2183 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2185 case LB_ITEMFROMPOINT32:
2187 POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
2188 RECT32 rect = { 0, 0, descr->width, descr->height };
2189 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2190 PtInRect32( &rect, pt ) );
2193 case LB_SETCARETINDEX16:
2194 case LB_SETCARETINDEX32:
2195 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2197 case LB_GETCARETINDEX16:
2198 case LB_GETCARETINDEX32:
2199 return descr->focus_item;
2201 case LB_SETTOPINDEX16:
2202 case LB_SETTOPINDEX32:
2203 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2205 case LB_SETCOLUMNWIDTH16:
2206 case LB_SETCOLUMNWIDTH32:
2207 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2209 case LB_GETITEMRECT16:
2211 RECT32 rect;
2212 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2213 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2215 return ret;
2217 case LB_GETITEMRECT32:
2218 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam );
2220 case LB_FINDSTRING16:
2221 wParam = (INT32)(INT16)wParam;
2222 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2223 /* fall through */
2224 case LB_FINDSTRING32:
2225 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2227 case LB_FINDSTRINGEXACT16:
2228 wParam = (INT32)(INT16)wParam;
2229 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2230 /* fall through */
2231 case LB_FINDSTRINGEXACT32:
2232 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2234 case LB_SELECTSTRING16:
2235 wParam = (INT32)(INT16)wParam;
2236 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2237 /* fall through */
2238 case LB_SELECTSTRING32:
2240 INT32 index = LISTBOX_FindString( wnd, descr, wParam,
2241 (LPCSTR)lParam, FALSE );
2242 if (index == LB_ERR) return LB_ERR;
2243 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2244 return index;
2247 case LB_GETSEL16:
2248 wParam = (INT32)(INT16)wParam;
2249 /* fall through */
2250 case LB_GETSEL32:
2251 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2252 return LB_ERR;
2253 return descr->items[wParam].selected;
2255 case LB_SETSEL16:
2256 lParam = (INT32)(INT16)lParam;
2257 /* fall through */
2258 case LB_SETSEL32:
2259 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2261 case LB_SETCURSEL16:
2262 wParam = (INT32)(INT16)wParam;
2263 /* fall through */
2264 case LB_SETCURSEL32:
2265 if (wParam != -1) LISTBOX_MakeItemVisible( wnd, descr, wParam, TRUE );
2266 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2268 case LB_GETSELCOUNT16:
2269 case LB_GETSELCOUNT32:
2270 return LISTBOX_GetSelCount( wnd, descr );
2272 case LB_GETSELITEMS16:
2273 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2274 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2276 case LB_GETSELITEMS32:
2277 return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam );
2279 case LB_SELITEMRANGE16:
2280 case LB_SELITEMRANGE32:
2281 if (LOWORD(lParam) <= HIWORD(lParam))
2282 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2283 HIWORD(lParam), wParam );
2284 else
2285 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2286 LOWORD(lParam), wParam );
2288 case LB_SELITEMRANGEEX16:
2289 case LB_SELITEMRANGEEX32:
2290 if ((INT32)lParam >= (INT32)wParam)
2291 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2292 else
2293 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2295 case LB_GETHORIZONTALEXTENT16:
2296 case LB_GETHORIZONTALEXTENT32:
2297 return descr->horz_extent;
2299 case LB_SETHORIZONTALEXTENT16:
2300 case LB_SETHORIZONTALEXTENT32:
2301 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2303 case LB_GETANCHORINDEX16:
2304 case LB_GETANCHORINDEX32:
2305 return descr->anchor_item;
2307 case LB_SETANCHORINDEX16:
2308 wParam = (INT32)(INT16)wParam;
2309 /* fall through */
2310 case LB_SETANCHORINDEX32:
2311 if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items))
2312 return LB_ERR;
2313 descr->anchor_item = (INT32)wParam;
2314 return LB_OKAY;
2316 case LB_DIR16:
2317 return LISTBOX_Directory( wnd, descr, wParam,
2318 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2320 case LB_DIR32:
2321 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2323 case LB_GETLOCALE32:
2324 return descr->locale;
2326 case LB_SETLOCALE32:
2327 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2328 return LB_OKAY;
2330 case LB_INITSTORAGE32:
2331 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2333 case LB_SETCOUNT32:
2334 return LISTBOX_SetCount( wnd, descr, (INT32)wParam );
2336 case LB_SETTABSTOPS16:
2337 return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam,
2338 (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE );
2340 case LB_SETTABSTOPS32:
2341 return LISTBOX_SetTabStops( wnd, descr, wParam,
2342 (LPINT32)lParam, FALSE );
2344 case LB_CARETON16:
2345 case LB_CARETON32:
2346 if (descr->caret_on) return LB_OKAY;
2347 descr->caret_on = TRUE;
2348 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2349 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2350 return LB_OKAY;
2352 case LB_CARETOFF16:
2353 case LB_CARETOFF32:
2354 if (!descr->caret_on) return LB_OKAY;
2355 descr->caret_on = FALSE;
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 WM_DESTROY:
2361 return LISTBOX_Destroy( wnd, descr );
2363 case WM_ENABLE:
2364 InvalidateRect32( hwnd, NULL, TRUE );
2365 return 0;
2367 case WM_SETREDRAW:
2368 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2369 return 0;
2371 case WM_GETDLGCODE:
2372 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2374 case WM_PAINT:
2376 PAINTSTRUCT32 ps;
2377 HDC32 hdc = ( wParam ) ? ((HDC32)wParam)
2378 : BeginPaint32( hwnd, &ps );
2379 ret = LISTBOX_Paint( wnd, descr, hdc );
2380 if( !wParam ) EndPaint32( hwnd, &ps );
2382 return ret;
2384 case WM_SIZE:
2385 LISTBOX_UpdateSize( wnd, descr );
2386 return 0;
2388 case WM_GETFONT:
2389 return descr->font;
2391 case WM_SETFONT:
2392 LISTBOX_SetFont( wnd, descr, (HFONT32)wParam );
2393 if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE );
2394 return 0;
2396 case WM_SETFOCUS:
2397 descr->caret_on = TRUE;
2398 if (descr->focus_item != -1)
2399 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2400 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2401 return 0;
2403 case WM_KILLFOCUS:
2404 if ((descr->focus_item != -1) && descr->caret_on)
2405 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2406 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2407 return 0;
2409 case WM_HSCROLL:
2410 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2412 case WM_VSCROLL:
2413 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2415 case WM_LBUTTONDOWN:
2416 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2417 (INT16)LOWORD(lParam),
2418 (INT16)HIWORD(lParam) );
2420 case WM_LBUTTONDBLCLK:
2421 if (descr->style & LBS_NOTIFY)
2422 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2423 return 0;
2425 case WM_MOUSEMOVE:
2426 if (GetCapture32() == hwnd)
2427 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2428 (INT16)HIWORD(lParam) );
2429 return 0;
2431 case WM_LBUTTONUP:
2432 return LISTBOX_HandleLButtonUp( wnd, descr );
2434 case WM_KEYDOWN:
2435 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2437 case WM_CHAR:
2438 return LISTBOX_HandleChar( wnd, descr, wParam );
2440 case WM_SYSTIMER:
2441 return LISTBOX_HandleSystemTimer( wnd, descr );
2443 case WM_ERASEBKGND:
2444 if (IS_OWNERDRAW(descr))
2446 RECT32 rect = { 0, 0, descr->width, descr->height };
2447 HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
2448 wParam, (LPARAM)wnd->hwndSelf );
2449 if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush );
2451 return 1;
2453 case WM_DROPFILES:
2454 if( !descr->lphc )
2455 return SendMessage32A( descr->owner, msg, wParam, lParam );
2456 break;
2458 case WM_DROPOBJECT:
2459 case WM_QUERYDROPOBJECT:
2460 case WM_DRAGSELECT:
2461 case WM_DRAGMOVE:
2462 if( !descr->lphc )
2464 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2465 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2466 dragInfo->pt.y );
2467 return SendMessage32A( descr->owner, msg, wParam, lParam );
2469 break;
2471 default:
2472 if ((msg >= WM_USER) && (msg < 0xc000))
2473 dprintf_listbox(stddeb,"Listbox %04x: unknown msg %04x wp %08x lp %08lx\n",
2474 hwnd, msg, wParam, lParam );
2475 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2477 return 0;
2480 /***********************************************************************
2481 * COMBO_Directory
2483 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT32 attrib, LPSTR dir, BOOL32 bLong)
2485 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2487 if( wnd )
2489 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2490 if( descr )
2492 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2494 RedrawWindow32( lphc->self->hwndSelf, NULL, 0,
2495 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2496 return lRet;
2499 return CB_ERR;
2502 /***********************************************************************
2503 * ComboLBWndProc
2505 * NOTE: in Windows, winproc address of the ComboLBox is the same
2506 * as that of the Listbox.
2508 LRESULT WINAPI ComboLBWndProc( HWND32 hwnd, UINT32 msg,
2509 WPARAM32 wParam, LPARAM lParam )
2511 LRESULT lRet = 0;
2512 WND *wnd = WIN_FindWndPtr( hwnd );
2514 if (wnd)
2516 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2518 dprintf_combo( stddeb, "ComboLBox [%04x]: msg %s wp %08x lp %08lx\n",
2519 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2521 if( descr || msg == WM_CREATE )
2523 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2525 switch( msg )
2527 case WM_CREATE:
2528 #define lpcs ((LPCREATESTRUCT32A)lParam)
2529 dprintf_combo(stddeb, "\tpassed parent handle = 0x%08x\n",
2530 (UINT32)lpcs->lpCreateParams);
2532 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2533 #undef lpcs
2534 return LISTBOX_Create( wnd, lphc );
2536 case WM_LBUTTONDOWN:
2537 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2538 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2540 /* avoid activation at all costs */
2542 case WM_MOUSEACTIVATE:
2543 return MA_NOACTIVATE;
2545 case WM_NCACTIVATE:
2546 return FALSE;
2548 case WM_KEYDOWN:
2549 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2551 /* for some reason(?) Windows makes it possible to
2552 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2554 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2555 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2556 && (wParam == VK_DOWN || wParam == VK_UP)) )
2558 COMBO_FlipListbox( lphc, FALSE );
2559 return 0;
2562 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2564 default:
2565 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2568 lRet = DefWindowProc32A( hwnd, msg, wParam, lParam );
2570 dprintf_combo(stddeb,"\tComboLBox: default on msg [%04x]\n", (UINT16)msg );
2573 return lRet;