Small fixes.
[wine/wine-kai.git] / controls / listbox.c
blob9f97f5d7dac99fcc23ff987efef879c6f989c97d
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <string.h>
8 #include "windows.h"
9 #include "winerror.h"
10 #include "drive.h"
11 #include "heap.h"
12 #include "spy.h"
13 #include "win.h"
14 #include "combo.h"
15 #include "debug.h"
16 #include "tweak.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 & WS_VSCROLL)) return;
175 /* It is important that we check descr->style, and not wnd->dwStyle,
176 for WS_VSCROLL, as the former is exactly the one passed in
177 argument to CreateWindow.
178 In Windows (and from now on in Wine :) a listbox created
179 with such a style (no WS_SCROLL) does not update
180 the scrollbar with listbox-related data, thus letting
181 the programmer use it for his/her own purposes. */
183 if (descr->style & LBS_NOREDRAW) return;
184 info.cbSize = sizeof(info);
186 if (descr->style & LBS_MULTICOLUMN)
188 info.nMin = 0;
189 info.nMax = (descr->nb_items - 1) / descr->page_size;
190 info.nPos = descr->top_item / descr->page_size;
191 info.nPage = descr->width / descr->column_width;
192 if (info.nPage < 1) info.nPage = 1;
193 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
194 if (descr->style & LBS_DISABLENOSCROLL)
195 info.fMask |= SIF_DISABLENOSCROLL;
196 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
197 info.nMax = 0;
198 info.fMask = SIF_RANGE;
199 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
201 else
203 info.nMin = 0;
204 info.nMax = descr->nb_items - 1;
205 info.nPos = descr->top_item;
206 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
207 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
208 if (descr->style & LBS_DISABLENOSCROLL)
209 info.fMask |= SIF_DISABLENOSCROLL;
210 SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE );
212 if (descr->horz_extent)
214 info.nMin = 0;
215 info.nMax = descr->horz_extent - 1;
216 info.nPos = descr->horz_pos;
217 info.nPage = descr->width;
218 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
219 if (descr->style & LBS_DISABLENOSCROLL)
220 info.fMask |= SIF_DISABLENOSCROLL;
221 SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE );
227 /***********************************************************************
228 * LISTBOX_SetTopItem
230 * Set the top item of the listbox, scrolling up or down if necessary.
232 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index,
233 BOOL32 scroll )
235 INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr );
236 if (index > max) index = max;
237 if (index < 0) index = 0;
238 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
239 if (descr->top_item == index) return LB_OKAY;
240 if (descr->style & LBS_MULTICOLUMN)
242 INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width;
243 if (scroll && (abs(diff) < descr->width))
244 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
245 SW_INVALIDATE | SW_ERASE );
246 else
247 scroll = FALSE;
249 else if (scroll)
251 INT32 diff;
252 if (descr->style & LBS_OWNERDRAWVARIABLE)
254 INT32 i;
255 diff = 0;
256 if (index > descr->top_item)
258 for (i = index - 1; i >= descr->top_item; i--)
259 diff -= descr->items[i].height;
261 else
263 for (i = index; i < descr->top_item; i++)
264 diff += descr->items[i].height;
267 else
268 diff = (descr->top_item - index) * descr->item_height;
270 if (abs(diff) < descr->height)
271 ScrollWindowEx32( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
272 SW_INVALIDATE | SW_ERASE );
273 else
274 scroll = FALSE;
276 if (!scroll) InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
277 descr->top_item = index;
278 LISTBOX_UpdateScroll( wnd, descr );
279 return LB_OKAY;
283 /***********************************************************************
284 * LISTBOX_UpdatePage
286 * Update the page size. Should be called when the size of
287 * the client area or the item height changes.
289 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
291 INT32 page_size;
293 if ((page_size = descr->height / descr->item_height) < 1) page_size = 1;
294 if (page_size == descr->page_size) return;
295 descr->page_size = page_size;
296 if (descr->style & LBS_MULTICOLUMN)
297 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
298 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
302 /***********************************************************************
303 * LISTBOX_UpdateSize
305 * Update the size of the listbox. Should be called when the size of
306 * the client area changes.
308 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
310 RECT32 rect;
312 GetClientRect32( wnd->hwndSelf, &rect );
313 descr->width = rect.right - rect.left;
314 descr->height = rect.bottom - rect.top;
315 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
317 if ((descr->height > descr->item_height) &&
318 (descr->height % descr->item_height))
320 TRACE(listbox, "[%04x]: changing height %d -> %d\n",
321 wnd->hwndSelf, descr->height,
322 descr->height - descr->height%descr->item_height );
323 SetWindowPos32( wnd->hwndSelf, 0, 0, 0,
324 wnd->rectWindow.right - wnd->rectWindow.left,
325 wnd->rectWindow.bottom - wnd->rectWindow.top -
326 (descr->height % descr->item_height),
327 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
328 return;
331 TRACE(listbox, "[%04x]: new size = %d,%d\n",
332 wnd->hwndSelf, descr->width, descr->height );
333 LISTBOX_UpdatePage( wnd, descr );
334 LISTBOX_UpdateScroll( wnd, descr );
338 /***********************************************************************
339 * LISTBOX_GetItemRect
341 * Get the rectangle enclosing an item, in listbox client coordinates.
342 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
344 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT32 index,
345 RECT32 *rect )
347 /* Index <= 0 is legal even on empty listboxes */
348 if (index && (index >= descr->nb_items)) return -1;
349 SetRect32( rect, 0, 0, descr->width, descr->height );
350 if (descr->style & LBS_MULTICOLUMN)
352 INT32 col = (index / descr->page_size) -
353 (descr->top_item / descr->page_size);
354 rect->left += col * descr->column_width;
355 rect->right = rect->left + descr->column_width;
356 rect->top += (index % descr->page_size) * descr->item_height;
357 rect->bottom = rect->top + descr->item_height;
359 else if (descr->style & LBS_OWNERDRAWVARIABLE)
361 INT32 i;
362 rect->right += descr->horz_pos;
363 if ((index >= 0) && (index < descr->nb_items))
365 if (index < descr->top_item)
367 for (i = descr->top_item-1; i >= index; i--)
368 rect->top -= descr->items[i].height;
370 else
372 for (i = descr->top_item; i < index; i++)
373 rect->top += descr->items[i].height;
375 rect->bottom = rect->top + descr->items[index].height;
379 else
381 rect->top += (index - descr->top_item) * descr->item_height;
382 rect->bottom = rect->top + descr->item_height;
383 rect->right += descr->horz_pos;
386 return ((rect->left < descr->width) && (rect->right > 0) &&
387 (rect->top < descr->height) && (rect->bottom > 0));
391 /***********************************************************************
392 * LISTBOX_GetItemFromPoint
394 * Return the item nearest from point (x,y) (in client coordinates).
396 static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
397 INT32 x, INT32 y )
399 INT32 index = descr->top_item;
401 if (!descr->nb_items) return -1; /* No items */
402 if (descr->style & LBS_OWNERDRAWVARIABLE)
404 INT32 pos = 0;
405 if (y >= 0)
407 while (index < descr->nb_items)
409 if ((pos += descr->items[index].height) > y) break;
410 index++;
413 else
415 while (index > 0)
417 index--;
418 if ((pos -= descr->items[index].height) <= y) break;
422 else if (descr->style & LBS_MULTICOLUMN)
424 if (y >= descr->item_height * descr->page_size) return -1;
425 if (y >= 0) index += y / descr->item_height;
426 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
427 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
429 else
431 index += (y / descr->item_height);
433 if (index < 0) return 0;
434 if (index >= descr->nb_items) return -1;
435 return index;
439 /***********************************************************************
440 * LISTBOX_PaintItem
442 * Paint an item.
444 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc,
445 const RECT32 *rect, INT32 index, UINT32 action )
447 LB_ITEMDATA *item = NULL;
448 if (index < descr->nb_items) item = &descr->items[index];
450 if (IS_OWNERDRAW(descr))
452 DRAWITEMSTRUCT32 dis;
453 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
455 dis.CtlType = ODT_LISTBOX;
456 dis.CtlID = id;
457 dis.hwndItem = wnd->hwndSelf;
458 dis.itemAction = action;
459 dis.hDC = hdc;
460 dis.itemID = index;
461 dis.itemState = 0;
462 if (item && item->selected) dis.itemState |= ODS_SELECTED;
463 if ((descr->focus_item == index) &&
464 (descr->caret_on) &&
465 (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
466 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
467 dis.itemData = item ? item->data : 0;
468 dis.rcItem = *rect;
469 TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x "
470 "state=%02x rect=%d,%d-%d,%d\n",
471 wnd->hwndSelf, index, item ? item->str : "", action,
472 dis.itemState, rect->left, rect->top,
473 rect->right, rect->bottom );
474 SendMessage32A(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
476 else
478 COLORREF oldText = 0, oldBk = 0;
480 if (action == ODA_FOCUS)
482 DrawFocusRect32( hdc, rect );
483 return;
485 if (item && item->selected)
487 oldBk = SetBkColor32( hdc, GetSysColor32( COLOR_HIGHLIGHT ) );
488 oldText = SetTextColor32( hdc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
491 TRACE(listbox, "[%04x]: painting %d (%s) action=%02x "
492 "rect=%d,%d-%d,%d\n",
493 wnd->hwndSelf, index, item ? item->str : "", action,
494 rect->left, rect->top, rect->right, rect->bottom );
495 if (!item)
496 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
497 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
498 else if (!(descr->style & LBS_USETABSTOPS))
499 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
500 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
501 strlen(item->str), NULL );
502 else
504 /* Output empty string to paint background in the full width. */
505 ExtTextOut32A( hdc, rect->left + 1, rect->top + 1,
506 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
507 TabbedTextOut32A( hdc, rect->left + 1 , rect->top + 1,
508 item->str, strlen(item->str),
509 descr->nb_tabs, descr->tabs, 0);
511 if (item && item->selected)
513 SetBkColor32( hdc, oldBk );
514 SetTextColor32( hdc, oldText );
516 if ((descr->focus_item == index) &&
517 (descr->caret_on) &&
518 (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect );
523 /***********************************************************************
524 * LISTBOX_SetRedraw
526 * Change the redraw flag.
528 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on )
530 if (on)
532 if (!(descr->style & LBS_NOREDRAW)) return;
533 descr->style &= ~LBS_NOREDRAW;
534 LISTBOX_UpdateScroll( wnd, descr );
536 else descr->style |= LBS_NOREDRAW;
540 /***********************************************************************
541 * LISTBOX_RepaintItem
543 * Repaint a single item synchronously.
545 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index,
546 UINT32 action )
548 HDC32 hdc;
549 RECT32 rect;
550 HFONT32 oldFont = 0;
551 HBRUSH32 hbrush, oldBrush = 0;
553 if (descr->style & LBS_NOREDRAW) return;
554 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
555 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE ))) return;
556 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
557 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
558 hdc, (LPARAM)wnd->hwndSelf );
559 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
560 if (wnd->dwStyle & WS_DISABLED)
561 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
562 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
563 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
564 if (oldFont) SelectObject32( hdc, oldFont );
565 if (oldBrush) SelectObject32( hdc, oldBrush );
566 ReleaseDC32( wnd->hwndSelf, hdc );
570 /***********************************************************************
571 * LISTBOX_InitStorage
573 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items,
574 DWORD bytes )
576 LB_ITEMDATA *item;
578 nb_items += LB_ARRAY_GRANULARITY - 1;
579 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
580 if (descr->items)
581 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
582 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
583 nb_items * sizeof(LB_ITEMDATA) )))
585 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
586 return LB_ERRSPACE;
588 descr->items = item;
589 return LB_OKAY;
593 /***********************************************************************
594 * LISTBOX_SetTabStops
596 static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count,
597 LPINT32 tabs, BOOL32 short_ints )
599 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
600 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
601 if (!(descr->nb_tabs = count))
603 descr->tabs = NULL;
604 return TRUE;
606 /* FIXME: count = 1 */
607 if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0,
608 descr->nb_tabs * sizeof(INT32) )))
609 return FALSE;
610 if (short_ints)
612 INT32 i;
613 LPINT16 p = (LPINT16)tabs;
614 dbg_decl_str(listbox, 256);
616 for (i = 0; i < descr->nb_tabs; i++) {
617 descr->tabs[i] = *p++<<1; /* FIXME */
618 if(TRACE_ON(listbox))
619 dsprintf(listbox, "%hd ", descr->tabs[i]);
621 TRACE(listbox, "[%04x]: settabstops %s\n",
622 wnd->hwndSelf, dbg_str(listbox));
624 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) );
625 /* FIXME: repaint the window? */
626 return TRUE;
630 /***********************************************************************
631 * LISTBOX_GetText
633 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index,
634 LPSTR buffer )
636 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
637 if (HAS_STRINGS(descr))
639 if (!buffer)
640 return strlen(descr->items[index].str);
641 lstrcpy32A( buffer, descr->items[index].str );
642 return strlen(buffer);
643 } else {
644 if (buffer)
645 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
646 return sizeof(DWORD);
651 /***********************************************************************
652 * LISTBOX_FindStringPos
654 * Find the nearest string located before a given string in sort order.
655 * If 'exact' is TRUE, return an error if we don't get an exact match.
657 static INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
658 BOOL32 exact )
660 INT32 index, min, max, res = -1;
662 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
663 min = 0;
664 max = descr->nb_items;
665 while (min != max)
667 index = (min + max) / 2;
668 if (HAS_STRINGS(descr))
669 res = lstrcmpi32A( descr->items[index].str, str );
670 else
672 COMPAREITEMSTRUCT32 cis;
673 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
675 cis.CtlType = ODT_LISTBOX;
676 cis.CtlID = id;
677 cis.hwndItem = wnd->hwndSelf;
678 cis.itemID1 = index;
679 cis.itemData1 = descr->items[index].data;
680 cis.itemID2 = -1;
681 cis.itemData2 = (DWORD)str;
682 cis.dwLocaleId = descr->locale;
683 res = SendMessage32A( descr->owner, WM_COMPAREITEM,
684 id, (LPARAM)&cis );
686 if (!res) return index;
687 if (res > 0) max = index;
688 else min = index + 1;
690 return exact ? -1 : max;
694 /***********************************************************************
695 * LISTBOX_FindFileStrPos
697 * Find the nearest string located before a given string in directory
698 * sort order (i.e. first files, then directories, then drives).
700 static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
702 INT32 min, max, res = -1;
704 if (!HAS_STRINGS(descr))
705 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
706 min = 0;
707 max = descr->nb_items;
708 while (min != max)
710 INT32 index = (min + max) / 2;
711 const char *p = descr->items[index].str;
712 if (*p == '[') /* drive or directory */
714 if (*str != '[') res = -1;
715 else if (p[1] == '-') /* drive */
717 if (str[1] == '-') res = str[2] - p[2];
718 else res = -1;
720 else /* directory */
722 if (str[1] == '-') res = 1;
723 else res = lstrcmpi32A( str, p );
726 else /* filename */
728 if (*str == '[') res = 1;
729 else res = lstrcmpi32A( str, p );
731 if (!res) return index;
732 if (res < 0) max = index;
733 else min = index + 1;
735 return max;
739 /***********************************************************************
740 * LISTBOX_FindString
742 * Find the item beginning with a given string.
744 static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start,
745 LPCSTR str, BOOL32 exact )
747 INT32 i;
748 LB_ITEMDATA *item;
750 if (start >= descr->nb_items) start = -1;
751 item = descr->items + start + 1;
752 if (HAS_STRINGS(descr))
754 if (!str) return LB_ERR;
755 if (exact)
757 for (i = start + 1; i < descr->nb_items; i++, item++)
758 if (!lstrcmpi32A( str, item->str )) return i;
759 for (i = 0, item = descr->items; i <= start; i++, item++)
760 if (!lstrcmpi32A( str, item->str )) return i;
762 else
764 /* Special case for drives and directories: ignore prefix */
765 #define CHECK_DRIVE(item) \
766 if ((item)->str[0] == '[') \
768 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
769 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
770 return i; \
773 INT32 len = strlen(str);
774 for (i = start + 1; i < descr->nb_items; i++, item++)
776 if (!lstrncmpi32A( str, item->str, len )) return i;
777 CHECK_DRIVE(item);
779 for (i = 0, item = descr->items; i <= start; i++, item++)
781 if (!lstrncmpi32A( str, item->str, len )) return i;
782 CHECK_DRIVE(item);
784 #undef CHECK_DRIVE
787 else
789 if (exact && (descr->style & LBS_SORT))
790 /* If sorted, use a WM_COMPAREITEM binary search */
791 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
793 /* Otherwise use a linear search */
794 for (i = start + 1; i < descr->nb_items; i++, item++)
795 if (item->data == (DWORD)str) return i;
796 for (i = 0, item = descr->items; i <= start; i++, item++)
797 if (item->data == (DWORD)str) return i;
799 return LB_ERR;
803 /***********************************************************************
804 * LISTBOX_GetSelCount
806 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
808 INT32 i, count;
809 LB_ITEMDATA *item = descr->items;
811 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
812 for (i = count = 0; i < descr->nb_items; i++, item++)
813 if (item->selected) count++;
814 return count;
818 /***********************************************************************
819 * LISTBOX_GetSelItems16
821 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
822 LPINT16 array )
824 INT32 i, count;
825 LB_ITEMDATA *item = descr->items;
827 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
828 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
829 if (item->selected) array[count++] = (INT16)i;
830 return count;
834 /***********************************************************************
835 * LISTBOX_GetSelItems32
837 static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max,
838 LPINT32 array )
840 INT32 i, count;
841 LB_ITEMDATA *item = descr->items;
843 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
844 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
845 if (item->selected) array[count++] = i;
846 return count;
850 /***********************************************************************
851 * LISTBOX_Paint
853 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc )
855 INT32 i, col_pos = descr->page_size - 1;
856 RECT32 rect;
857 HFONT32 oldFont = 0;
858 HBRUSH32 hbrush, oldBrush = 0;
860 SetRect32( &rect, 0, 0, descr->width, descr->height );
861 if (descr->style & LBS_NOREDRAW) return 0;
862 if (descr->style & LBS_MULTICOLUMN)
863 rect.right = rect.left + descr->column_width;
864 else if (descr->horz_pos)
866 SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL );
867 rect.right += descr->horz_pos;
870 if (descr->font) oldFont = SelectObject32( hdc, descr->font );
871 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
872 hdc, (LPARAM)wnd->hwndSelf );
873 if (hbrush) oldBrush = SelectObject32( hdc, hbrush );
874 if (wnd->dwStyle & WS_DISABLED)
875 SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
877 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
878 (GetFocus32() == wnd->hwndSelf))
880 /* Special case for empty listbox: paint focus rect */
881 rect.bottom = rect.top + descr->item_height;
882 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
883 ODA_DRAWENTIRE );
884 rect.top = rect.bottom;
887 for (i = descr->top_item; i < descr->nb_items; i++)
889 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
890 rect.bottom = rect.top + descr->item_height;
891 else
892 rect.bottom = rect.top + descr->items[i].height;
894 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
895 rect.top = rect.bottom;
897 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
899 if (!IS_OWNERDRAW(descr))
901 /* Clear the bottom of the column */
902 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
903 if (rect.top < descr->height)
905 rect.bottom = descr->height;
906 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
907 &rect, NULL, 0, NULL );
911 /* Go to the next column */
912 rect.left += descr->column_width;
913 rect.right += descr->column_width;
914 rect.top = 0;
915 col_pos = descr->page_size - 1;
917 else
919 col_pos--;
920 if (rect.top >= descr->height) break;
924 if (!IS_OWNERDRAW(descr))
926 /* Clear the remainder of the client area */
927 SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) );
928 if (rect.top < descr->height)
930 rect.bottom = descr->height;
931 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
932 &rect, NULL, 0, NULL );
934 if (rect.right < descr->width)
936 rect.left = rect.right;
937 rect.right = descr->width;
938 rect.top = 0;
939 rect.bottom = descr->height;
940 ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
941 &rect, NULL, 0, NULL );
944 if (oldFont) SelectObject32( hdc, oldFont );
945 if (oldBrush) SelectObject32( hdc, oldBrush );
946 return 0;
950 /***********************************************************************
951 * LISTBOX_InvalidateItems
953 * Invalidate all items from a given item. If the specified item is not
954 * visible, nothing happens.
956 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index )
958 RECT32 rect;
960 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
962 rect.bottom = descr->height;
963 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
964 if (descr->style & LBS_MULTICOLUMN)
966 /* Repaint the other columns */
967 rect.left = rect.right;
968 rect.right = descr->width;
969 rect.top = 0;
970 InvalidateRect32( wnd->hwndSelf, &rect, TRUE );
976 /***********************************************************************
977 * LISTBOX_GetItemHeight
979 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index )
981 if (descr->style & LBS_OWNERDRAWVARIABLE)
983 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
984 return descr->items[index].height;
986 else return descr->item_height;
990 /***********************************************************************
991 * LISTBOX_SetItemHeight
993 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index,
994 UINT32 height )
996 if (!height) height = 1;
998 if (descr->style & LBS_OWNERDRAWVARIABLE)
1000 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1001 TRACE(listbox, "[%04x]: item %d height = %d\n",
1002 wnd->hwndSelf, index, height );
1003 descr->items[index].height = height;
1004 LISTBOX_UpdateScroll( wnd, descr );
1005 LISTBOX_InvalidateItems( wnd, descr, index );
1007 else if (height != descr->item_height)
1009 TRACE(listbox, "[%04x]: new height = %d\n",
1010 wnd->hwndSelf, height );
1011 descr->item_height = height;
1012 LISTBOX_UpdatePage( wnd, descr );
1013 LISTBOX_UpdateScroll( wnd, descr );
1014 InvalidateRect32( wnd->hwndSelf, 0, TRUE );
1016 return LB_OKAY;
1020 /***********************************************************************
1021 * LISTBOX_SetHorizontalPos
1023 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos )
1025 INT32 diff;
1027 if (pos > descr->horz_extent - descr->width)
1028 pos = descr->horz_extent - descr->width;
1029 if (pos < 0) pos = 0;
1030 if (!(diff = descr->horz_pos - pos)) return;
1031 TRACE(listbox, "[%04x]: new horz pos = %d\n",
1032 wnd->hwndSelf, pos );
1033 descr->horz_pos = pos;
1034 LISTBOX_UpdateScroll( wnd, descr );
1035 if (abs(diff) < descr->width)
1036 ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1037 SW_INVALIDATE | SW_ERASE );
1038 else
1039 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1043 /***********************************************************************
1044 * LISTBOX_SetHorizontalExtent
1046 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1047 UINT32 extent )
1049 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1050 return LB_OKAY;
1051 if (extent <= 0) extent = 1;
1052 if (extent == descr->horz_extent) return LB_OKAY;
1053 TRACE(listbox, "[%04x]: new horz extent = %d\n",
1054 wnd->hwndSelf, extent );
1055 descr->horz_extent = extent;
1056 if (descr->horz_pos > extent - descr->width)
1057 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1058 else
1059 LISTBOX_UpdateScroll( wnd, descr );
1060 return LB_OKAY;
1064 /***********************************************************************
1065 * LISTBOX_SetColumnWidth
1067 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width)
1069 width += 2; /* For left and right margin */
1070 if (width == descr->column_width) return LB_OKAY;
1071 TRACE(listbox, "[%04x]: new column width = %d\n",
1072 wnd->hwndSelf, width );
1073 descr->column_width = width;
1074 LISTBOX_UpdatePage( wnd, descr );
1075 return LB_OKAY;
1079 /***********************************************************************
1080 * LISTBOX_SetFont
1082 * Returns the item height.
1084 static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font )
1086 HDC32 hdc;
1087 HFONT32 oldFont = 0;
1088 TEXTMETRIC32A tm;
1090 descr->font = font;
1092 if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE )))
1094 ERR(listbox, "unable to get DC.\n" );
1095 return 16;
1097 if (font) oldFont = SelectObject32( hdc, font );
1098 GetTextMetrics32A( hdc, &tm );
1099 if (oldFont) SelectObject32( hdc, oldFont );
1100 ReleaseDC32( wnd->hwndSelf, hdc );
1101 if (!IS_OWNERDRAW(descr))
1102 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1103 return tm.tmHeight ;
1107 /***********************************************************************
1108 * LISTBOX_MakeItemVisible
1110 * Make sure that a given item is partially or fully visible.
1112 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index,
1113 BOOL32 fully )
1115 INT32 top;
1117 if (index <= descr->top_item) top = index;
1118 else if (descr->style & LBS_MULTICOLUMN)
1120 INT32 cols = descr->width;
1121 if (!fully) cols += descr->column_width - 1;
1122 if (cols >= descr->column_width) cols /= descr->column_width;
1123 else cols = 1;
1124 if (index < descr->top_item + (descr->page_size * cols)) return;
1125 top = index - descr->page_size * (cols - 1);
1127 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1129 INT32 height = fully ? descr->items[index].height : 1;
1130 for (top = index; top > descr->top_item; top--)
1131 if ((height += descr->items[top-1].height) > descr->height) break;
1133 else
1135 if (index < descr->top_item + descr->page_size) return;
1136 if (!fully && (index == descr->top_item + descr->page_size) &&
1137 (descr->height > (descr->page_size * descr->item_height))) return;
1138 top = index - descr->page_size + 1;
1140 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1144 /***********************************************************************
1145 * LISTBOX_SelectItemRange
1147 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1149 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first,
1150 INT32 last, BOOL32 on )
1152 INT32 i;
1154 /* A few sanity checks */
1156 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1157 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1158 if (last == -1) last = descr->nb_items - 1;
1159 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1160 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1161 /* selected_item reflects last selected/unselected item on multiple sel */
1162 descr->selected_item = last;
1164 if (on) /* Turn selection on */
1166 for (i = first; i <= last; i++)
1168 if (descr->items[i].selected) continue;
1169 descr->items[i].selected = TRUE;
1170 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1173 else /* Turn selection off */
1175 for (i = first; i <= last; i++)
1177 if (!descr->items[i].selected) continue;
1178 descr->items[i].selected = FALSE;
1179 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1182 return LB_OKAY;
1186 /***********************************************************************
1187 * LISTBOX_SetCaretIndex
1189 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index,
1190 BOOL32 fully_visible )
1192 INT32 oldfocus = descr->focus_item;
1194 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1195 if (index == oldfocus) return LB_OKAY;
1196 descr->focus_item = index;
1197 if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1198 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1199 if (index != -1)
1201 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1202 if (descr->caret_on && (GetFocus32() == wnd->hwndSelf))
1203 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1205 return LB_OKAY;
1209 /***********************************************************************
1210 * LISTBOX_SetSelection
1212 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index,
1213 BOOL32 on, BOOL32 send_notify )
1215 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1216 if (descr->style & LBS_MULTIPLESEL)
1218 if (index == -1) /* Select all items */
1219 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1220 else /* Only one item */
1221 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1223 else
1225 INT32 oldsel = descr->selected_item;
1226 if (index == oldsel) return LB_OKAY;
1227 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1228 if (index != -1) descr->items[index].selected = TRUE;
1229 descr->selected_item = index;
1230 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1231 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1232 if (send_notify) SEND_NOTIFICATION( wnd, descr,
1233 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1235 return LB_OKAY;
1239 /***********************************************************************
1240 * LISTBOX_MoveCaret
1242 * Change the caret position and extend the selection to the new caret.
1244 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index,
1245 BOOL32 fully_visible )
1247 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1248 if (descr->style & LBS_EXTENDEDSEL)
1250 if (descr->anchor_item != -1)
1252 INT32 first = MIN( descr->focus_item, descr->anchor_item );
1253 INT32 last = MAX( descr->focus_item, descr->anchor_item );
1254 if (first > 0)
1255 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1256 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1257 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1260 else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1))
1262 /* Set selection to new caret item */
1263 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1268 /***********************************************************************
1269 * LISTBOX_InsertItem
1271 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index,
1272 LPSTR str, DWORD data )
1274 LB_ITEMDATA *item;
1275 INT32 max_items;
1277 if (index == -1) index = descr->nb_items;
1278 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1279 if (!descr->items) max_items = 0;
1280 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1281 if (descr->nb_items == max_items)
1283 /* We need to grow the array */
1284 max_items += LB_ARRAY_GRANULARITY;
1285 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1286 max_items * sizeof(LB_ITEMDATA) )))
1288 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1289 return LB_ERRSPACE;
1291 descr->items = item;
1294 /* Insert the item structure */
1296 item = &descr->items[index];
1297 if (index < descr->nb_items)
1298 RtlMoveMemory( item + 1, item,
1299 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1300 item->str = str;
1301 item->data = data;
1302 item->height = 0;
1303 item->selected = FALSE;
1304 descr->nb_items++;
1306 /* Get item height */
1308 if (descr->style & LBS_OWNERDRAWVARIABLE)
1310 MEASUREITEMSTRUCT32 mis;
1311 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1313 mis.CtlType = ODT_LISTBOX;
1314 mis.CtlID = id;
1315 mis.itemID = index;
1316 mis.itemData = descr->items[index].data;
1317 mis.itemHeight = descr->item_height;
1318 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1319 item->height = mis.itemHeight ? mis.itemHeight : 1;
1320 TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n",
1321 wnd->hwndSelf, index, str ? str : "", item->height );
1324 /* Repaint the items */
1326 LISTBOX_UpdateScroll( wnd, descr );
1327 LISTBOX_InvalidateItems( wnd, descr, index );
1329 /* Move selection and focused item */
1331 if (index <= descr->selected_item) descr->selected_item++;
1332 if (index <= descr->focus_item)
1334 descr->focus_item++;
1335 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1338 /* If listbox was empty, set focus to the first item */
1340 if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1341 return LB_OKAY;
1345 /***********************************************************************
1346 * LISTBOX_InsertString
1348 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index,
1349 LPCSTR str )
1351 LPSTR new_str = NULL;
1352 DWORD data = 0;
1353 LRESULT ret;
1355 if (HAS_STRINGS(descr))
1357 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1359 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1360 return LB_ERRSPACE;
1363 else data = (DWORD)str;
1365 if (index == -1) index = descr->nb_items;
1366 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1368 if (new_str) HeapFree( descr->heap, 0, new_str );
1369 return ret;
1372 TRACE(listbox, "[%04x]: added item %d '%s'\n",
1373 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1374 return index;
1378 /***********************************************************************
1379 * LISTBOX_DeleteItem
1381 * Delete the content of an item. 'index' must be a valid index.
1383 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index )
1385 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1386 * while Win95 sends it for all items with user data.
1387 * It's probably better to send it too often than not
1388 * often enough, so this is what we do here.
1390 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1392 DELETEITEMSTRUCT32 dis;
1393 UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1395 dis.CtlType = ODT_LISTBOX;
1396 dis.CtlID = id;
1397 dis.itemID = index;
1398 dis.hwndItem = wnd->hwndSelf;
1399 dis.itemData = descr->items[index].data;
1400 SendMessage32A( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1402 if (HAS_STRINGS(descr) && descr->items[index].str)
1403 HeapFree( descr->heap, 0, descr->items[index].str );
1407 /***********************************************************************
1408 * LISTBOX_RemoveItem
1410 * Remove an item from the listbox and delete its content.
1412 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index )
1414 LB_ITEMDATA *item;
1415 INT32 max_items;
1417 if (index == -1) index = descr->nb_items - 1;
1418 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1419 LISTBOX_DeleteItem( wnd, descr, index );
1421 /* Remove the item */
1423 item = &descr->items[index];
1424 if (index < descr->nb_items-1)
1425 RtlMoveMemory( item, item + 1,
1426 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1427 descr->nb_items--;
1428 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1430 /* Shrink the item array if possible */
1432 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1433 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1435 max_items -= LB_ARRAY_GRANULARITY;
1436 item = HeapReAlloc( descr->heap, 0, descr->items,
1437 max_items * sizeof(LB_ITEMDATA) );
1438 if (item) descr->items = item;
1441 /* Repaint the items */
1443 LISTBOX_UpdateScroll( wnd, descr );
1444 LISTBOX_InvalidateItems( wnd, descr, index );
1446 /* Move selection and focused item */
1448 if (index <= descr->selected_item) descr->selected_item--;
1449 if (index <= descr->focus_item)
1451 descr->focus_item--;
1452 LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE );
1454 return LB_OKAY;
1458 /***********************************************************************
1459 * LISTBOX_ResetContent
1461 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1463 INT32 i;
1465 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1466 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1467 descr->nb_items = 0;
1468 descr->top_item = 0;
1469 descr->selected_item = -1;
1470 descr->focus_item = 0;
1471 descr->anchor_item = -1;
1472 descr->items = NULL;
1473 LISTBOX_UpdateScroll( wnd, descr );
1474 InvalidateRect32( wnd->hwndSelf, NULL, TRUE );
1478 /***********************************************************************
1479 * LISTBOX_SetCount
1481 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count )
1483 LRESULT ret;
1485 if (HAS_STRINGS(descr)) return LB_ERR;
1486 /* FIXME: this is far from optimal... */
1487 if (count > descr->nb_items)
1489 while (count > descr->nb_items)
1490 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1491 return ret;
1493 else if (count < descr->nb_items)
1495 while (count < descr->nb_items)
1496 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1497 return ret;
1499 return LB_OKAY;
1503 /***********************************************************************
1504 * LISTBOX_Directory
1506 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib,
1507 LPCSTR filespec, BOOL32 long_names )
1509 HANDLE32 handle;
1510 LRESULT ret = LB_OKAY;
1511 WIN32_FIND_DATA32A entry;
1512 int pos;
1514 if ((handle = FindFirstFile32A(filespec,&entry)) == INVALID_HANDLE_VALUE32)
1516 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1518 else
1522 char buffer[270];
1523 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1525 if (!(attrib & DDL_DIRECTORY) ||
1526 !strcmp( entry.cAlternateFileName, "." )) continue;
1527 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1528 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1530 else /* not a directory */
1532 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1533 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1535 if ((attrib & DDL_EXCLUSIVE) &&
1536 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1537 continue;
1538 #undef ATTRIBS
1539 if (long_names) strcpy( buffer, entry.cFileName );
1540 else strcpy( buffer, entry.cAlternateFileName );
1542 if (!long_names) CharLower32A( buffer );
1543 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1544 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1545 break;
1546 } while (FindNextFile32A( handle, &entry ));
1547 FindClose32( handle );
1550 if ((ret >= 0) && (attrib & DDL_DRIVES))
1552 char buffer[] = "[-a-]";
1553 int drive;
1554 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1556 if (!DRIVE_IsValid(drive)) continue;
1557 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1558 break;
1561 return ret;
1565 /***********************************************************************
1566 * LISTBOX_HandleVScroll
1568 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1569 WPARAM32 wParam, LPARAM lParam )
1571 SCROLLINFO info;
1573 if (descr->style & LBS_MULTICOLUMN) return 0;
1574 switch(LOWORD(wParam))
1576 case SB_LINEUP:
1577 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1578 break;
1579 case SB_LINEDOWN:
1580 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1581 break;
1582 case SB_PAGEUP:
1583 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1584 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1585 break;
1586 case SB_PAGEDOWN:
1587 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1588 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1589 break;
1590 case SB_THUMBPOSITION:
1591 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1592 break;
1593 case SB_THUMBTRACK:
1594 info.cbSize = sizeof(info);
1595 info.fMask = SIF_TRACKPOS;
1596 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1597 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1598 break;
1599 case SB_TOP:
1600 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1601 break;
1602 case SB_BOTTOM:
1603 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1604 break;
1606 return 0;
1610 /***********************************************************************
1611 * LISTBOX_HandleHScroll
1613 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1614 WPARAM32 wParam, LPARAM lParam )
1616 SCROLLINFO info;
1617 INT32 page;
1619 if (descr->style & LBS_MULTICOLUMN)
1621 switch(LOWORD(wParam))
1623 case SB_LINELEFT:
1624 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1625 TRUE );
1626 break;
1627 case SB_LINERIGHT:
1628 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1629 TRUE );
1630 break;
1631 case SB_PAGELEFT:
1632 page = descr->width / descr->column_width;
1633 if (page < 1) page = 1;
1634 LISTBOX_SetTopItem( wnd, descr,
1635 descr->top_item - page * descr->page_size, TRUE );
1636 break;
1637 case SB_PAGERIGHT:
1638 page = descr->width / descr->column_width;
1639 if (page < 1) page = 1;
1640 LISTBOX_SetTopItem( wnd, descr,
1641 descr->top_item + page * descr->page_size, TRUE );
1642 break;
1643 case SB_THUMBPOSITION:
1644 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1645 TRUE );
1646 break;
1647 case SB_THUMBTRACK:
1648 info.cbSize = sizeof(info);
1649 info.fMask = SIF_TRACKPOS;
1650 GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info );
1651 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1652 TRUE );
1653 break;
1654 case SB_LEFT:
1655 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1656 break;
1657 case SB_RIGHT:
1658 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1659 break;
1662 else if (descr->horz_extent)
1664 switch(LOWORD(wParam))
1666 case SB_LINELEFT:
1667 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1668 break;
1669 case SB_LINERIGHT:
1670 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1671 break;
1672 case SB_PAGELEFT:
1673 LISTBOX_SetHorizontalPos( wnd, descr,
1674 descr->horz_pos - descr->width );
1675 break;
1676 case SB_PAGERIGHT:
1677 LISTBOX_SetHorizontalPos( wnd, descr,
1678 descr->horz_pos + descr->width );
1679 break;
1680 case SB_THUMBPOSITION:
1681 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1682 break;
1683 case SB_THUMBTRACK:
1684 info.cbSize = sizeof(info);
1685 info.fMask = SIF_TRACKPOS;
1686 GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info );
1687 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1688 break;
1689 case SB_LEFT:
1690 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1691 break;
1692 case SB_RIGHT:
1693 LISTBOX_SetHorizontalPos( wnd, descr,
1694 descr->horz_extent - descr->width );
1695 break;
1698 return 0;
1702 /***********************************************************************
1703 * LISTBOX_HandleLButtonDown
1705 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1706 WPARAM32 wParam, INT32 x, INT32 y )
1708 INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1709 TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n",
1710 wnd->hwndSelf, x, y, index );
1711 if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0;
1712 if (index != -1)
1714 if (descr->style & LBS_EXTENDEDSEL)
1716 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1717 if (wParam & MK_CONTROL)
1719 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1720 LISTBOX_SetSelection( wnd, descr, index,
1721 !descr->items[index].selected, FALSE );
1723 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1725 else
1727 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1728 LISTBOX_SetSelection( wnd, descr, index,
1729 (!(descr->style & LBS_MULTIPLESEL) ||
1730 !descr->items[index].selected), FALSE );
1734 if( !descr->lphc ) SetFocus32( wnd->hwndSelf );
1735 else SetFocus32( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1736 : descr->lphc->self->hwndSelf ) ;
1738 SetCapture32( wnd->hwndSelf );
1739 if (index != -1 && !descr->lphc)
1741 if (descr->style & LBS_NOTIFY )
1742 SendMessage32A( descr->owner, WM_LBTRACKPOINT, index,
1743 MAKELPARAM( x, y ) );
1744 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1746 POINT32 pt = { x, y };
1747 if (DragDetect32( wnd->hwndSelf, pt ))
1748 SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 );
1751 return 0;
1755 /***********************************************************************
1756 * LISTBOX_HandleLButtonUp
1758 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1760 if (LISTBOX_Timer != LB_TIMER_NONE)
1761 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1762 LISTBOX_Timer = LB_TIMER_NONE;
1763 if (GetCapture32() == wnd->hwndSelf)
1765 ReleaseCapture();
1766 if (descr->style & LBS_NOTIFY)
1767 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1769 return 0;
1773 /***********************************************************************
1774 * LISTBOX_HandleTimer
1776 * Handle scrolling upon a timer event.
1777 * Return TRUE if scrolling should continue.
1779 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1780 INT32 index, TIMER_DIRECTION dir )
1782 switch(dir)
1784 case LB_TIMER_UP:
1785 if (descr->top_item) index = descr->top_item - 1;
1786 else index = 0;
1787 break;
1788 case LB_TIMER_LEFT:
1789 if (descr->top_item) index -= descr->page_size;
1790 break;
1791 case LB_TIMER_DOWN:
1792 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1793 if (index == descr->focus_item) index++;
1794 if (index >= descr->nb_items) index = descr->nb_items - 1;
1795 break;
1796 case LB_TIMER_RIGHT:
1797 if (index + descr->page_size < descr->nb_items)
1798 index += descr->page_size;
1799 break;
1800 case LB_TIMER_NONE:
1801 break;
1803 if (index == descr->focus_item) return FALSE;
1804 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1805 return TRUE;
1809 /***********************************************************************
1810 * LISTBOX_HandleSystemTimer
1812 * WM_SYSTIMER handler.
1814 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1816 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1818 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1819 LISTBOX_Timer = LB_TIMER_NONE;
1821 return 0;
1825 /***********************************************************************
1826 * LISTBOX_HandleMouseMove
1828 * WM_MOUSEMOVE handler.
1830 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1831 INT32 x, INT32 y )
1833 INT32 index;
1834 TIMER_DIRECTION dir;
1836 if (descr->style & LBS_MULTICOLUMN)
1838 if (y < 0) y = 0;
1839 else if (y >= descr->item_height * descr->page_size)
1840 y = descr->item_height * descr->page_size - 1;
1842 if (x < 0)
1844 dir = LB_TIMER_LEFT;
1845 x = 0;
1847 else if (x >= descr->width)
1849 dir = LB_TIMER_RIGHT;
1850 x = descr->width - 1;
1852 else dir = LB_TIMER_NONE; /* inside */
1854 else
1856 if (y < 0) dir = LB_TIMER_UP; /* above */
1857 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
1858 else dir = LB_TIMER_NONE; /* inside */
1861 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1862 if (index == -1) index = descr->focus_item;
1863 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
1865 /* Start/stop the system timer */
1867 if (dir != LB_TIMER_NONE)
1868 SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
1869 else if (LISTBOX_Timer != LB_TIMER_NONE)
1870 KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID );
1871 LISTBOX_Timer = dir;
1875 /***********************************************************************
1876 * LISTBOX_HandleKeyDown
1878 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM32 wParam )
1880 INT32 caret = -1;
1881 if (descr->style & LBS_WANTKEYBOARDINPUT)
1883 caret = SendMessage32A( descr->owner, WM_VKEYTOITEM,
1884 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1885 wnd->hwndSelf );
1886 if (caret == -2) return 0;
1888 if (caret == -1) switch(wParam)
1890 case VK_LEFT:
1891 if (descr->style & LBS_MULTICOLUMN)
1893 if (descr->focus_item >= descr->page_size)
1894 caret = descr->focus_item - descr->page_size;
1895 break;
1897 /* fall through */
1898 case VK_UP:
1899 caret = descr->focus_item - 1;
1900 if (caret < 0) caret = 0;
1901 break;
1902 case VK_RIGHT:
1903 if (descr->style & LBS_MULTICOLUMN)
1905 if (descr->focus_item + descr->page_size < descr->nb_items)
1906 caret = descr->focus_item + descr->page_size;
1907 break;
1909 /* fall through */
1910 case VK_DOWN:
1911 caret = descr->focus_item + 1;
1912 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1913 break;
1914 case VK_PRIOR:
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 < 0) caret = 0;
1923 break;
1924 case VK_NEXT:
1925 if (descr->style & LBS_MULTICOLUMN)
1927 INT32 page = descr->width / descr->column_width;
1928 if (page < 1) page = 1;
1929 caret = descr->focus_item + (page * descr->page_size) - 1;
1931 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
1932 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
1933 break;
1934 case VK_HOME:
1935 caret = 0;
1936 break;
1937 case VK_END:
1938 caret = descr->nb_items - 1;
1939 break;
1940 case VK_SPACE:
1941 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
1942 else if (descr->style & LBS_MULTIPLESEL)
1944 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
1945 !descr->items[descr->focus_item].selected,
1946 (descr->style & LBS_NOTIFY) != 0 );
1948 else if (descr->selected_item == -1)
1950 LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE,
1951 (descr->style & LBS_NOTIFY) != 0 );
1953 break;
1955 if (caret >= 0)
1957 if ((descr->style & LBS_EXTENDEDSEL) &&
1958 !(GetKeyState32( VK_SHIFT ) & 0x8000))
1959 descr->anchor_item = caret;
1960 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1961 if (descr->style & LBS_NOTIFY)
1963 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
1965 /* make sure that combo parent doesn't hide us */
1966 descr->lphc->wState |= CBF_NOROLLUP;
1968 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1971 return 0;
1975 /***********************************************************************
1976 * LISTBOX_HandleChar
1978 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
1979 WPARAM32 wParam )
1981 INT32 caret = -1;
1982 char str[2] = { wParam & 0xff, '\0' };
1984 if (descr->style & LBS_WANTKEYBOARDINPUT)
1986 caret = SendMessage32A( descr->owner, WM_CHARTOITEM,
1987 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
1988 wnd->hwndSelf );
1989 if (caret == -2) return 0;
1991 if (caret == -1)
1992 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
1993 if (caret != -1)
1995 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
1996 if (descr->style & LBS_NOTIFY)
1997 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1999 return 0;
2003 /***********************************************************************
2004 * LISTBOX_Create
2006 static BOOL32 LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2008 LB_DESCR *descr;
2009 MEASUREITEMSTRUCT32 mis;
2010 RECT32 rect;
2012 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2013 return FALSE;
2014 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2016 HeapFree( GetProcessHeap(), 0, descr );
2017 return FALSE;
2019 GetClientRect32( wnd->hwndSelf, &rect );
2020 descr->owner = GetParent32( wnd->hwndSelf );
2021 descr->style = wnd->dwStyle;
2022 descr->width = rect.right - rect.left;
2023 descr->height = rect.bottom - rect.top;
2024 descr->items = NULL;
2025 descr->nb_items = 0;
2026 descr->top_item = 0;
2027 descr->selected_item = -1;
2028 descr->focus_item = 0;
2029 descr->anchor_item = -1;
2030 descr->item_height = 1;
2031 descr->page_size = 1;
2032 descr->column_width = 150;
2033 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2034 descr->horz_pos = 0;
2035 descr->nb_tabs = 0;
2036 descr->tabs = NULL;
2037 descr->caret_on = TRUE;
2038 descr->font = 0;
2039 descr->locale = 0; /* FIXME */
2040 descr->lphc = lphc;
2042 if( lphc )
2044 TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n",
2045 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2046 descr->owner = lphc->self->hwndSelf;
2049 *(LB_DESCR **)wnd->wExtra = descr;
2051 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2053 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2054 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2055 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2056 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2058 if (descr->style & LBS_OWNERDRAWFIXED)
2060 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2062 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2063 descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6;
2065 else
2067 UINT32 id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2069 mis.CtlType = ODT_LISTBOX;
2070 mis.CtlID = id;
2071 mis.itemID = -1;
2072 mis.itemWidth = 0;
2073 mis.itemData = 0;
2074 mis.itemHeight = descr->item_height;
2075 SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2076 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2080 return TRUE;
2084 /***********************************************************************
2085 * LISTBOX_Destroy
2087 static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2089 LISTBOX_ResetContent( wnd, descr );
2090 HeapDestroy( descr->heap );
2091 HeapFree( GetProcessHeap(), 0, descr );
2092 wnd->wExtra[0] = 0;
2093 return TRUE;
2097 /***********************************************************************
2098 * ListBoxWndProc
2100 LRESULT WINAPI ListBoxWndProc( HWND32 hwnd, UINT32 msg,
2101 WPARAM32 wParam, LPARAM lParam )
2103 LRESULT ret;
2104 LB_DESCR *descr;
2105 WND *wnd = WIN_FindWndPtr( hwnd );
2107 if (!wnd) return 0;
2108 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2110 if (msg == WM_CREATE)
2112 if (!LISTBOX_Create( wnd, NULL )) return -1;
2113 TRACE(listbox, "creating wnd=%04x descr=%p\n",
2114 hwnd, *(LB_DESCR **)wnd->wExtra );
2115 return 0;
2117 /* Ignore all other messages before we get a WM_CREATE */
2118 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2121 TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n",
2122 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2123 switch(msg)
2125 case LB_RESETCONTENT16:
2126 case LB_RESETCONTENT32:
2127 LISTBOX_ResetContent( wnd, descr );
2128 return 0;
2130 case LB_ADDSTRING16:
2131 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2132 /* fall through */
2133 case LB_ADDSTRING32:
2134 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2135 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2137 case LB_INSERTSTRING16:
2138 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2139 wParam = (INT32)(INT16)wParam;
2140 /* fall through */
2141 case LB_INSERTSTRING32:
2142 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2144 case LB_ADDFILE16:
2145 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2146 /* fall through */
2147 case LB_ADDFILE32:
2148 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2149 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2151 case LB_DELETESTRING16:
2152 case LB_DELETESTRING32:
2153 return LISTBOX_RemoveItem( wnd, descr, wParam );
2155 case LB_GETITEMDATA16:
2156 case LB_GETITEMDATA32:
2157 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2158 return LB_ERR;
2159 return descr->items[wParam].data;
2161 case LB_SETITEMDATA16:
2162 case LB_SETITEMDATA32:
2163 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2164 return LB_ERR;
2165 descr->items[wParam].data = (DWORD)lParam;
2166 return LB_OKAY;
2168 case LB_GETCOUNT16:
2169 case LB_GETCOUNT32:
2170 return descr->nb_items;
2172 case LB_GETTEXT16:
2173 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2174 /* fall through */
2175 case LB_GETTEXT32:
2176 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2178 case LB_GETTEXTLEN16:
2179 /* fall through */
2180 case LB_GETTEXTLEN32:
2181 if (wParam >= descr->nb_items) return LB_ERR;
2182 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2183 : sizeof(DWORD));
2185 case LB_GETCURSEL16:
2186 case LB_GETCURSEL32:
2187 return descr->selected_item;
2189 case LB_GETTOPINDEX16:
2190 case LB_GETTOPINDEX32:
2191 return descr->top_item;
2193 case LB_GETITEMHEIGHT16:
2194 case LB_GETITEMHEIGHT32:
2195 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2197 case LB_SETITEMHEIGHT16:
2198 lParam = LOWORD(lParam);
2199 /* fall through */
2200 case LB_SETITEMHEIGHT32:
2201 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2203 case LB_ITEMFROMPOINT32:
2205 POINT32 pt = { LOWORD(lParam), HIWORD(lParam) };
2206 RECT32 rect = { 0, 0, descr->width, descr->height };
2207 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2208 PtInRect32( &rect, pt ) );
2211 case LB_SETCARETINDEX16:
2212 case LB_SETCARETINDEX32:
2213 return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam );
2215 case LB_GETCARETINDEX16:
2216 case LB_GETCARETINDEX32:
2217 return descr->focus_item;
2219 case LB_SETTOPINDEX16:
2220 case LB_SETTOPINDEX32:
2221 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2223 case LB_SETCOLUMNWIDTH16:
2224 case LB_SETCOLUMNWIDTH32:
2225 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2227 case LB_GETITEMRECT16:
2229 RECT32 rect;
2230 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2231 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2233 return ret;
2235 case LB_GETITEMRECT32:
2236 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam );
2238 case LB_FINDSTRING16:
2239 wParam = (INT32)(INT16)wParam;
2240 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2241 /* fall through */
2242 case LB_FINDSTRING32:
2243 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2245 case LB_FINDSTRINGEXACT16:
2246 wParam = (INT32)(INT16)wParam;
2247 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2248 /* fall through */
2249 case LB_FINDSTRINGEXACT32:
2250 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2252 case LB_SELECTSTRING16:
2253 wParam = (INT32)(INT16)wParam;
2254 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2255 /* fall through */
2256 case LB_SELECTSTRING32:
2258 INT32 index = LISTBOX_FindString( wnd, descr, wParam,
2259 (LPCSTR)lParam, FALSE );
2260 if (index == LB_ERR) return LB_ERR;
2261 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2262 return index;
2265 case LB_GETSEL16:
2266 wParam = (INT32)(INT16)wParam;
2267 /* fall through */
2268 case LB_GETSEL32:
2269 if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items))
2270 return LB_ERR;
2271 return descr->items[wParam].selected;
2273 case LB_SETSEL16:
2274 lParam = (INT32)(INT16)lParam;
2275 /* fall through */
2276 case LB_SETSEL32:
2277 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2279 case LB_SETCURSEL16:
2280 wParam = (INT32)(INT16)wParam;
2281 /* fall through */
2282 case LB_SETCURSEL32:
2283 if (wParam != -1) LISTBOX_MakeItemVisible( wnd, descr, wParam, TRUE );
2284 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, (descr->lphc != NULL) );
2286 case LB_GETSELCOUNT16:
2287 case LB_GETSELCOUNT32:
2288 return LISTBOX_GetSelCount( wnd, descr );
2290 case LB_GETSELITEMS16:
2291 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2292 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2294 case LB_GETSELITEMS32:
2295 return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam );
2297 case LB_SELITEMRANGE16:
2298 case LB_SELITEMRANGE32:
2299 if (LOWORD(lParam) <= HIWORD(lParam))
2300 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2301 HIWORD(lParam), wParam );
2302 else
2303 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2304 LOWORD(lParam), wParam );
2306 case LB_SELITEMRANGEEX16:
2307 case LB_SELITEMRANGEEX32:
2308 if ((INT32)lParam >= (INT32)wParam)
2309 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2310 else
2311 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2313 case LB_GETHORIZONTALEXTENT16:
2314 case LB_GETHORIZONTALEXTENT32:
2315 return descr->horz_extent;
2317 case LB_SETHORIZONTALEXTENT16:
2318 case LB_SETHORIZONTALEXTENT32:
2319 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2321 case LB_GETANCHORINDEX16:
2322 case LB_GETANCHORINDEX32:
2323 return descr->anchor_item;
2325 case LB_SETANCHORINDEX16:
2326 wParam = (INT32)(INT16)wParam;
2327 /* fall through */
2328 case LB_SETANCHORINDEX32:
2329 if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items))
2330 return LB_ERR;
2331 descr->anchor_item = (INT32)wParam;
2332 return LB_OKAY;
2334 case LB_DIR16:
2335 return LISTBOX_Directory( wnd, descr, wParam,
2336 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2338 case LB_DIR32:
2339 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2341 case LB_GETLOCALE32:
2342 return descr->locale;
2344 case LB_SETLOCALE32:
2345 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2346 return LB_OKAY;
2348 case LB_INITSTORAGE32:
2349 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2351 case LB_SETCOUNT32:
2352 return LISTBOX_SetCount( wnd, descr, (INT32)wParam );
2354 case LB_SETTABSTOPS16:
2355 return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam,
2356 (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE );
2358 case LB_SETTABSTOPS32:
2359 return LISTBOX_SetTabStops( wnd, descr, wParam,
2360 (LPINT32)lParam, FALSE );
2362 case LB_CARETON16:
2363 case LB_CARETON32:
2364 if (descr->caret_on) return LB_OKAY;
2365 descr->caret_on = TRUE;
2366 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2367 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2368 return LB_OKAY;
2370 case LB_CARETOFF16:
2371 case LB_CARETOFF32:
2372 if (!descr->caret_on) return LB_OKAY;
2373 descr->caret_on = FALSE;
2374 if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf))
2375 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2376 return LB_OKAY;
2378 case WM_DESTROY:
2379 return LISTBOX_Destroy( wnd, descr );
2381 case WM_ENABLE:
2382 InvalidateRect32( hwnd, NULL, TRUE );
2383 return 0;
2385 case WM_SETREDRAW:
2386 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2387 return 0;
2389 case WM_GETDLGCODE:
2390 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2392 case WM_PAINT:
2394 PAINTSTRUCT32 ps;
2395 HDC32 hdc = ( wParam ) ? ((HDC32)wParam)
2396 : BeginPaint32( hwnd, &ps );
2397 ret = LISTBOX_Paint( wnd, descr, hdc );
2398 if( !wParam ) EndPaint32( hwnd, &ps );
2400 return ret;
2402 case WM_SIZE:
2403 LISTBOX_UpdateSize( wnd, descr );
2404 return 0;
2406 case WM_GETFONT:
2407 return descr->font;
2409 case WM_SETFONT:
2410 LISTBOX_SetFont( wnd, descr, (HFONT32)wParam );
2411 if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE );
2412 return 0;
2414 case WM_SETFOCUS:
2415 descr->caret_on = TRUE;
2416 if (descr->focus_item != -1)
2417 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2418 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2419 return 0;
2421 case WM_KILLFOCUS:
2422 if ((descr->focus_item != -1) && descr->caret_on)
2423 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2424 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2425 return 0;
2427 case WM_HSCROLL:
2428 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2430 case WM_VSCROLL:
2431 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2433 case WM_LBUTTONDOWN:
2434 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2435 (INT16)LOWORD(lParam),
2436 (INT16)HIWORD(lParam) );
2438 case WM_LBUTTONDBLCLK:
2439 if (descr->style & LBS_NOTIFY)
2440 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2441 return 0;
2443 case WM_MOUSEMOVE:
2444 if (GetCapture32() == hwnd)
2445 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2446 (INT16)HIWORD(lParam) );
2447 return 0;
2449 case WM_LBUTTONUP:
2450 return LISTBOX_HandleLButtonUp( wnd, descr );
2452 case WM_KEYDOWN:
2453 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2455 case WM_CHAR:
2456 return LISTBOX_HandleChar( wnd, descr, wParam );
2458 case WM_SYSTIMER:
2459 return LISTBOX_HandleSystemTimer( wnd, descr );
2461 case WM_ERASEBKGND:
2462 if (IS_OWNERDRAW(descr))
2464 RECT32 rect = { 0, 0, descr->width, descr->height };
2465 HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX,
2466 wParam, (LPARAM)wnd->hwndSelf );
2467 if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush );
2469 return 1;
2471 case WM_DROPFILES:
2472 if( !descr->lphc )
2473 return SendMessage32A( descr->owner, msg, wParam, lParam );
2474 break;
2476 case WM_DROPOBJECT:
2477 case WM_QUERYDROPOBJECT:
2478 case WM_DRAGSELECT:
2479 case WM_DRAGMOVE:
2480 if( !descr->lphc )
2482 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2483 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2484 dragInfo->pt.y );
2485 return SendMessage32A( descr->owner, msg, wParam, lParam );
2487 break;
2489 case WM_NCCREATE:
2490 if (TWEAK_WineLook > WIN31_LOOK)
2491 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2492 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2494 default:
2495 if ((msg >= WM_USER) && (msg < 0xc000))
2496 WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2497 hwnd, msg, wParam, lParam );
2498 return DefWindowProc32A( hwnd, msg, wParam, lParam );
2500 return 0;
2503 /***********************************************************************
2504 * COMBO_Directory
2506 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT32 attrib, LPSTR dir, BOOL32 bLong)
2508 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2510 if( wnd )
2512 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2513 if( descr )
2515 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2517 RedrawWindow32( lphc->self->hwndSelf, NULL, 0,
2518 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2519 return lRet;
2522 return CB_ERR;
2525 /***********************************************************************
2526 * ComboLBWndProc
2528 * NOTE: in Windows, winproc address of the ComboLBox is the same
2529 * as that of the Listbox.
2531 LRESULT WINAPI ComboLBWndProc( HWND32 hwnd, UINT32 msg,
2532 WPARAM32 wParam, LPARAM lParam )
2534 LRESULT lRet = 0;
2535 WND *wnd = WIN_FindWndPtr( hwnd );
2537 if (wnd)
2539 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2541 TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n",
2542 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2544 if( descr || msg == WM_CREATE )
2546 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2548 switch( msg )
2550 case WM_CREATE:
2551 #define lpcs ((LPCREATESTRUCT32A)lParam)
2552 TRACE(combo, "\tpassed parent handle = 0x%08x\n",
2553 (UINT32)lpcs->lpCreateParams);
2555 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2556 #undef lpcs
2557 return LISTBOX_Create( wnd, lphc );
2559 case WM_LBUTTONDOWN:
2560 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2561 (INT16)LOWORD(lParam), (INT16)HIWORD(lParam));
2563 /* avoid activation at all costs */
2565 case WM_MOUSEACTIVATE:
2566 return MA_NOACTIVATE;
2568 case WM_NCACTIVATE:
2569 return FALSE;
2571 case WM_KEYDOWN:
2572 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2574 /* for some reason(?) Windows makes it possible to
2575 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2577 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2578 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2579 && (wParam == VK_DOWN || wParam == VK_UP)) )
2581 COMBO_FlipListbox( lphc, FALSE );
2582 return 0;
2585 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2587 case WM_NCDESTROY:
2588 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2589 lphc->hWndLBox = 0;
2590 /* fall through */
2592 default:
2593 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2596 lRet = DefWindowProc32A( hwnd, msg, wParam, lParam );
2598 TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg );
2601 return lRet;