| /* | |
| * Listbox controls | |
| * | |
| * Copyright 1996 Alexandre Julliard | |
| */ | |
| #include <string.h> | |
| #include "windows.h" | |
| #include "winerror.h" | |
| #include "drive.h" | |
| #include "heap.h" | |
| #include "spy.h" | |
| #include "win.h" | |
| #include "combo.h" | |
| #include "debug.h" | |
| #include "tweak.h" | |
| /* Unimplemented yet: | |
| * - LBS_NOSEL | |
| * - LBS_USETABSTOPS | |
| * - Unicode | |
| * - Locale handling | |
| */ | |
| /* Items array granularity */ | |
| #define LB_ARRAY_GRANULARITY 16 | |
| /* Scrolling timeout in ms */ | |
| #define LB_SCROLL_TIMEOUT 50 | |
| /* Listbox system timer id */ | |
| #define LB_TIMER_ID 2 | |
| /* Item structure */ | |
| typedef struct | |
| { | |
| LPSTR str; /* Item text */ | |
| BOOL32 selected; /* Is item selected? */ | |
| UINT32 height; /* Item height (only for OWNERDRAWVARIABLE) */ | |
| DWORD data; /* User data */ | |
| } LB_ITEMDATA; | |
| /* Listbox structure */ | |
| typedef struct | |
| { | |
| HANDLE32 heap; /* Heap for this listbox */ | |
| HWND32 owner; /* Owner window to send notifications to */ | |
| UINT32 style; /* Window style */ | |
| INT32 width; /* Window width */ | |
| INT32 height; /* Window height */ | |
| LB_ITEMDATA *items; /* Array of items */ | |
| INT32 nb_items; /* Number of items */ | |
| INT32 top_item; /* Top visible item */ | |
| INT32 selected_item; /* Selected item */ | |
| INT32 focus_item; /* Item that has the focus */ | |
| INT32 anchor_item; /* Anchor item for extended selection */ | |
| INT32 item_height; /* Default item height */ | |
| INT32 page_size; /* Items per listbox page */ | |
| INT32 column_width; /* Column width for multi-column listboxes */ | |
| INT32 horz_extent; /* Horizontal extent (0 if no hscroll) */ | |
| INT32 horz_pos; /* Horizontal position */ | |
| INT32 nb_tabs; /* Number of tabs in array */ | |
| INT32 *tabs; /* Array of tabs */ | |
| BOOL32 caret_on; /* Is caret on? */ | |
| HFONT32 font; /* Current font */ | |
| LCID locale; /* Current locale for string comparisons */ | |
| LPHEADCOMBO lphc; /* ComboLBox */ | |
| } LB_DESCR; | |
| #define IS_OWNERDRAW(descr) \ | |
| ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) | |
| #define HAS_STRINGS(descr) \ | |
| (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS)) | |
| #define SEND_NOTIFICATION(wnd,descr,code) \ | |
| (SendMessage32A( (descr)->owner, WM_COMMAND, \ | |
| MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf )) | |
| /* Current timer status */ | |
| typedef enum | |
| { | |
| LB_TIMER_NONE, | |
| LB_TIMER_UP, | |
| LB_TIMER_LEFT, | |
| LB_TIMER_DOWN, | |
| LB_TIMER_RIGHT | |
| } TIMER_DIRECTION; | |
| static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE; | |
| /*********************************************************************** | |
| * LISTBOX_Dump | |
| */ | |
| void LISTBOX_Dump( WND *wnd ) | |
| { | |
| INT32 i; | |
| LB_ITEMDATA *item; | |
| LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra; | |
| DUMP( "Listbox:\n" ); | |
| DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n", | |
| wnd->hwndSelf, (UINT32)descr, descr->heap, descr->nb_items, | |
| descr->top_item ); | |
| for (i = 0, item = descr->items; i < descr->nb_items; i++, item++) | |
| { | |
| DUMP( "%4d: %-40s %d %08lx %3d\n", | |
| i, item->str, item->selected, item->data, item->height ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetCurrentPageSize | |
| * | |
| * Return the current page size | |
| */ | |
| static INT32 LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr ) | |
| { | |
| INT32 i, height; | |
| if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size; | |
| for (i = descr->top_item, height = 0; i < descr->nb_items; i++) | |
| { | |
| if ((height += descr->items[i].height) > descr->height) break; | |
| } | |
| if (i == descr->top_item) return 1; | |
| else return i - descr->top_item; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetMaxTopIndex | |
| * | |
| * Return the maximum possible index for the top of the listbox. | |
| */ | |
| static INT32 LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr ) | |
| { | |
| INT32 max, page; | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| page = descr->height; | |
| for (max = descr->nb_items - 1; max >= 0; max--) | |
| if ((page -= descr->items[max].height) < 0) break; | |
| if (max < descr->nb_items - 1) max++; | |
| } | |
| else if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| if ((page = descr->width / descr->column_width) < 1) page = 1; | |
| max = (descr->nb_items + descr->page_size - 1) / descr->page_size; | |
| max = (max - page) * descr->page_size; | |
| } | |
| else | |
| { | |
| max = descr->nb_items - descr->page_size; | |
| } | |
| if (max < 0) max = 0; | |
| return max; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_UpdateScroll | |
| * | |
| * Update the scrollbars. Should be called whenever the content | |
| * of the listbox changes. | |
| */ | |
| static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr ) | |
| { | |
| SCROLLINFO info; | |
| if (!(descr->style & WS_VSCROLL)) return; | |
| /* It is important that we check descr->style, and not wnd->dwStyle, | |
| for WS_VSCROLL, as the former is exactly the one passed in | |
| argument to CreateWindow. | |
| In Windows (and from now on in Wine :) a listbox created | |
| with such a style (no WS_SCROLL) does not update | |
| the scrollbar with listbox-related data, thus letting | |
| the programmer use it for his/her own purposes. */ | |
| if (descr->style & LBS_NOREDRAW) return; | |
| info.cbSize = sizeof(info); | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| info.nMin = 0; | |
| info.nMax = (descr->nb_items - 1) / descr->page_size; | |
| info.nPos = descr->top_item / descr->page_size; | |
| info.nPage = descr->width / descr->column_width; | |
| if (info.nPage < 1) info.nPage = 1; | |
| info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; | |
| if (descr->style & LBS_DISABLENOSCROLL) | |
| info.fMask |= SIF_DISABLENOSCROLL; | |
| SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE ); | |
| info.nMax = 0; | |
| info.fMask = SIF_RANGE; | |
| SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE ); | |
| } | |
| else | |
| { | |
| info.nMin = 0; | |
| info.nMax = descr->nb_items - 1; | |
| info.nPos = descr->top_item; | |
| info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr ); | |
| info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; | |
| if (descr->style & LBS_DISABLENOSCROLL) | |
| info.fMask |= SIF_DISABLENOSCROLL; | |
| SetScrollInfo32( wnd->hwndSelf, SB_VERT, &info, TRUE ); | |
| if (descr->horz_extent) | |
| { | |
| info.nMin = 0; | |
| info.nMax = descr->horz_extent - 1; | |
| info.nPos = descr->horz_pos; | |
| info.nPage = descr->width; | |
| info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; | |
| if (descr->style & LBS_DISABLENOSCROLL) | |
| info.fMask |= SIF_DISABLENOSCROLL; | |
| SetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info, TRUE ); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetTopItem | |
| * | |
| * Set the top item of the listbox, scrolling up or down if necessary. | |
| */ | |
| static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT32 index, | |
| BOOL32 scroll ) | |
| { | |
| INT32 max = LISTBOX_GetMaxTopIndex( wnd, descr ); | |
| if (index > max) index = max; | |
| if (index < 0) index = 0; | |
| if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size; | |
| if (descr->top_item == index) return LB_OKAY; | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| INT32 diff = (descr->top_item - index) / descr->page_size * descr->column_width; | |
| if (scroll && (abs(diff) < descr->width)) | |
| ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL, | |
| SW_INVALIDATE | SW_ERASE ); | |
| else | |
| scroll = FALSE; | |
| } | |
| else if (scroll) | |
| { | |
| INT32 diff; | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| INT32 i; | |
| diff = 0; | |
| if (index > descr->top_item) | |
| { | |
| for (i = index - 1; i >= descr->top_item; i--) | |
| diff -= descr->items[i].height; | |
| } | |
| else | |
| { | |
| for (i = index; i < descr->top_item; i++) | |
| diff += descr->items[i].height; | |
| } | |
| } | |
| else | |
| diff = (descr->top_item - index) * descr->item_height; | |
| if (abs(diff) < descr->height) | |
| ScrollWindowEx32( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL, | |
| SW_INVALIDATE | SW_ERASE ); | |
| else | |
| scroll = FALSE; | |
| } | |
| if (!scroll) InvalidateRect32( wnd->hwndSelf, NULL, TRUE ); | |
| descr->top_item = index; | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_UpdatePage | |
| * | |
| * Update the page size. Should be called when the size of | |
| * the client area or the item height changes. | |
| */ | |
| static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr ) | |
| { | |
| INT32 page_size; | |
| if ((page_size = descr->height / descr->item_height) < 1) page_size = 1; | |
| if (page_size == descr->page_size) return; | |
| descr->page_size = page_size; | |
| if (descr->style & LBS_MULTICOLUMN) | |
| InvalidateRect32( wnd->hwndSelf, NULL, TRUE ); | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_UpdateSize | |
| * | |
| * Update the size of the listbox. Should be called when the size of | |
| * the client area changes. | |
| */ | |
| static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr ) | |
| { | |
| RECT32 rect; | |
| GetClientRect32( wnd->hwndSelf, &rect ); | |
| descr->width = rect.right - rect.left; | |
| descr->height = rect.bottom - rect.top; | |
| if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr)) | |
| { | |
| if ((descr->height > descr->item_height) && | |
| (descr->height % descr->item_height)) | |
| { | |
| TRACE(listbox, "[%04x]: changing height %d -> %d\n", | |
| wnd->hwndSelf, descr->height, | |
| descr->height - descr->height%descr->item_height ); | |
| SetWindowPos32( wnd->hwndSelf, 0, 0, 0, | |
| wnd->rectWindow.right - wnd->rectWindow.left, | |
| wnd->rectWindow.bottom - wnd->rectWindow.top - | |
| (descr->height % descr->item_height), | |
| SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE ); | |
| return; | |
| } | |
| } | |
| TRACE(listbox, "[%04x]: new size = %d,%d\n", | |
| wnd->hwndSelf, descr->width, descr->height ); | |
| LISTBOX_UpdatePage( wnd, descr ); | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetItemRect | |
| * | |
| * Get the rectangle enclosing an item, in listbox client coordinates. | |
| * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error. | |
| */ | |
| static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT32 index, | |
| RECT32 *rect ) | |
| { | |
| /* Index <= 0 is legal even on empty listboxes */ | |
| if (index && (index >= descr->nb_items)) return -1; | |
| SetRect32( rect, 0, 0, descr->width, descr->height ); | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| INT32 col = (index / descr->page_size) - | |
| (descr->top_item / descr->page_size); | |
| rect->left += col * descr->column_width; | |
| rect->right = rect->left + descr->column_width; | |
| rect->top += (index % descr->page_size) * descr->item_height; | |
| rect->bottom = rect->top + descr->item_height; | |
| } | |
| else if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| INT32 i; | |
| rect->right += descr->horz_pos; | |
| if ((index >= 0) && (index < descr->nb_items)) | |
| { | |
| if (index < descr->top_item) | |
| { | |
| for (i = descr->top_item-1; i >= index; i--) | |
| rect->top -= descr->items[i].height; | |
| } | |
| else | |
| { | |
| for (i = descr->top_item; i < index; i++) | |
| rect->top += descr->items[i].height; | |
| } | |
| rect->bottom = rect->top + descr->items[index].height; | |
| } | |
| } | |
| else | |
| { | |
| rect->top += (index - descr->top_item) * descr->item_height; | |
| rect->bottom = rect->top + descr->item_height; | |
| rect->right += descr->horz_pos; | |
| } | |
| return ((rect->left < descr->width) && (rect->right > 0) && | |
| (rect->top < descr->height) && (rect->bottom > 0)); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetItemFromPoint | |
| * | |
| * Return the item nearest from point (x,y) (in client coordinates). | |
| */ | |
| static INT32 LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr, | |
| INT32 x, INT32 y ) | |
| { | |
| INT32 index = descr->top_item; | |
| if (!descr->nb_items) return -1; /* No items */ | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| INT32 pos = 0; | |
| if (y >= 0) | |
| { | |
| while (index < descr->nb_items) | |
| { | |
| if ((pos += descr->items[index].height) > y) break; | |
| index++; | |
| } | |
| } | |
| else | |
| { | |
| while (index > 0) | |
| { | |
| index--; | |
| if ((pos -= descr->items[index].height) <= y) break; | |
| } | |
| } | |
| } | |
| else if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| if (y >= descr->item_height * descr->page_size) return -1; | |
| if (y >= 0) index += y / descr->item_height; | |
| if (x >= 0) index += (x / descr->column_width) * descr->page_size; | |
| else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size; | |
| } | |
| else | |
| { | |
| index += (y / descr->item_height); | |
| } | |
| if (index < 0) return 0; | |
| if (index >= descr->nb_items) return -1; | |
| return index; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_PaintItem | |
| * | |
| * Paint an item. | |
| */ | |
| static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC32 hdc, | |
| const RECT32 *rect, INT32 index, UINT32 action ) | |
| { | |
| LB_ITEMDATA *item = NULL; | |
| if (index < descr->nb_items) item = &descr->items[index]; | |
| if (IS_OWNERDRAW(descr)) | |
| { | |
| DRAWITEMSTRUCT32 dis; | |
| UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu; | |
| dis.CtlType = ODT_LISTBOX; | |
| dis.CtlID = id; | |
| dis.hwndItem = wnd->hwndSelf; | |
| dis.itemAction = action; | |
| dis.hDC = hdc; | |
| dis.itemID = index; | |
| dis.itemState = 0; | |
| if (item && item->selected) dis.itemState |= ODS_SELECTED; | |
| if ((descr->focus_item == index) && | |
| (descr->caret_on) && | |
| (GetFocus32() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS; | |
| if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED; | |
| dis.itemData = item ? item->data : 0; | |
| dis.rcItem = *rect; | |
| TRACE(listbox, "[%04x]: drawitem %d (%s) action=%02x " | |
| "state=%02x rect=%d,%d-%d,%d\n", | |
| wnd->hwndSelf, index, item ? item->str : "", action, | |
| dis.itemState, rect->left, rect->top, | |
| rect->right, rect->bottom ); | |
| SendMessage32A(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis); | |
| } | |
| else | |
| { | |
| COLORREF oldText = 0, oldBk = 0; | |
| if (action == ODA_FOCUS) | |
| { | |
| DrawFocusRect32( hdc, rect ); | |
| return; | |
| } | |
| if (item && item->selected) | |
| { | |
| oldBk = SetBkColor32( hdc, GetSysColor32( COLOR_HIGHLIGHT ) ); | |
| oldText = SetTextColor32( hdc, GetSysColor32(COLOR_HIGHLIGHTTEXT)); | |
| } | |
| TRACE(listbox, "[%04x]: painting %d (%s) action=%02x " | |
| "rect=%d,%d-%d,%d\n", | |
| wnd->hwndSelf, index, item ? item->str : "", action, | |
| rect->left, rect->top, rect->right, rect->bottom ); | |
| if (!item) | |
| ExtTextOut32A( hdc, rect->left + 1, rect->top + 1, | |
| ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); | |
| else if (!(descr->style & LBS_USETABSTOPS)) | |
| ExtTextOut32A( hdc, rect->left + 1, rect->top + 1, | |
| ETO_OPAQUE | ETO_CLIPPED, rect, item->str, | |
| strlen(item->str), NULL ); | |
| else | |
| { | |
| /* Output empty string to paint background in the full width. */ | |
| ExtTextOut32A( hdc, rect->left + 1, rect->top + 1, | |
| ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); | |
| TabbedTextOut32A( hdc, rect->left + 1 , rect->top + 1, | |
| item->str, strlen(item->str), | |
| descr->nb_tabs, descr->tabs, 0); | |
| } | |
| if (item && item->selected) | |
| { | |
| SetBkColor32( hdc, oldBk ); | |
| SetTextColor32( hdc, oldText ); | |
| } | |
| if ((descr->focus_item == index) && | |
| (descr->caret_on) && | |
| (GetFocus32() == wnd->hwndSelf)) DrawFocusRect32( hdc, rect ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetRedraw | |
| * | |
| * Change the redraw flag. | |
| */ | |
| static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL32 on ) | |
| { | |
| if (on) | |
| { | |
| if (!(descr->style & LBS_NOREDRAW)) return; | |
| descr->style &= ~LBS_NOREDRAW; | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| } | |
| else descr->style |= LBS_NOREDRAW; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_RepaintItem | |
| * | |
| * Repaint a single item synchronously. | |
| */ | |
| static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT32 index, | |
| UINT32 action ) | |
| { | |
| HDC32 hdc; | |
| RECT32 rect; | |
| HFONT32 oldFont = 0; | |
| HBRUSH32 hbrush, oldBrush = 0; | |
| if (descr->style & LBS_NOREDRAW) return; | |
| if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return; | |
| if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE ))) return; | |
| if (descr->font) oldFont = SelectObject32( hdc, descr->font ); | |
| hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX, | |
| hdc, (LPARAM)wnd->hwndSelf ); | |
| if (hbrush) oldBrush = SelectObject32( hdc, hbrush ); | |
| if (wnd->dwStyle & WS_DISABLED) | |
| SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) ); | |
| SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL ); | |
| LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action ); | |
| if (oldFont) SelectObject32( hdc, oldFont ); | |
| if (oldBrush) SelectObject32( hdc, oldBrush ); | |
| ReleaseDC32( wnd->hwndSelf, hdc ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_InitStorage | |
| */ | |
| static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT32 nb_items, | |
| DWORD bytes ) | |
| { | |
| LB_ITEMDATA *item; | |
| nb_items += LB_ARRAY_GRANULARITY - 1; | |
| nb_items -= (nb_items % LB_ARRAY_GRANULARITY); | |
| if (descr->items) | |
| nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item); | |
| if (!(item = HeapReAlloc( descr->heap, 0, descr->items, | |
| nb_items * sizeof(LB_ITEMDATA) ))) | |
| { | |
| SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE ); | |
| return LB_ERRSPACE; | |
| } | |
| descr->items = item; | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetTabStops | |
| */ | |
| static BOOL32 LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT32 count, | |
| LPINT32 tabs, BOOL32 short_ints ) | |
| { | |
| if (!(descr->style & LBS_USETABSTOPS)) return TRUE; | |
| if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs ); | |
| if (!(descr->nb_tabs = count)) | |
| { | |
| descr->tabs = NULL; | |
| return TRUE; | |
| } | |
| /* FIXME: count = 1 */ | |
| if (!(descr->tabs = (INT32 *)HeapAlloc( descr->heap, 0, | |
| descr->nb_tabs * sizeof(INT32) ))) | |
| return FALSE; | |
| if (short_ints) | |
| { | |
| INT32 i; | |
| LPINT16 p = (LPINT16)tabs; | |
| dbg_decl_str(listbox, 256); | |
| for (i = 0; i < descr->nb_tabs; i++) { | |
| descr->tabs[i] = *p++<<1; /* FIXME */ | |
| if(TRACE_ON(listbox)) | |
| dsprintf(listbox, "%hd ", descr->tabs[i]); | |
| } | |
| TRACE(listbox, "[%04x]: settabstops %s\n", | |
| wnd->hwndSelf, dbg_str(listbox)); | |
| } | |
| else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT32) ); | |
| /* FIXME: repaint the window? */ | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetText | |
| */ | |
| static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT32 index, | |
| LPSTR buffer ) | |
| { | |
| if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; | |
| if (HAS_STRINGS(descr)) | |
| { | |
| if (!buffer) | |
| return strlen(descr->items[index].str); | |
| lstrcpy32A( buffer, descr->items[index].str ); | |
| return strlen(buffer); | |
| } else { | |
| if (buffer) | |
| *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data); | |
| return sizeof(DWORD); | |
| } | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_FindStringPos | |
| * | |
| * Find the nearest string located before a given string in sort order. | |
| * If 'exact' is TRUE, return an error if we don't get an exact match. | |
| */ | |
| static INT32 LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str, | |
| BOOL32 exact ) | |
| { | |
| INT32 index, min, max, res = -1; | |
| if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */ | |
| min = 0; | |
| max = descr->nb_items; | |
| while (min != max) | |
| { | |
| index = (min + max) / 2; | |
| if (HAS_STRINGS(descr)) | |
| res = lstrcmpi32A( descr->items[index].str, str ); | |
| else | |
| { | |
| COMPAREITEMSTRUCT32 cis; | |
| UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu; | |
| cis.CtlType = ODT_LISTBOX; | |
| cis.CtlID = id; | |
| cis.hwndItem = wnd->hwndSelf; | |
| cis.itemID1 = index; | |
| cis.itemData1 = descr->items[index].data; | |
| cis.itemID2 = -1; | |
| cis.itemData2 = (DWORD)str; | |
| cis.dwLocaleId = descr->locale; | |
| res = SendMessage32A( descr->owner, WM_COMPAREITEM, | |
| id, (LPARAM)&cis ); | |
| } | |
| if (!res) return index; | |
| if (res > 0) max = index; | |
| else min = index + 1; | |
| } | |
| return exact ? -1 : max; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_FindFileStrPos | |
| * | |
| * Find the nearest string located before a given string in directory | |
| * sort order (i.e. first files, then directories, then drives). | |
| */ | |
| static INT32 LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str ) | |
| { | |
| INT32 min, max, res = -1; | |
| if (!HAS_STRINGS(descr)) | |
| return LISTBOX_FindStringPos( wnd, descr, str, FALSE ); | |
| min = 0; | |
| max = descr->nb_items; | |
| while (min != max) | |
| { | |
| INT32 index = (min + max) / 2; | |
| const char *p = descr->items[index].str; | |
| if (*p == '[') /* drive or directory */ | |
| { | |
| if (*str != '[') res = -1; | |
| else if (p[1] == '-') /* drive */ | |
| { | |
| if (str[1] == '-') res = str[2] - p[2]; | |
| else res = -1; | |
| } | |
| else /* directory */ | |
| { | |
| if (str[1] == '-') res = 1; | |
| else res = lstrcmpi32A( str, p ); | |
| } | |
| } | |
| else /* filename */ | |
| { | |
| if (*str == '[') res = 1; | |
| else res = lstrcmpi32A( str, p ); | |
| } | |
| if (!res) return index; | |
| if (res < 0) max = index; | |
| else min = index + 1; | |
| } | |
| return max; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_FindString | |
| * | |
| * Find the item beginning with a given string. | |
| */ | |
| static INT32 LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT32 start, | |
| LPCSTR str, BOOL32 exact ) | |
| { | |
| INT32 i; | |
| LB_ITEMDATA *item; | |
| if (start >= descr->nb_items) start = -1; | |
| item = descr->items + start + 1; | |
| if (HAS_STRINGS(descr)) | |
| { | |
| if (!str) return LB_ERR; | |
| if (exact) | |
| { | |
| for (i = start + 1; i < descr->nb_items; i++, item++) | |
| if (!lstrcmpi32A( str, item->str )) return i; | |
| for (i = 0, item = descr->items; i <= start; i++, item++) | |
| if (!lstrcmpi32A( str, item->str )) return i; | |
| } | |
| else | |
| { | |
| /* Special case for drives and directories: ignore prefix */ | |
| #define CHECK_DRIVE(item) \ | |
| if ((item)->str[0] == '[') \ | |
| { \ | |
| if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \ | |
| if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \ | |
| return i; \ | |
| } | |
| INT32 len = strlen(str); | |
| for (i = start + 1; i < descr->nb_items; i++, item++) | |
| { | |
| if (!lstrncmpi32A( str, item->str, len )) return i; | |
| CHECK_DRIVE(item); | |
| } | |
| for (i = 0, item = descr->items; i <= start; i++, item++) | |
| { | |
| if (!lstrncmpi32A( str, item->str, len )) return i; | |
| CHECK_DRIVE(item); | |
| } | |
| #undef CHECK_DRIVE | |
| } | |
| } | |
| else | |
| { | |
| if (exact && (descr->style & LBS_SORT)) | |
| /* If sorted, use a WM_COMPAREITEM binary search */ | |
| return LISTBOX_FindStringPos( wnd, descr, str, TRUE ); | |
| /* Otherwise use a linear search */ | |
| for (i = start + 1; i < descr->nb_items; i++, item++) | |
| if (item->data == (DWORD)str) return i; | |
| for (i = 0, item = descr->items; i <= start; i++, item++) | |
| if (item->data == (DWORD)str) return i; | |
| } | |
| return LB_ERR; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetSelCount | |
| */ | |
| static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr ) | |
| { | |
| INT32 i, count; | |
| LB_ITEMDATA *item = descr->items; | |
| if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; | |
| for (i = count = 0; i < descr->nb_items; i++, item++) | |
| if (item->selected) count++; | |
| return count; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetSelItems16 | |
| */ | |
| static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max, | |
| LPINT16 array ) | |
| { | |
| INT32 i, count; | |
| LB_ITEMDATA *item = descr->items; | |
| if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; | |
| for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++) | |
| if (item->selected) array[count++] = (INT16)i; | |
| return count; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetSelItems32 | |
| */ | |
| static LRESULT LISTBOX_GetSelItems32( WND *wnd, LB_DESCR *descr, INT32 max, | |
| LPINT32 array ) | |
| { | |
| INT32 i, count; | |
| LB_ITEMDATA *item = descr->items; | |
| if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; | |
| for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++) | |
| if (item->selected) array[count++] = i; | |
| return count; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_Paint | |
| */ | |
| static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC32 hdc ) | |
| { | |
| INT32 i, col_pos = descr->page_size - 1; | |
| RECT32 rect; | |
| HFONT32 oldFont = 0; | |
| HBRUSH32 hbrush, oldBrush = 0; | |
| SetRect32( &rect, 0, 0, descr->width, descr->height ); | |
| if (descr->style & LBS_NOREDRAW) return 0; | |
| if (descr->style & LBS_MULTICOLUMN) | |
| rect.right = rect.left + descr->column_width; | |
| else if (descr->horz_pos) | |
| { | |
| SetWindowOrgEx32( hdc, descr->horz_pos, 0, NULL ); | |
| rect.right += descr->horz_pos; | |
| } | |
| if (descr->font) oldFont = SelectObject32( hdc, descr->font ); | |
| hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX, | |
| hdc, (LPARAM)wnd->hwndSelf ); | |
| if (hbrush) oldBrush = SelectObject32( hdc, hbrush ); | |
| if (wnd->dwStyle & WS_DISABLED) | |
| SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) ); | |
| if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on && | |
| (GetFocus32() == wnd->hwndSelf)) | |
| { | |
| /* Special case for empty listbox: paint focus rect */ | |
| rect.bottom = rect.top + descr->item_height; | |
| LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item, | |
| ODA_DRAWENTIRE ); | |
| rect.top = rect.bottom; | |
| } | |
| for (i = descr->top_item; i < descr->nb_items; i++) | |
| { | |
| if (!(descr->style & LBS_OWNERDRAWVARIABLE)) | |
| rect.bottom = rect.top + descr->item_height; | |
| else | |
| rect.bottom = rect.top + descr->items[i].height; | |
| LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE ); | |
| rect.top = rect.bottom; | |
| if ((descr->style & LBS_MULTICOLUMN) && !col_pos) | |
| { | |
| if (!IS_OWNERDRAW(descr)) | |
| { | |
| /* Clear the bottom of the column */ | |
| SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) ); | |
| if (rect.top < descr->height) | |
| { | |
| rect.bottom = descr->height; | |
| ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, | |
| &rect, NULL, 0, NULL ); | |
| } | |
| } | |
| /* Go to the next column */ | |
| rect.left += descr->column_width; | |
| rect.right += descr->column_width; | |
| rect.top = 0; | |
| col_pos = descr->page_size - 1; | |
| } | |
| else | |
| { | |
| col_pos--; | |
| if (rect.top >= descr->height) break; | |
| } | |
| } | |
| if (!IS_OWNERDRAW(descr)) | |
| { | |
| /* Clear the remainder of the client area */ | |
| SetBkColor32( hdc, GetSysColor32( COLOR_WINDOW ) ); | |
| if (rect.top < descr->height) | |
| { | |
| rect.bottom = descr->height; | |
| ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, | |
| &rect, NULL, 0, NULL ); | |
| } | |
| if (rect.right < descr->width) | |
| { | |
| rect.left = rect.right; | |
| rect.right = descr->width; | |
| rect.top = 0; | |
| rect.bottom = descr->height; | |
| ExtTextOut32A( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, | |
| &rect, NULL, 0, NULL ); | |
| } | |
| } | |
| if (oldFont) SelectObject32( hdc, oldFont ); | |
| if (oldBrush) SelectObject32( hdc, oldBrush ); | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_InvalidateItems | |
| * | |
| * Invalidate all items from a given item. If the specified item is not | |
| * visible, nothing happens. | |
| */ | |
| static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT32 index ) | |
| { | |
| RECT32 rect; | |
| if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1) | |
| { | |
| rect.bottom = descr->height; | |
| InvalidateRect32( wnd->hwndSelf, &rect, TRUE ); | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| /* Repaint the other columns */ | |
| rect.left = rect.right; | |
| rect.right = descr->width; | |
| rect.top = 0; | |
| InvalidateRect32( wnd->hwndSelf, &rect, TRUE ); | |
| } | |
| } | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_GetItemHeight | |
| */ | |
| static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index ) | |
| { | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; | |
| return descr->items[index].height; | |
| } | |
| else return descr->item_height; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetItemHeight | |
| */ | |
| static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT32 index, | |
| UINT32 height ) | |
| { | |
| if (!height) height = 1; | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; | |
| TRACE(listbox, "[%04x]: item %d height = %d\n", | |
| wnd->hwndSelf, index, height ); | |
| descr->items[index].height = height; | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| LISTBOX_InvalidateItems( wnd, descr, index ); | |
| } | |
| else if (height != descr->item_height) | |
| { | |
| TRACE(listbox, "[%04x]: new height = %d\n", | |
| wnd->hwndSelf, height ); | |
| descr->item_height = height; | |
| LISTBOX_UpdatePage( wnd, descr ); | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| InvalidateRect32( wnd->hwndSelf, 0, TRUE ); | |
| } | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetHorizontalPos | |
| */ | |
| static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT32 pos ) | |
| { | |
| INT32 diff; | |
| if (pos > descr->horz_extent - descr->width) | |
| pos = descr->horz_extent - descr->width; | |
| if (pos < 0) pos = 0; | |
| if (!(diff = descr->horz_pos - pos)) return; | |
| TRACE(listbox, "[%04x]: new horz pos = %d\n", | |
| wnd->hwndSelf, pos ); | |
| descr->horz_pos = pos; | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| if (abs(diff) < descr->width) | |
| ScrollWindowEx32( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL, | |
| SW_INVALIDATE | SW_ERASE ); | |
| else | |
| InvalidateRect32( wnd->hwndSelf, NULL, TRUE ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetHorizontalExtent | |
| */ | |
| static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr, | |
| UINT32 extent ) | |
| { | |
| if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN)) | |
| return LB_OKAY; | |
| if (extent <= 0) extent = 1; | |
| if (extent == descr->horz_extent) return LB_OKAY; | |
| TRACE(listbox, "[%04x]: new horz extent = %d\n", | |
| wnd->hwndSelf, extent ); | |
| descr->horz_extent = extent; | |
| if (descr->horz_pos > extent - descr->width) | |
| LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width ); | |
| else | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetColumnWidth | |
| */ | |
| static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT32 width) | |
| { | |
| width += 2; /* For left and right margin */ | |
| if (width == descr->column_width) return LB_OKAY; | |
| TRACE(listbox, "[%04x]: new column width = %d\n", | |
| wnd->hwndSelf, width ); | |
| descr->column_width = width; | |
| LISTBOX_UpdatePage( wnd, descr ); | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetFont | |
| * | |
| * Returns the item height. | |
| */ | |
| static INT32 LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT32 font ) | |
| { | |
| HDC32 hdc; | |
| HFONT32 oldFont = 0; | |
| TEXTMETRIC32A tm; | |
| descr->font = font; | |
| if (!(hdc = GetDCEx32( wnd->hwndSelf, 0, DCX_CACHE ))) | |
| { | |
| ERR(listbox, "unable to get DC.\n" ); | |
| return 16; | |
| } | |
| if (font) oldFont = SelectObject32( hdc, font ); | |
| GetTextMetrics32A( hdc, &tm ); | |
| if (oldFont) SelectObject32( hdc, oldFont ); | |
| ReleaseDC32( wnd->hwndSelf, hdc ); | |
| if (!IS_OWNERDRAW(descr)) | |
| LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight ); | |
| return tm.tmHeight ; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_MakeItemVisible | |
| * | |
| * Make sure that a given item is partially or fully visible. | |
| */ | |
| static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT32 index, | |
| BOOL32 fully ) | |
| { | |
| INT32 top; | |
| if (index <= descr->top_item) top = index; | |
| else if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| INT32 cols = descr->width; | |
| if (!fully) cols += descr->column_width - 1; | |
| if (cols >= descr->column_width) cols /= descr->column_width; | |
| else cols = 1; | |
| if (index < descr->top_item + (descr->page_size * cols)) return; | |
| top = index - descr->page_size * (cols - 1); | |
| } | |
| else if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| INT32 height = fully ? descr->items[index].height : 1; | |
| for (top = index; top > descr->top_item; top--) | |
| if ((height += descr->items[top-1].height) > descr->height) break; | |
| } | |
| else | |
| { | |
| if (index < descr->top_item + descr->page_size) return; | |
| if (!fully && (index == descr->top_item + descr->page_size) && | |
| (descr->height > (descr->page_size * descr->item_height))) return; | |
| top = index - descr->page_size + 1; | |
| } | |
| LISTBOX_SetTopItem( wnd, descr, top, TRUE ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SelectItemRange | |
| * | |
| * Select a range of items. Should only be used on a MULTIPLESEL listbox. | |
| */ | |
| static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT32 first, | |
| INT32 last, BOOL32 on ) | |
| { | |
| INT32 i; | |
| /* A few sanity checks */ | |
| if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY; | |
| if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; | |
| if (last == -1) last = descr->nb_items - 1; | |
| if ((first < 0) || (first >= descr->nb_items)) return LB_ERR; | |
| if ((last < 0) || (last >= descr->nb_items)) return LB_ERR; | |
| /* selected_item reflects last selected/unselected item on multiple sel */ | |
| descr->selected_item = last; | |
| if (on) /* Turn selection on */ | |
| { | |
| for (i = first; i <= last; i++) | |
| { | |
| if (descr->items[i].selected) continue; | |
| descr->items[i].selected = TRUE; | |
| LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT ); | |
| } | |
| } | |
| else /* Turn selection off */ | |
| { | |
| for (i = first; i <= last; i++) | |
| { | |
| if (!descr->items[i].selected) continue; | |
| descr->items[i].selected = FALSE; | |
| LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT ); | |
| } | |
| } | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetCaretIndex | |
| */ | |
| static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT32 index, | |
| BOOL32 fully_visible ) | |
| { | |
| INT32 oldfocus = descr->focus_item; | |
| if ((index < -1) || (index >= descr->nb_items)) return LB_ERR; | |
| if (index == oldfocus) return LB_OKAY; | |
| descr->focus_item = index; | |
| if ((oldfocus != -1) && descr->caret_on && (GetFocus32() == wnd->hwndSelf)) | |
| LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS ); | |
| if (index != -1) | |
| { | |
| LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible ); | |
| if (descr->caret_on && (GetFocus32() == wnd->hwndSelf)) | |
| LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS ); | |
| } | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetSelection | |
| */ | |
| static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT32 index, | |
| BOOL32 on, BOOL32 send_notify ) | |
| { | |
| if ((index < -1) || (index >= descr->nb_items)) return LB_ERR; | |
| if (descr->style & LBS_MULTIPLESEL) | |
| { | |
| if (index == -1) /* Select all items */ | |
| return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on ); | |
| else /* Only one item */ | |
| return LISTBOX_SelectItemRange( wnd, descr, index, index, on ); | |
| } | |
| else | |
| { | |
| INT32 oldsel = descr->selected_item; | |
| if (index == oldsel) return LB_OKAY; | |
| if (oldsel != -1) descr->items[oldsel].selected = FALSE; | |
| if (index != -1) descr->items[index].selected = TRUE; | |
| descr->selected_item = index; | |
| if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT); | |
| if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT ); | |
| if (send_notify) SEND_NOTIFICATION( wnd, descr, | |
| (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL ); | |
| } | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_MoveCaret | |
| * | |
| * Change the caret position and extend the selection to the new caret. | |
| */ | |
| static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT32 index, | |
| BOOL32 fully_visible ) | |
| { | |
| LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible ); | |
| if (descr->style & LBS_EXTENDEDSEL) | |
| { | |
| if (descr->anchor_item != -1) | |
| { | |
| INT32 first = MIN( descr->focus_item, descr->anchor_item ); | |
| INT32 last = MAX( descr->focus_item, descr->anchor_item ); | |
| if (first > 0) | |
| LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE ); | |
| LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE ); | |
| LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE ); | |
| } | |
| } | |
| else if (!(descr->style & LBS_MULTIPLESEL) && (descr->selected_item != -1)) | |
| { | |
| /* Set selection to new caret item */ | |
| LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_InsertItem | |
| */ | |
| static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT32 index, | |
| LPSTR str, DWORD data ) | |
| { | |
| LB_ITEMDATA *item; | |
| INT32 max_items; | |
| if (index == -1) index = descr->nb_items; | |
| else if ((index < 0) || (index > descr->nb_items)) return LB_ERR; | |
| if (!descr->items) max_items = 0; | |
| else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item); | |
| if (descr->nb_items == max_items) | |
| { | |
| /* We need to grow the array */ | |
| max_items += LB_ARRAY_GRANULARITY; | |
| if (!(item = HeapReAlloc( descr->heap, 0, descr->items, | |
| max_items * sizeof(LB_ITEMDATA) ))) | |
| { | |
| SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE ); | |
| return LB_ERRSPACE; | |
| } | |
| descr->items = item; | |
| } | |
| /* Insert the item structure */ | |
| item = &descr->items[index]; | |
| if (index < descr->nb_items) | |
| RtlMoveMemory( item + 1, item, | |
| (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); | |
| item->str = str; | |
| item->data = data; | |
| item->height = 0; | |
| item->selected = FALSE; | |
| descr->nb_items++; | |
| /* Get item height */ | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) | |
| { | |
| MEASUREITEMSTRUCT32 mis; | |
| UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu; | |
| mis.CtlType = ODT_LISTBOX; | |
| mis.CtlID = id; | |
| mis.itemID = index; | |
| mis.itemData = descr->items[index].data; | |
| mis.itemHeight = descr->item_height; | |
| SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); | |
| item->height = mis.itemHeight ? mis.itemHeight : 1; | |
| TRACE(listbox, "[%04x]: measure item %d (%s) = %d\n", | |
| wnd->hwndSelf, index, str ? str : "", item->height ); | |
| } | |
| /* Repaint the items */ | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| LISTBOX_InvalidateItems( wnd, descr, index ); | |
| /* Move selection and focused item */ | |
| if (index <= descr->selected_item) descr->selected_item++; | |
| if (index <= descr->focus_item) | |
| { | |
| descr->focus_item++; | |
| LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE ); | |
| } | |
| /* If listbox was empty, set focus to the first item */ | |
| if (descr->nb_items == 1) LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE ); | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_InsertString | |
| */ | |
| static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT32 index, | |
| LPCSTR str ) | |
| { | |
| LPSTR new_str = NULL; | |
| DWORD data = 0; | |
| LRESULT ret; | |
| if (HAS_STRINGS(descr)) | |
| { | |
| if (!(new_str = HEAP_strdupA( descr->heap, 0, str ))) | |
| { | |
| SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE ); | |
| return LB_ERRSPACE; | |
| } | |
| } | |
| else data = (DWORD)str; | |
| if (index == -1) index = descr->nb_items; | |
| if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0) | |
| { | |
| if (new_str) HeapFree( descr->heap, 0, new_str ); | |
| return ret; | |
| } | |
| TRACE(listbox, "[%04x]: added item %d '%s'\n", | |
| wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" ); | |
| return index; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_DeleteItem | |
| * | |
| * Delete the content of an item. 'index' must be a valid index. | |
| */ | |
| static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT32 index ) | |
| { | |
| /* Note: Win 3.1 only sends DELETEITEM on owner-draw items, | |
| * while Win95 sends it for all items with user data. | |
| * It's probably better to send it too often than not | |
| * often enough, so this is what we do here. | |
| */ | |
| if (IS_OWNERDRAW(descr) || descr->items[index].data) | |
| { | |
| DELETEITEMSTRUCT32 dis; | |
| UINT32 id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu; | |
| dis.CtlType = ODT_LISTBOX; | |
| dis.CtlID = id; | |
| dis.itemID = index; | |
| dis.hwndItem = wnd->hwndSelf; | |
| dis.itemData = descr->items[index].data; | |
| SendMessage32A( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis ); | |
| } | |
| if (HAS_STRINGS(descr) && descr->items[index].str) | |
| HeapFree( descr->heap, 0, descr->items[index].str ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_RemoveItem | |
| * | |
| * Remove an item from the listbox and delete its content. | |
| */ | |
| static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT32 index ) | |
| { | |
| LB_ITEMDATA *item; | |
| INT32 max_items; | |
| if (index == -1) index = descr->nb_items - 1; | |
| else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; | |
| LISTBOX_DeleteItem( wnd, descr, index ); | |
| /* Remove the item */ | |
| item = &descr->items[index]; | |
| if (index < descr->nb_items-1) | |
| RtlMoveMemory( item, item + 1, | |
| (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) ); | |
| descr->nb_items--; | |
| if (descr->anchor_item == descr->nb_items) descr->anchor_item--; | |
| /* Shrink the item array if possible */ | |
| max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA); | |
| if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) | |
| { | |
| max_items -= LB_ARRAY_GRANULARITY; | |
| item = HeapReAlloc( descr->heap, 0, descr->items, | |
| max_items * sizeof(LB_ITEMDATA) ); | |
| if (item) descr->items = item; | |
| } | |
| /* Repaint the items */ | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| LISTBOX_InvalidateItems( wnd, descr, index ); | |
| /* Move selection and focused item */ | |
| if (index <= descr->selected_item) descr->selected_item--; | |
| if (index <= descr->focus_item) | |
| { | |
| descr->focus_item--; | |
| LISTBOX_MoveCaret( wnd, descr, descr->focus_item, FALSE ); | |
| } | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_ResetContent | |
| */ | |
| static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr ) | |
| { | |
| INT32 i; | |
| for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i ); | |
| if (descr->items) HeapFree( descr->heap, 0, descr->items ); | |
| descr->nb_items = 0; | |
| descr->top_item = 0; | |
| descr->selected_item = -1; | |
| descr->focus_item = 0; | |
| descr->anchor_item = -1; | |
| descr->items = NULL; | |
| LISTBOX_UpdateScroll( wnd, descr ); | |
| InvalidateRect32( wnd->hwndSelf, NULL, TRUE ); | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_SetCount | |
| */ | |
| static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT32 count ) | |
| { | |
| LRESULT ret; | |
| if (HAS_STRINGS(descr)) return LB_ERR; | |
| /* FIXME: this is far from optimal... */ | |
| if (count > descr->nb_items) | |
| { | |
| while (count > descr->nb_items) | |
| if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0) | |
| return ret; | |
| } | |
| else if (count < descr->nb_items) | |
| { | |
| while (count < descr->nb_items) | |
| if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0) | |
| return ret; | |
| } | |
| return LB_OKAY; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_Directory | |
| */ | |
| static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT32 attrib, | |
| LPCSTR filespec, BOOL32 long_names ) | |
| { | |
| HANDLE32 handle; | |
| LRESULT ret = LB_OKAY; | |
| WIN32_FIND_DATA32A entry; | |
| int pos; | |
| if ((handle = FindFirstFile32A(filespec,&entry)) == INVALID_HANDLE_VALUE32) | |
| { | |
| if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR; | |
| } | |
| else | |
| { | |
| do | |
| { | |
| char buffer[270]; | |
| if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | |
| { | |
| if (!(attrib & DDL_DIRECTORY) || | |
| !strcmp( entry.cAlternateFileName, "." )) continue; | |
| if (long_names) sprintf( buffer, "[%s]", entry.cFileName ); | |
| else sprintf( buffer, "[%s]", entry.cAlternateFileName ); | |
| } | |
| else /* not a directory */ | |
| { | |
| #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \ | |
| FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE) | |
| if ((attrib & DDL_EXCLUSIVE) && | |
| ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS))) | |
| continue; | |
| #undef ATTRIBS | |
| if (long_names) strcpy( buffer, entry.cFileName ); | |
| else strcpy( buffer, entry.cAlternateFileName ); | |
| } | |
| if (!long_names) CharLower32A( buffer ); | |
| pos = LISTBOX_FindFileStrPos( wnd, descr, buffer ); | |
| if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0) | |
| break; | |
| } while (FindNextFile32A( handle, &entry )); | |
| FindClose32( handle ); | |
| } | |
| if ((ret >= 0) && (attrib & DDL_DRIVES)) | |
| { | |
| char buffer[] = "[-a-]"; | |
| int drive; | |
| for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++) | |
| { | |
| if (!DRIVE_IsValid(drive)) continue; | |
| if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0) | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleVScroll | |
| */ | |
| static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr, | |
| WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| SCROLLINFO info; | |
| if (descr->style & LBS_MULTICOLUMN) return 0; | |
| switch(LOWORD(wParam)) | |
| { | |
| case SB_LINEUP: | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE ); | |
| break; | |
| case SB_LINEDOWN: | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE ); | |
| break; | |
| case SB_PAGEUP: | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item - | |
| LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE ); | |
| break; | |
| case SB_PAGEDOWN: | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item + | |
| LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE ); | |
| break; | |
| case SB_THUMBPOSITION: | |
| LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE ); | |
| break; | |
| case SB_THUMBTRACK: | |
| info.cbSize = sizeof(info); | |
| info.fMask = SIF_TRACKPOS; | |
| GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info ); | |
| LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE ); | |
| break; | |
| case SB_TOP: | |
| LISTBOX_SetTopItem( wnd, descr, 0, TRUE ); | |
| break; | |
| case SB_BOTTOM: | |
| LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE ); | |
| break; | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleHScroll | |
| */ | |
| static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr, | |
| WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| SCROLLINFO info; | |
| INT32 page; | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| switch(LOWORD(wParam)) | |
| { | |
| case SB_LINELEFT: | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size, | |
| TRUE ); | |
| break; | |
| case SB_LINERIGHT: | |
| LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size, | |
| TRUE ); | |
| break; | |
| case SB_PAGELEFT: | |
| page = descr->width / descr->column_width; | |
| if (page < 1) page = 1; | |
| LISTBOX_SetTopItem( wnd, descr, | |
| descr->top_item - page * descr->page_size, TRUE ); | |
| break; | |
| case SB_PAGERIGHT: | |
| page = descr->width / descr->column_width; | |
| if (page < 1) page = 1; | |
| LISTBOX_SetTopItem( wnd, descr, | |
| descr->top_item + page * descr->page_size, TRUE ); | |
| break; | |
| case SB_THUMBPOSITION: | |
| LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size, | |
| TRUE ); | |
| break; | |
| case SB_THUMBTRACK: | |
| info.cbSize = sizeof(info); | |
| info.fMask = SIF_TRACKPOS; | |
| GetScrollInfo32( wnd->hwndSelf, SB_VERT, &info ); | |
| LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size, | |
| TRUE ); | |
| break; | |
| case SB_LEFT: | |
| LISTBOX_SetTopItem( wnd, descr, 0, TRUE ); | |
| break; | |
| case SB_RIGHT: | |
| LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE ); | |
| break; | |
| } | |
| } | |
| else if (descr->horz_extent) | |
| { | |
| switch(LOWORD(wParam)) | |
| { | |
| case SB_LINELEFT: | |
| LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 ); | |
| break; | |
| case SB_LINERIGHT: | |
| LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 ); | |
| break; | |
| case SB_PAGELEFT: | |
| LISTBOX_SetHorizontalPos( wnd, descr, | |
| descr->horz_pos - descr->width ); | |
| break; | |
| case SB_PAGERIGHT: | |
| LISTBOX_SetHorizontalPos( wnd, descr, | |
| descr->horz_pos + descr->width ); | |
| break; | |
| case SB_THUMBPOSITION: | |
| LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) ); | |
| break; | |
| case SB_THUMBTRACK: | |
| info.cbSize = sizeof(info); | |
| info.fMask = SIF_TRACKPOS; | |
| GetScrollInfo32( wnd->hwndSelf, SB_HORZ, &info ); | |
| LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos ); | |
| break; | |
| case SB_LEFT: | |
| LISTBOX_SetHorizontalPos( wnd, descr, 0 ); | |
| break; | |
| case SB_RIGHT: | |
| LISTBOX_SetHorizontalPos( wnd, descr, | |
| descr->horz_extent - descr->width ); | |
| break; | |
| } | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleLButtonDown | |
| */ | |
| static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr, | |
| WPARAM32 wParam, INT32 x, INT32 y ) | |
| { | |
| INT32 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y ); | |
| TRACE(listbox, "[%04x]: lbuttondown %d,%d item %d\n", | |
| wnd->hwndSelf, x, y, index ); | |
| if (!descr->caret_on && (GetFocus32() == wnd->hwndSelf)) return 0; | |
| if (index != -1) | |
| { | |
| if (descr->style & LBS_EXTENDEDSEL) | |
| { | |
| if (!(wParam & MK_SHIFT)) descr->anchor_item = index; | |
| if (wParam & MK_CONTROL) | |
| { | |
| LISTBOX_SetCaretIndex( wnd, descr, index, FALSE ); | |
| LISTBOX_SetSelection( wnd, descr, index, | |
| !descr->items[index].selected, FALSE ); | |
| } | |
| else LISTBOX_MoveCaret( wnd, descr, index, FALSE ); | |
| } | |
| else | |
| { | |
| LISTBOX_MoveCaret( wnd, descr, index, FALSE ); | |
| LISTBOX_SetSelection( wnd, descr, index, | |
| (!(descr->style & LBS_MULTIPLESEL) || | |
| !descr->items[index].selected), FALSE ); | |
| } | |
| } | |
| if( !descr->lphc ) SetFocus32( wnd->hwndSelf ); | |
| else SetFocus32( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit | |
| : descr->lphc->self->hwndSelf ) ; | |
| SetCapture32( wnd->hwndSelf ); | |
| if (index != -1 && !descr->lphc) | |
| { | |
| if (descr->style & LBS_NOTIFY ) | |
| SendMessage32A( descr->owner, WM_LBTRACKPOINT, index, | |
| MAKELPARAM( x, y ) ); | |
| if (wnd->dwExStyle & WS_EX_DRAGDETECT) | |
| { | |
| POINT32 pt = { x, y }; | |
| if (DragDetect32( wnd->hwndSelf, pt )) | |
| SendMessage32A( descr->owner, WM_BEGINDRAG, 0, 0 ); | |
| } | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleLButtonUp | |
| */ | |
| static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr ) | |
| { | |
| if (LISTBOX_Timer != LB_TIMER_NONE) | |
| KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID ); | |
| LISTBOX_Timer = LB_TIMER_NONE; | |
| if (GetCapture32() == wnd->hwndSelf) | |
| { | |
| ReleaseCapture(); | |
| if (descr->style & LBS_NOTIFY) | |
| SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE ); | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleTimer | |
| * | |
| * Handle scrolling upon a timer event. | |
| * Return TRUE if scrolling should continue. | |
| */ | |
| static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr, | |
| INT32 index, TIMER_DIRECTION dir ) | |
| { | |
| switch(dir) | |
| { | |
| case LB_TIMER_UP: | |
| if (descr->top_item) index = descr->top_item - 1; | |
| else index = 0; | |
| break; | |
| case LB_TIMER_LEFT: | |
| if (descr->top_item) index -= descr->page_size; | |
| break; | |
| case LB_TIMER_DOWN: | |
| index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr ); | |
| if (index == descr->focus_item) index++; | |
| if (index >= descr->nb_items) index = descr->nb_items - 1; | |
| break; | |
| case LB_TIMER_RIGHT: | |
| if (index + descr->page_size < descr->nb_items) | |
| index += descr->page_size; | |
| break; | |
| case LB_TIMER_NONE: | |
| break; | |
| } | |
| if (index == descr->focus_item) return FALSE; | |
| LISTBOX_MoveCaret( wnd, descr, index, FALSE ); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleSystemTimer | |
| * | |
| * WM_SYSTIMER handler. | |
| */ | |
| static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr ) | |
| { | |
| if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer )) | |
| { | |
| KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID ); | |
| LISTBOX_Timer = LB_TIMER_NONE; | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleMouseMove | |
| * | |
| * WM_MOUSEMOVE handler. | |
| */ | |
| static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr, | |
| INT32 x, INT32 y ) | |
| { | |
| INT32 index; | |
| TIMER_DIRECTION dir; | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| if (y < 0) y = 0; | |
| else if (y >= descr->item_height * descr->page_size) | |
| y = descr->item_height * descr->page_size - 1; | |
| if (x < 0) | |
| { | |
| dir = LB_TIMER_LEFT; | |
| x = 0; | |
| } | |
| else if (x >= descr->width) | |
| { | |
| dir = LB_TIMER_RIGHT; | |
| x = descr->width - 1; | |
| } | |
| else dir = LB_TIMER_NONE; /* inside */ | |
| } | |
| else | |
| { | |
| if (y < 0) dir = LB_TIMER_UP; /* above */ | |
| else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */ | |
| else dir = LB_TIMER_NONE; /* inside */ | |
| } | |
| index = LISTBOX_GetItemFromPoint( wnd, descr, x, y ); | |
| if (index == -1) index = descr->focus_item; | |
| if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE; | |
| /* Start/stop the system timer */ | |
| if (dir != LB_TIMER_NONE) | |
| SetSystemTimer32( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL); | |
| else if (LISTBOX_Timer != LB_TIMER_NONE) | |
| KillSystemTimer32( wnd->hwndSelf, LB_TIMER_ID ); | |
| LISTBOX_Timer = dir; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleKeyDown | |
| */ | |
| static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM32 wParam ) | |
| { | |
| INT32 caret = -1; | |
| if (descr->style & LBS_WANTKEYBOARDINPUT) | |
| { | |
| caret = SendMessage32A( descr->owner, WM_VKEYTOITEM, | |
| MAKEWPARAM(LOWORD(wParam), descr->focus_item), | |
| wnd->hwndSelf ); | |
| if (caret == -2) return 0; | |
| } | |
| if (caret == -1) switch(wParam) | |
| { | |
| case VK_LEFT: | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| if (descr->focus_item >= descr->page_size) | |
| caret = descr->focus_item - descr->page_size; | |
| break; | |
| } | |
| /* fall through */ | |
| case VK_UP: | |
| caret = descr->focus_item - 1; | |
| if (caret < 0) caret = 0; | |
| break; | |
| case VK_RIGHT: | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| if (descr->focus_item + descr->page_size < descr->nb_items) | |
| caret = descr->focus_item + descr->page_size; | |
| break; | |
| } | |
| /* fall through */ | |
| case VK_DOWN: | |
| caret = descr->focus_item + 1; | |
| if (caret >= descr->nb_items) caret = descr->nb_items - 1; | |
| break; | |
| case VK_PRIOR: | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| INT32 page = descr->width / descr->column_width; | |
| if (page < 1) page = 1; | |
| caret = descr->focus_item - (page * descr->page_size) + 1; | |
| } | |
| else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1; | |
| if (caret < 0) caret = 0; | |
| break; | |
| case VK_NEXT: | |
| if (descr->style & LBS_MULTICOLUMN) | |
| { | |
| INT32 page = descr->width / descr->column_width; | |
| if (page < 1) page = 1; | |
| caret = descr->focus_item + (page * descr->page_size) - 1; | |
| } | |
| else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1; | |
| if (caret >= descr->nb_items) caret = descr->nb_items - 1; | |
| break; | |
| case VK_HOME: | |
| caret = 0; | |
| break; | |
| case VK_END: | |
| caret = descr->nb_items - 1; | |
| break; | |
| case VK_SPACE: | |
| if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item; | |
| else if (descr->style & LBS_MULTIPLESEL) | |
| { | |
| LISTBOX_SetSelection( wnd, descr, descr->focus_item, | |
| !descr->items[descr->focus_item].selected, | |
| (descr->style & LBS_NOTIFY) != 0 ); | |
| } | |
| else if (descr->selected_item == -1) | |
| { | |
| LISTBOX_SetSelection( wnd, descr, descr->focus_item, TRUE, | |
| (descr->style & LBS_NOTIFY) != 0 ); | |
| } | |
| break; | |
| } | |
| if (caret >= 0) | |
| { | |
| if ((descr->style & LBS_EXTENDEDSEL) && | |
| !(GetKeyState32( VK_SHIFT ) & 0x8000)) | |
| descr->anchor_item = caret; | |
| LISTBOX_MoveCaret( wnd, descr, caret, TRUE ); | |
| if (descr->style & LBS_NOTIFY) | |
| { | |
| if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE ) | |
| { | |
| /* make sure that combo parent doesn't hide us */ | |
| descr->lphc->wState |= CBF_NOROLLUP; | |
| } | |
| SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE ); | |
| } | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_HandleChar | |
| */ | |
| static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr, | |
| WPARAM32 wParam ) | |
| { | |
| INT32 caret = -1; | |
| char str[2] = { wParam & 0xff, '\0' }; | |
| if (descr->style & LBS_WANTKEYBOARDINPUT) | |
| { | |
| caret = SendMessage32A( descr->owner, WM_CHARTOITEM, | |
| MAKEWPARAM(LOWORD(wParam), descr->focus_item), | |
| wnd->hwndSelf ); | |
| if (caret == -2) return 0; | |
| } | |
| if (caret == -1) | |
| caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE); | |
| if (caret != -1) | |
| { | |
| LISTBOX_MoveCaret( wnd, descr, caret, TRUE ); | |
| if (descr->style & LBS_NOTIFY) | |
| SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE ); | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_Create | |
| */ | |
| static BOOL32 LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc ) | |
| { | |
| LB_DESCR *descr; | |
| MEASUREITEMSTRUCT32 mis; | |
| RECT32 rect; | |
| if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) ))) | |
| return FALSE; | |
| if (!(descr->heap = HeapCreate( 0, 0x10000, 0 ))) | |
| { | |
| HeapFree( GetProcessHeap(), 0, descr ); | |
| return FALSE; | |
| } | |
| GetClientRect32( wnd->hwndSelf, &rect ); | |
| descr->owner = GetParent32( wnd->hwndSelf ); | |
| descr->style = wnd->dwStyle; | |
| descr->width = rect.right - rect.left; | |
| descr->height = rect.bottom - rect.top; | |
| descr->items = NULL; | |
| descr->nb_items = 0; | |
| descr->top_item = 0; | |
| descr->selected_item = -1; | |
| descr->focus_item = 0; | |
| descr->anchor_item = -1; | |
| descr->item_height = 1; | |
| descr->page_size = 1; | |
| descr->column_width = 150; | |
| descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0; | |
| descr->horz_pos = 0; | |
| descr->nb_tabs = 0; | |
| descr->tabs = NULL; | |
| descr->caret_on = TRUE; | |
| descr->font = 0; | |
| descr->locale = 0; /* FIXME */ | |
| descr->lphc = lphc; | |
| if( lphc ) | |
| { | |
| TRACE(combo,"[%04x]: resetting owner %04x -> %04x\n", | |
| wnd->hwndSelf, descr->owner, lphc->self->hwndSelf ); | |
| descr->owner = lphc->self->hwndSelf; | |
| } | |
| *(LB_DESCR **)wnd->wExtra = descr; | |
| /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY; | |
| */ | |
| if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL; | |
| if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE; | |
| if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT; | |
| descr->item_height = LISTBOX_SetFont( wnd, descr, 0 ); | |
| if (descr->style & LBS_OWNERDRAWFIXED) | |
| { | |
| if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN)) | |
| { | |
| /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */ | |
| descr->item_height = lphc->RectButton.bottom - lphc->RectButton.top - 6; | |
| } | |
| else | |
| { | |
| UINT32 id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu; | |
| mis.CtlType = ODT_LISTBOX; | |
| mis.CtlID = id; | |
| mis.itemID = -1; | |
| mis.itemWidth = 0; | |
| mis.itemData = 0; | |
| mis.itemHeight = descr->item_height; | |
| SendMessage32A( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); | |
| descr->item_height = mis.itemHeight ? mis.itemHeight : 1; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * LISTBOX_Destroy | |
| */ | |
| static BOOL32 LISTBOX_Destroy( WND *wnd, LB_DESCR *descr ) | |
| { | |
| LISTBOX_ResetContent( wnd, descr ); | |
| HeapDestroy( descr->heap ); | |
| HeapFree( GetProcessHeap(), 0, descr ); | |
| wnd->wExtra[0] = 0; | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * ListBoxWndProc | |
| */ | |
| LRESULT WINAPI ListBoxWndProc( HWND32 hwnd, UINT32 msg, | |
| WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| LRESULT ret; | |
| LB_DESCR *descr; | |
| WND *wnd = WIN_FindWndPtr( hwnd ); | |
| if (!wnd) return 0; | |
| if (!(descr = *(LB_DESCR **)wnd->wExtra)) | |
| { | |
| if (msg == WM_CREATE) | |
| { | |
| if (!LISTBOX_Create( wnd, NULL )) return -1; | |
| TRACE(listbox, "creating wnd=%04x descr=%p\n", | |
| hwnd, *(LB_DESCR **)wnd->wExtra ); | |
| return 0; | |
| } | |
| /* Ignore all other messages before we get a WM_CREATE */ | |
| return DefWindowProc32A( hwnd, msg, wParam, lParam ); | |
| } | |
| TRACE(listbox, "[%04x]: msg %s wp %08x lp %08lx\n", | |
| wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam ); | |
| switch(msg) | |
| { | |
| case LB_RESETCONTENT16: | |
| case LB_RESETCONTENT32: | |
| LISTBOX_ResetContent( wnd, descr ); | |
| return 0; | |
| case LB_ADDSTRING16: | |
| if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case LB_ADDSTRING32: | |
| wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE ); | |
| return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam ); | |
| case LB_INSERTSTRING16: | |
| if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| wParam = (INT32)(INT16)wParam; | |
| /* fall through */ | |
| case LB_INSERTSTRING32: | |
| return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam ); | |
| case LB_ADDFILE16: | |
| if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case LB_ADDFILE32: | |
| wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam ); | |
| return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam ); | |
| case LB_DELETESTRING16: | |
| case LB_DELETESTRING32: | |
| return LISTBOX_RemoveItem( wnd, descr, wParam ); | |
| case LB_GETITEMDATA16: | |
| case LB_GETITEMDATA32: | |
| if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items)) | |
| return LB_ERR; | |
| return descr->items[wParam].data; | |
| case LB_SETITEMDATA16: | |
| case LB_SETITEMDATA32: | |
| if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items)) | |
| return LB_ERR; | |
| descr->items[wParam].data = (DWORD)lParam; | |
| return LB_OKAY; | |
| case LB_GETCOUNT16: | |
| case LB_GETCOUNT32: | |
| return descr->nb_items; | |
| case LB_GETTEXT16: | |
| lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case LB_GETTEXT32: | |
| return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam ); | |
| case LB_GETTEXTLEN16: | |
| /* fall through */ | |
| case LB_GETTEXTLEN32: | |
| if (wParam >= descr->nb_items) return LB_ERR; | |
| return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str) | |
| : sizeof(DWORD)); | |
| case LB_GETCURSEL16: | |
| case LB_GETCURSEL32: | |
| return descr->selected_item; | |
| case LB_GETTOPINDEX16: | |
| case LB_GETTOPINDEX32: | |
| return descr->top_item; | |
| case LB_GETITEMHEIGHT16: | |
| case LB_GETITEMHEIGHT32: | |
| return LISTBOX_GetItemHeight( wnd, descr, wParam ); | |
| case LB_SETITEMHEIGHT16: | |
| lParam = LOWORD(lParam); | |
| /* fall through */ | |
| case LB_SETITEMHEIGHT32: | |
| return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam ); | |
| case LB_ITEMFROMPOINT32: | |
| { | |
| POINT32 pt = { LOWORD(lParam), HIWORD(lParam) }; | |
| RECT32 rect = { 0, 0, descr->width, descr->height }; | |
| return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y), | |
| PtInRect32( &rect, pt ) ); | |
| } | |
| case LB_SETCARETINDEX16: | |
| case LB_SETCARETINDEX32: | |
| return LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ); | |
| case LB_GETCARETINDEX16: | |
| case LB_GETCARETINDEX32: | |
| return descr->focus_item; | |
| case LB_SETTOPINDEX16: | |
| case LB_SETTOPINDEX32: | |
| return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE ); | |
| case LB_SETCOLUMNWIDTH16: | |
| case LB_SETCOLUMNWIDTH32: | |
| return LISTBOX_SetColumnWidth( wnd, descr, wParam ); | |
| case LB_GETITEMRECT16: | |
| { | |
| RECT32 rect; | |
| ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect ); | |
| CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) ); | |
| } | |
| return ret; | |
| case LB_GETITEMRECT32: | |
| return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT32 *)lParam ); | |
| case LB_FINDSTRING16: | |
| wParam = (INT32)(INT16)wParam; | |
| if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case LB_FINDSTRING32: | |
| return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE ); | |
| case LB_FINDSTRINGEXACT16: | |
| wParam = (INT32)(INT16)wParam; | |
| if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case LB_FINDSTRINGEXACT32: | |
| return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE ); | |
| case LB_SELECTSTRING16: | |
| wParam = (INT32)(INT16)wParam; | |
| if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam); | |
| /* fall through */ | |
| case LB_SELECTSTRING32: | |
| { | |
| INT32 index = LISTBOX_FindString( wnd, descr, wParam, | |
| (LPCSTR)lParam, FALSE ); | |
| if (index == LB_ERR) return LB_ERR; | |
| LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE ); | |
| return index; | |
| } | |
| case LB_GETSEL16: | |
| wParam = (INT32)(INT16)wParam; | |
| /* fall through */ | |
| case LB_GETSEL32: | |
| if (((INT32)wParam < 0) || ((INT32)wParam >= descr->nb_items)) | |
| return LB_ERR; | |
| return descr->items[wParam].selected; | |
| case LB_SETSEL16: | |
| lParam = (INT32)(INT16)lParam; | |
| /* fall through */ | |
| case LB_SETSEL32: | |
| return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE ); | |
| case LB_SETCURSEL16: | |
| wParam = (INT32)(INT16)wParam; | |
| /* fall through */ | |
| case LB_SETCURSEL32: | |
| if (wParam != -1) LISTBOX_MakeItemVisible( wnd, descr, wParam, TRUE ); | |
| return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, (descr->lphc != NULL) ); | |
| case LB_GETSELCOUNT16: | |
| case LB_GETSELCOUNT32: | |
| return LISTBOX_GetSelCount( wnd, descr ); | |
| case LB_GETSELITEMS16: | |
| return LISTBOX_GetSelItems16( wnd, descr, wParam, | |
| (LPINT16)PTR_SEG_TO_LIN(lParam) ); | |
| case LB_GETSELITEMS32: | |
| return LISTBOX_GetSelItems32( wnd, descr, wParam, (LPINT32)lParam ); | |
| case LB_SELITEMRANGE16: | |
| case LB_SELITEMRANGE32: | |
| if (LOWORD(lParam) <= HIWORD(lParam)) | |
| return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam), | |
| HIWORD(lParam), wParam ); | |
| else | |
| return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam), | |
| LOWORD(lParam), wParam ); | |
| case LB_SELITEMRANGEEX16: | |
| case LB_SELITEMRANGEEX32: | |
| if ((INT32)lParam >= (INT32)wParam) | |
| return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE ); | |
| else | |
| return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE); | |
| case LB_GETHORIZONTALEXTENT16: | |
| case LB_GETHORIZONTALEXTENT32: | |
| return descr->horz_extent; | |
| case LB_SETHORIZONTALEXTENT16: | |
| case LB_SETHORIZONTALEXTENT32: | |
| return LISTBOX_SetHorizontalExtent( wnd, descr, wParam ); | |
| case LB_GETANCHORINDEX16: | |
| case LB_GETANCHORINDEX32: | |
| return descr->anchor_item; | |
| case LB_SETANCHORINDEX16: | |
| wParam = (INT32)(INT16)wParam; | |
| /* fall through */ | |
| case LB_SETANCHORINDEX32: | |
| if (((INT32)wParam < -1) || ((INT32)wParam >= descr->nb_items)) | |
| return LB_ERR; | |
| descr->anchor_item = (INT32)wParam; | |
| return LB_OKAY; | |
| case LB_DIR16: | |
| return LISTBOX_Directory( wnd, descr, wParam, | |
| (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE ); | |
| case LB_DIR32: | |
| return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE ); | |
| case LB_GETLOCALE32: | |
| return descr->locale; | |
| case LB_SETLOCALE32: | |
| descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */ | |
| return LB_OKAY; | |
| case LB_INITSTORAGE32: | |
| return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam ); | |
| case LB_SETCOUNT32: | |
| return LISTBOX_SetCount( wnd, descr, (INT32)wParam ); | |
| case LB_SETTABSTOPS16: | |
| return LISTBOX_SetTabStops( wnd, descr, (INT32)(INT16)wParam, | |
| (LPINT32)PTR_SEG_TO_LIN(lParam), TRUE ); | |
| case LB_SETTABSTOPS32: | |
| return LISTBOX_SetTabStops( wnd, descr, wParam, | |
| (LPINT32)lParam, FALSE ); | |
| case LB_CARETON16: | |
| case LB_CARETON32: | |
| if (descr->caret_on) return LB_OKAY; | |
| descr->caret_on = TRUE; | |
| if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf)) | |
| LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS ); | |
| return LB_OKAY; | |
| case LB_CARETOFF16: | |
| case LB_CARETOFF32: | |
| if (!descr->caret_on) return LB_OKAY; | |
| descr->caret_on = FALSE; | |
| if ((descr->focus_item != -1) && (GetFocus32() == wnd->hwndSelf)) | |
| LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS ); | |
| return LB_OKAY; | |
| case WM_DESTROY: | |
| return LISTBOX_Destroy( wnd, descr ); | |
| case WM_ENABLE: | |
| InvalidateRect32( hwnd, NULL, TRUE ); | |
| return 0; | |
| case WM_SETREDRAW: | |
| LISTBOX_SetRedraw( wnd, descr, wParam != 0 ); | |
| return 0; | |
| case WM_GETDLGCODE: | |
| return DLGC_WANTARROWS | DLGC_WANTCHARS; | |
| case WM_PAINT: | |
| { | |
| PAINTSTRUCT32 ps; | |
| HDC32 hdc = ( wParam ) ? ((HDC32)wParam) | |
| : BeginPaint32( hwnd, &ps ); | |
| ret = LISTBOX_Paint( wnd, descr, hdc ); | |
| if( !wParam ) EndPaint32( hwnd, &ps ); | |
| } | |
| return ret; | |
| case WM_SIZE: | |
| LISTBOX_UpdateSize( wnd, descr ); | |
| return 0; | |
| case WM_GETFONT: | |
| return descr->font; | |
| case WM_SETFONT: | |
| LISTBOX_SetFont( wnd, descr, (HFONT32)wParam ); | |
| if (lParam) InvalidateRect32( wnd->hwndSelf, 0, TRUE ); | |
| return 0; | |
| case WM_SETFOCUS: | |
| descr->caret_on = TRUE; | |
| if (descr->focus_item != -1) | |
| LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS ); | |
| SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS ); | |
| return 0; | |
| case WM_KILLFOCUS: | |
| if ((descr->focus_item != -1) && descr->caret_on) | |
| LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS ); | |
| SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS ); | |
| return 0; | |
| case WM_HSCROLL: | |
| return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam ); | |
| case WM_VSCROLL: | |
| return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam ); | |
| case WM_LBUTTONDOWN: | |
| return LISTBOX_HandleLButtonDown( wnd, descr, wParam, | |
| (INT16)LOWORD(lParam), | |
| (INT16)HIWORD(lParam) ); | |
| case WM_LBUTTONDBLCLK: | |
| if (descr->style & LBS_NOTIFY) | |
| SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK ); | |
| return 0; | |
| case WM_MOUSEMOVE: | |
| if (GetCapture32() == hwnd) | |
| LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam), | |
| (INT16)HIWORD(lParam) ); | |
| return 0; | |
| case WM_LBUTTONUP: | |
| return LISTBOX_HandleLButtonUp( wnd, descr ); | |
| case WM_KEYDOWN: | |
| return LISTBOX_HandleKeyDown( wnd, descr, wParam ); | |
| case WM_CHAR: | |
| return LISTBOX_HandleChar( wnd, descr, wParam ); | |
| case WM_SYSTIMER: | |
| return LISTBOX_HandleSystemTimer( wnd, descr ); | |
| case WM_ERASEBKGND: | |
| if (IS_OWNERDRAW(descr)) | |
| { | |
| RECT32 rect = { 0, 0, descr->width, descr->height }; | |
| HBRUSH32 hbrush = SendMessage32A( descr->owner, WM_CTLCOLORLISTBOX, | |
| wParam, (LPARAM)wnd->hwndSelf ); | |
| if (hbrush) FillRect32( (HDC32)wParam, &rect, hbrush ); | |
| } | |
| return 1; | |
| case WM_DROPFILES: | |
| if( !descr->lphc ) | |
| return SendMessage32A( descr->owner, msg, wParam, lParam ); | |
| break; | |
| case WM_DROPOBJECT: | |
| case WM_QUERYDROPOBJECT: | |
| case WM_DRAGSELECT: | |
| case WM_DRAGMOVE: | |
| if( !descr->lphc ) | |
| { | |
| LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam ); | |
| dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x, | |
| dragInfo->pt.y ); | |
| return SendMessage32A( descr->owner, msg, wParam, lParam ); | |
| } | |
| break; | |
| case WM_NCCREATE: | |
| if (TWEAK_WineLook > WIN31_LOOK) | |
| wnd->dwExStyle |= WS_EX_CLIENTEDGE; | |
| return DefWindowProc32A( hwnd, msg, wParam, lParam ); | |
| default: | |
| if ((msg >= WM_USER) && (msg < 0xc000)) | |
| WARN(listbox, "[%04x]: unknown msg %04x wp %08x lp %08lx\n", | |
| hwnd, msg, wParam, lParam ); | |
| return DefWindowProc32A( hwnd, msg, wParam, lParam ); | |
| } | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * COMBO_Directory | |
| */ | |
| LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT32 attrib, LPSTR dir, BOOL32 bLong) | |
| { | |
| WND *wnd = WIN_FindWndPtr( lphc->hWndLBox ); | |
| if( wnd ) | |
| { | |
| LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra; | |
| if( descr ) | |
| { | |
| LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong ); | |
| RedrawWindow32( lphc->self->hwndSelf, NULL, 0, | |
| RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW ); | |
| return lRet; | |
| } | |
| } | |
| return CB_ERR; | |
| } | |
| /*********************************************************************** | |
| * ComboLBWndProc | |
| * | |
| * NOTE: in Windows, winproc address of the ComboLBox is the same | |
| * as that of the Listbox. | |
| */ | |
| LRESULT WINAPI ComboLBWndProc( HWND32 hwnd, UINT32 msg, | |
| WPARAM32 wParam, LPARAM lParam ) | |
| { | |
| LRESULT lRet = 0; | |
| WND *wnd = WIN_FindWndPtr( hwnd ); | |
| if (wnd) | |
| { | |
| LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra; | |
| TRACE(combo, "[%04x]: msg %s wp %08x lp %08lx\n", | |
| wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam ); | |
| if( descr || msg == WM_CREATE ) | |
| { | |
| LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL; | |
| switch( msg ) | |
| { | |
| case WM_CREATE: | |
| #define lpcs ((LPCREATESTRUCT32A)lParam) | |
| TRACE(combo, "\tpassed parent handle = 0x%08x\n", | |
| (UINT32)lpcs->lpCreateParams); | |
| lphc = (LPHEADCOMBO)(lpcs->lpCreateParams); | |
| #undef lpcs | |
| return LISTBOX_Create( wnd, lphc ); | |
| case WM_LBUTTONDOWN: | |
| return LISTBOX_HandleLButtonDown( wnd, descr, wParam, | |
| (INT16)LOWORD(lParam), (INT16)HIWORD(lParam)); | |
| /* avoid activation at all costs */ | |
| case WM_MOUSEACTIVATE: | |
| return MA_NOACTIVATE; | |
| case WM_NCACTIVATE: | |
| return FALSE; | |
| case WM_KEYDOWN: | |
| if( CB_GETTYPE(lphc) != CBS_SIMPLE ) | |
| { | |
| /* for some reason(?) Windows makes it possible to | |
| * show/hide ComboLBox by sending it WM_KEYDOWNs */ | |
| if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) || | |
| ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED) | |
| && (wParam == VK_DOWN || wParam == VK_UP)) ) | |
| { | |
| COMBO_FlipListbox( lphc, FALSE ); | |
| return 0; | |
| } | |
| } | |
| return LISTBOX_HandleKeyDown( wnd, descr, wParam ); | |
| case WM_NCDESTROY: | |
| if( CB_GETTYPE(lphc) != CBS_SIMPLE ) | |
| lphc->hWndLBox = 0; | |
| /* fall through */ | |
| default: | |
| return ListBoxWndProc( hwnd, msg, wParam, lParam ); | |
| } | |
| } | |
| lRet = DefWindowProc32A( hwnd, msg, wParam, lParam ); | |
| TRACE(combo,"\t default on msg [%04x]\n", (UINT16)msg ); | |
| } | |
| return lRet; | |
| } | |