
Sun Jun 28 18:37:02 1998 Alexandre Julliard <julliard@lrc.epfl.ch> * [if1632/signal.c] [miscemu/instr.c] [memory/virtual.c] Moved page-fault handling to INSTR_EmulateInstruction. * [scheduler/thread.c] Added locking and check for own thread in Suspend/ResumeThread. Sat Jun 27 21:25:21 1998 Ulrich Weigand <weigand@informatik.uni-erlangen.de> * [objects/dib.c] [objects/bitmap.c] [objects/oembitmap.c] [graphics/x11drv/bitblt.c] [include/bitmap.h] Improved DIB section handling using page fault handlers. (Note: This patch includes code contributed by Matthew J. Francis.) * [memory/virtual.c] [if1632/signal.c] [include/global.h] Page Fault handler support added. * [if1632/signal.c] [loader/signal.c] [tools/build.c] [misc/system.c] [misc/winsock_dns.c] [include/sig_context.h] [include/thread.h] 16-bit %fs handling improved: Always preserve 16-bit %fs value, always restore 32-bit %fs value for signal handlers. * [if1632/thunk.c] [loader/module.c] [misc/callback.c] [windows/user.c] [loader/ne/resource.c] [include/callback.h] [include/module.h] [if1632/kernel.spec] [if1632/wprocs.spec] Resource Handler function pointer stored as 16-bit SEGPTR. * [loader/task.c] [windows/win.c] [windows/winpos.c] [if1632/user.spec] [if1632/kernel.spec] [loader/ne/module.c] Some minor incompatibilities fixed (Win32s relies on those): GetExePtr, IsWindow16 should set ES on return; WINPOS_SendNCCalcSize should cope with having the WINDOWPOS structure trashed; the OFSTRUCT in the NE module image should be placed *last*. * [include/windows.h] Missing prototype for FlushViewOfFile. * [loader/task.c] Bugfix: Command line should *not* start with a blank. * [loader/ne/segment.c] Bugfix: Fixups to offset 0 were never applied. * [misc/lstr.c] Use debugstr_a in OutputDebugString16. * [msdos/dpmi.c] Stub for int 31 BL=2f AX=7a20 (NetWare: Get VLM Call Address) added. * [msdos/int21.c] Stub for int 21 AX=440d CL=6f (get drive map information) added. Fri Jun 26 18:08:30 1998 Rein Klazes <rklazes@casema.net> * [windows/winpos.c] Fix small buglet that mixed up maximized and minimized windows. * [include/x11drv.h] [objects/dc.c] [graphics/x11drv/pen.c] [graphics/x11drv/graphics.c] Fix some bugs with lines joining styles. Draws rectangles with thick pens now correctly. Fri Jun 26 16:22:23 1998 James Juran <jrj120@psu.edu> * [misc/shell.c] Fixed bug I introduced last release in InternalExtractIcon. * [win32/file.c] Added documentation for CreateFile32A. * [documentation/wine.man] Updated manpage. * [ChangeLog] Added my entry from last release. Fri Jun 26 13:33:30 1998 Huw D M Davies <daviesh@abacus.physics.ox.ac.uk> * [graphics/psdrv/*] [if1632/wineps.spec] [include/psdrv.h] [include/print.h] [objects/gdiobj.c] First stages of an internal Postscript driver. See graphics/psdrv/README . Should print text (badly) from win3.1 notepad, write and winword6. * [documentation/printing] Some notes on printing. * [controls/edit.c] Strip off WS_BORDER in WM_NCREATE, edit draws its own rectangle. EC_USEFONTINFO seems to be used as a left/right value for EM_SETMARGINS and not as an action as the docs say. This actually makes more sense. Scroll the caret back to zero after a WM_SETTEXT. Fri Jun 26 10:56:25 1998 Marcus Meissner <marcus@jet.franken.de> * [if1632/snoop.c] Added win16 inter-dll snooping. * [win32/ordinals.c] KERNEL_485 is GetProcessDword. * [include/xmalloc.h][include/bitmap.h][misc/xmalloc.c] Added xcalloc so we 0 initialize XImages. Fixes/Hides the 'junk around MOPYFish'. * [misc/ntdll.c] Some stubs added. Thu Jun 25 15:22:43 1998 Adrian Harvey <adrian@select.com.au> * [scheduler/thread.c] Implemented SuspendThread and ResumeThread. Thu Jun 25 00:55:03 1998 Peter Hunnisett <hunnise@nortel.ca> * [include/debug.h,dplay.h,dsound.h][multimedia/dsound.c,dplay.c] [relay32/dplayx.spec,dplay.spec][multimedia/Makefile.in] [documentation/status/directplay] Added preliminary support for DirectPlay & DirectPlayLobby. Moved the preliminary stubs put in the dsound files into two new files dplay.h and dplay.c. Added new debug channel (dplay) for this. Created new document to keep track of implementation. * [include/winioctl.h][win32/device.c] Added some framework in DeviceIoControl to, in the future, support the "builtin" windows dwIoControlCodes. Added new header file winioctl.h . * [multimedia/mmsystem.c] Added slightly improved debugging information for PlaySound. Wed Jun 24 12:00:00 1998 Juergen Schmied <juergen.schmied@metronet.de> * [files/profile.c][graphics/x11drv/xfont.c][loader/module.c] Changed lstrcmpi32A to strcasecmp, lstrncmpi32A to strncasecmp, lstrcpy32A to strcpy, lstrlen32A to strlen, lstrcmp32A to strcmp because it's not necessary to support locale on such places. It causes a huge overhead and even fails sometimes * [include/oleauto.h][include/winerror.h] Added some ole-related constants. * [misc/shell.c] SHELL32_DllGetClassObject, SHGetSpecialFolderLocation, SHGetPathFromIDList improved the stubs * [ole/folders.c] IShellFolder* functions rewrote the stubs so don't crash and give something sensible back, started implementation of. * [ole/typelib.c][relay32/oleaut32.spec] LoadTypeLib32, RegisterTypeLib stub. * [ole/ole2nls.c] Fixed a buffer overrun in CompareString32A. Test for a bad pointer in LCMapString32A (happens in winhlp32 while building a index for searching). * [relay32/oleaut32.spec] [ole/typelib.c] Added stub for LoadTypeLib (ole32) to make excel95 happy. Tue Jun 23 22:47:09 1998 Alex Priem <alexp@sci.kun.nl> * [files/profile.c] [relay32/kernel32.spec] Added WritePrivateProfileStructA, GetPrivateProfileStructA, GetPrivateProfileSectionNames16. Tue Jun 23 01:34:43 1998 Pascal Cuoq <pcuoq@ens-lyon.fr> * [ole/ole2nls.c] GetStringTypeEx32A: Implemented CT_CTYPE2 and CT_CTYPE3 cases. LCMapString32A: Map final '\0' for '\0'-terminated strings. * [misc/shellord.c] [files/profile.c] [graphics/driver.c] [loader/module.c] [msdos/int21.c] [windows/driver.c] [files/drive.c] Changed lstrcmpi32A -> strcasecmp. Should be OK in these places. Sat Jun 20 23:40:00 1998 Bertho Stultiens <bertho@akhphd.au.dk> * [tools/wrc/] Wrc version 1.0.2 (20-Jun-1998). Please revert to the file tools/wrc/CHANGES for details. Sat Jun 20 14:58:00 1998 Marcel Baur <mbaur@g26.ethz.ch> * [ole/ole2nls.c] [ole/nls/*] Added the first 57 nls files, most are not yet complete. Wed Jun 17 11:16:54 1998 David Luyer <luyer@ucs.uwa.edu.au> * [relay32/relay386.c] [if1632/relay.c] Move debug_relay_(include|exclude)_list handling into seperate function RELAY_ShowDebugmsgsRelay(). Include checking of this for 16 bit calls (originally only 32-bit calls). * [relay32/snoop.c] [misc/main.c] Add debug_snoop_(include|exclude)_list as per the relay stuff. Fix typo and add information on -debugmsg +/-relay=... in help on -debugmsg. Refer to availability of snoop too. Tue Jun 10 22:00:18 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de> * [controls/header.c][include/header.h][include/commctrl.h] Added owner draw support. * [windows/nonclient.c][windows/sysmetics.c] Fixed menu bar height for Win95 look. Split NC_AdjustRect95() into NC_AdjustRectOuter95() and NC_AdjustRectInner95 to fix a menu bar bug. Improved Win95 look. * [controls/progress.c] Improved drawing code. Borders will be drawn by non-client code. * [controls/updown.c] Changed memory allocation and fixed some bugs. * [controls/toolbar.c] Fixed TB_BUTTONSTRUCTSIZE bug in MFC programs. Several improvements. * [misc/shell.c] Added stub for BrowseForFoldersA(). * [misc/shellord.c] Added stub for SHELL32_147(). * [controls/comctl32undoc.c] Minor changes. * [documentation/common_controls] New File: Documentation about development status, undocumented features and functions of the common controls.
2603 lines
82 KiB
C
2603 lines
82 KiB
C
/*
|
|
* 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))
|
|
{
|
|
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))
|
|
{
|
|
lstrcpy32A( buffer, descr->items[index].str );
|
|
return strlen(buffer);
|
|
}
|
|
else
|
|
{
|
|
memcpy( buffer, &descr->items[index].data, sizeof(DWORD) );
|
|
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, FALSE );
|
|
|
|
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_Win95Look)
|
|
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;
|
|
}
|
|
|