diff --git a/dll/win32/comctl32/CMakeLists.txt b/dll/win32/comctl32/CMakeLists.txt index 92850014784..4a1151bd986 100644 --- a/dll/win32/comctl32/CMakeLists.txt +++ b/dll/win32/comctl32/CMakeLists.txt @@ -13,6 +13,7 @@ spec2def(comctl32.dll comctl32.spec ADD_IMPORTLIB) list(APPEND SOURCE animate.c button.c + combo.c comboex.c comctl32undoc.c commctrl.c @@ -20,11 +21,13 @@ list(APPEND SOURCE dpa.c draglist.c dsa.c + edit.c flatsb.c header.c hotkey.c imagelist.c ipaddress.c + listbox.c listview.c monthcal.c nativefont.c @@ -33,16 +36,12 @@ list(APPEND SOURCE propsheet.c rebar.c smoothscroll.c + static.c status.c string.c syslink.c tab.c taskdialog.c - theme_button.c - theme_combo.c - theme_dialog.c - theme_edit.c - theme_listbox.c theme_scrollbar.c theming.c toolbar.c @@ -50,7 +49,7 @@ list(APPEND SOURCE trackbar.c treeview.c updown.c - comctl32.h) + precomp.h) add_library(comctl32 SHARED ${SOURCE} @@ -61,8 +60,8 @@ add_library(comctl32 SHARED set_module_type(comctl32 win32dll UNICODE) target_link_libraries(comctl32 uuid wine ${PSEH_LIB}) add_delay_importlibs(comctl32 winmm uxtheme) -add_importlibs(comctl32 user32 gdi32 advapi32 msvcrt kernel32 ntdll) -add_pch(comctl32 comctl32.h SOURCE) +add_importlibs(comctl32 user32 gdi32 advapi32 usp10 imm32 msvcrt kernel32 ntdll) +add_pch(comctl32 precomp.h SOURCE) add_cd_file(TARGET comctl32 DESTINATION reactos/system32 FOR all) add_cd_file(TARGET comctl32 DESTINATION reactos/winsxs/x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.2600.2982_none_deadbeef FOR all) add_cd_file(TARGET comctl32 DESTINATION reactos/winsxs/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.2982_none_deadbeef FOR all) diff --git a/dll/win32/comctl32/animate.c b/dll/win32/comctl32/animate.c index 594a227568d..f8ba159779a 100644 --- a/dll/win32/comctl32/animate.c +++ b/dll/win32/comctl32/animate.c @@ -20,22 +20,23 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTES - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Mar. 15, 2005, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * - check for the 'rec ' list in some AVI files */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "vfw.h" +#include "mmsystem.h" #include "comctl32.h" - -#include +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(animate); @@ -179,36 +180,39 @@ static BOOL ANIMATE_DoStop(ANIMATE_INFO *infoPtr) static void ANIMATE_Free(ANIMATE_INFO *infoPtr) { - if (infoPtr->hMMio) { - ANIMATE_DoStop(infoPtr); - mmioClose(infoPtr->hMMio, 0); - if (infoPtr->hRes) { - FreeResource(infoPtr->hRes); - infoPtr->hRes = 0; - } - Free (infoPtr->lpIndex); - infoPtr->lpIndex = NULL; - if (infoPtr->hic) { - fnIC.fnICClose(infoPtr->hic); - infoPtr->hic = 0; - } - Free (infoPtr->inbih); - infoPtr->inbih = NULL; - Free (infoPtr->outbih); - infoPtr->outbih = NULL; - Free (infoPtr->indata); - infoPtr->indata = NULL; - Free (infoPtr->outdata); - infoPtr->outdata = NULL; - if( infoPtr->hbmPrevFrame ) + if (infoPtr->hMMio) + { + ANIMATE_DoStop(infoPtr); + mmioClose(infoPtr->hMMio, 0); + if (infoPtr->hRes) { - DeleteObject(infoPtr->hbmPrevFrame); + FreeResource(infoPtr->hRes); + infoPtr->hRes = 0; + } + heap_free (infoPtr->lpIndex); + infoPtr->lpIndex = NULL; + if (infoPtr->hic) + { + fnIC.fnICClose(infoPtr->hic); + infoPtr->hic = 0; + } + heap_free (infoPtr->inbih); + infoPtr->inbih = NULL; + heap_free (infoPtr->outbih); + infoPtr->outbih = NULL; + heap_free (infoPtr->indata); + infoPtr->indata = NULL; + heap_free (infoPtr->outdata); + infoPtr->outdata = NULL; + if (infoPtr->hbmPrevFrame) + { + DeleteObject(infoPtr->hbmPrevFrame); infoPtr->hbmPrevFrame = 0; } - memset(&infoPtr->mah, 0, sizeof(infoPtr->mah)); - memset(&infoPtr->ash, 0, sizeof(infoPtr->ash)); - infoPtr->nFromFrame = infoPtr->nToFrame = infoPtr->nLoop = infoPtr->currFrame = 0; + memset(&infoPtr->mah, 0, sizeof(infoPtr->mah)); + memset(&infoPtr->ash, 0, sizeof(infoPtr->ash)); + infoPtr->nFromFrame = infoPtr->nToFrame = infoPtr->nLoop = infoPtr->currFrame = 0; } infoPtr->transparentColor = ANIMATE_COLOR_NONE; } @@ -562,7 +566,7 @@ static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr) return FALSE; } - infoPtr->inbih = Alloc(mmckInfo.cksize); + infoPtr->inbih = heap_alloc_zero(mmckInfo.cksize); if (!infoPtr->inbih) { WARN("Can't alloc input BIH\n"); return FALSE; @@ -609,7 +613,7 @@ static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr) /* FIXME: should handle the 'rec ' LIST when present */ - infoPtr->lpIndex = Alloc(infoPtr->mah.dwTotalFrames * sizeof(DWORD)); + infoPtr->lpIndex = heap_alloc_zero(infoPtr->mah.dwTotalFrames * sizeof(DWORD)); if (!infoPtr->lpIndex) return FALSE; @@ -631,7 +635,7 @@ static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr) infoPtr->ash.dwSuggestedBufferSize = insize; } - infoPtr->indata = Alloc(infoPtr->ash.dwSuggestedBufferSize); + infoPtr->indata = heap_alloc_zero(infoPtr->ash.dwSuggestedBufferSize); if (!infoPtr->indata) return FALSE; @@ -662,7 +666,7 @@ static BOOL ANIMATE_GetAviCodec(ANIMATE_INFO *infoPtr) outSize = fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_GET_FORMAT, (DWORD_PTR)infoPtr->inbih, 0L); - infoPtr->outbih = Alloc(outSize); + infoPtr->outbih = heap_alloc_zero(outSize); if (!infoPtr->outbih) return FALSE; @@ -673,7 +677,7 @@ static BOOL ANIMATE_GetAviCodec(ANIMATE_INFO *infoPtr) return FALSE; } - infoPtr->outdata = Alloc(infoPtr->outbih->biSizeImage); + infoPtr->outdata = heap_alloc_zero(infoPtr->outbih->biSizeImage); if (!infoPtr->outdata) return FALSE; @@ -767,12 +771,12 @@ static BOOL ANIMATE_OpenA(ANIMATE_INFO *infoPtr, HINSTANCE hInstance, LPSTR lpsz return ANIMATE_OpenW(infoPtr, hInstance, (LPWSTR)lpszName); len = MultiByteToWideChar(CP_ACP, 0, lpszName, -1, NULL, 0); - lpwszName = Alloc(len * sizeof(WCHAR)); + lpwszName = heap_alloc(len * sizeof(WCHAR)); if (!lpwszName) return FALSE; MultiByteToWideChar(CP_ACP, 0, lpszName, -1, lpwszName, len); result = ANIMATE_OpenW(infoPtr, hInstance, lpwszName); - Free (lpwszName); + heap_free (lpwszName); return result; } @@ -805,7 +809,7 @@ static BOOL ANIMATE_Create(HWND hWnd, const CREATESTRUCTW *lpcs) } /* allocate memory for info structure */ - infoPtr = Alloc(sizeof(ANIMATE_INFO)); + infoPtr = heap_alloc_zero(sizeof(*infoPtr)); if (!infoPtr) return FALSE; /* store crossref hWnd <-> info structure */ @@ -835,7 +839,7 @@ static LRESULT ANIMATE_Destroy(ANIMATE_INFO *infoPtr) infoPtr->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&infoPtr->cs); - Free(infoPtr); + heap_free(infoPtr); return 0; } diff --git a/dll/win32/comctl32/button.c b/dll/win32/comctl32/button.c index bdc91840d5c..c313aa81e85 100644 --- a/dll/win32/comctl32/button.c +++ b/dll/win32/comctl32/button.c @@ -1,8 +1,8 @@ -/* File: button.c -- Button type widgets - * +/* * Copyright (C) 1993 Johannes Ruscheinski * Copyright (C) 1993 David Metcalfe * Copyright (C) 1994 Alexandre Julliard + * Copyright (C) 2008 by Reece H. Dunn * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,15 +18,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTES - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Oct. 3, 2004, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO * Styles * - BS_NOTIFY: is it complete? @@ -42,7 +33,7 @@ * - BCM_GETTEXTMARGIN * - BCM_SETIMAGELIST * - BCM_SETTEXTMARGIN - * + * * Notifications * - BCN_HOTITEMCHANGE * - BN_DISABLE @@ -62,18 +53,26 @@ * - Button_SetImageList * - Button_SetTextMargin */ + +#include +#include +#include + +#define OEMRESOURCE + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" +#include "wine/heap.h" + #include "comctl32.h" -#include WINE_DEFAULT_DEBUG_CHANNEL(button); -/* GetWindowLong offsets for window extra information */ -#define STATE_GWL_OFFSET 0 -#define HFONT_GWL_OFFSET (sizeof(LONG)) -#define HIMAGE_GWL_OFFSET (HFONT_GWL_OFFSET+sizeof(HFONT)) -#define UISTATE_GWL_OFFSET (HIMAGE_GWL_OFFSET+sizeof(HFONT)) -#define NB_EXTRA_BYTES (UISTATE_GWL_OFFSET+sizeof(LONG)) - /* undocumented flags */ #define BUTTON_NSTATES 0x0F #define BUTTON_BTNPRESSED 0x40 @@ -91,12 +90,31 @@ WINE_DEFAULT_DEBUG_CHANNEL(button); (LPARAM)(hWnd)); \ } while(0) -static UINT BUTTON_CalcLabelRect( HWND hwnd, HDC hdc, RECT *rc ); -static void PB_Paint( HWND hwnd, HDC hDC, UINT action ); -static void CB_Paint( HWND hwnd, HDC hDC, UINT action ); -static void GB_Paint( HWND hwnd, HDC hDC, UINT action ); -static void UB_Paint( HWND hwnd, HDC hDC, UINT action ); -static void OB_Paint( HWND hwnd, HDC hDC, UINT action ); +typedef struct _BUTTON_INFO +{ + HWND hwnd; + LONG state; + HFONT font; + union + { + HICON icon; + HBITMAP bitmap; + HANDLE image; + } u; + +#ifdef __REACTOS__ + DWORD ui_state; + RECT rcTextMargin; + BUTTON_IMAGELIST imlData; +#endif +} BUTTON_INFO; + +static UINT BUTTON_CalcLabelRect( const BUTTON_INFO *infoPtr, HDC hdc, RECT *rc ); +static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ); +static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ); +static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ); +static void UB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ); +static void OB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ); static void BUTTON_CheckAutoRadioButton( HWND hwnd ); #define MAX_BTN_TYPE 16 @@ -114,10 +132,24 @@ static const WORD maxCheckState[MAX_BTN_TYPE] = BST_UNCHECKED, /* BS_USERBUTTON */ BST_CHECKED, /* BS_AUTORADIOBUTTON */ BST_UNCHECKED, /* BS_PUSHBOX */ - BST_UNCHECKED /* BS_OWNERDRAW */ + BST_UNCHECKED, /* BS_OWNERDRAW */ + BST_UNCHECKED, /* BS_SPLITBUTTON */ + BST_UNCHECKED, /* BS_DEFSPLITBUTTON */ + BST_UNCHECKED, /* BS_COMMANDLINK */ + BST_UNCHECKED /* BS_DEFCOMMANDLINK */ }; -typedef void (*pfPaint)( HWND hwnd, HDC hdc, UINT action ); +/* These are indices into a states array to determine the theme state for a given theme part. */ +typedef enum +{ + STATE_NORMAL, + STATE_DISABLED, + STATE_HOT, + STATE_PRESSED, + STATE_DEFAULTED +} ButtonState; + +typedef void (*pfPaint)( const BUTTON_INFO *infoPtr, HDC hdc, UINT action ); static const pfPaint btnPaintFunc[MAX_BTN_TYPE] = { @@ -132,88 +164,76 @@ static const pfPaint btnPaintFunc[MAX_BTN_TYPE] = UB_Paint, /* BS_USERBUTTON */ CB_Paint, /* BS_AUTORADIOBUTTON */ NULL, /* BS_PUSHBOX */ - OB_Paint /* BS_OWNERDRAW */ + OB_Paint, /* BS_OWNERDRAW */ + PB_Paint, /* BS_SPLITBUTTON */ + PB_Paint, /* BS_DEFSPLITBUTTON */ + PB_Paint, /* BS_COMMANDLINK */ + PB_Paint /* BS_DEFCOMMANDLINK */ }; -/* The original code from user32 was kept in order to make it easier to bring changes from user32 */ -#ifdef _USER32_ -/********************************************************************* - * button class descriptor - */ -static const WCHAR buttonW[] = {'B','u','t','t','o','n',0}; -const struct builtin_class_descr BUTTON_builtin_class = -{ - buttonW, /* name */ - CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */ -#ifdef __REACTOS__ - ButtonWndProcA, /* procA */ - ButtonWndProcW, /* procW */ + +#ifdef __REACTOS__ /* r73885 */ +typedef void (*pfThemedPaint)( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused, LPARAM prfFlag); + +static void PB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused, LPARAM prfFlag); +static void CB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused, LPARAM prfFlag); +static void GB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused, LPARAM prfFlag); + #else - WINPROC_BUTTON, /* proc */ +typedef void (*pfThemedPaint)( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused); + +static void PB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused); +static void CB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused); +static void GB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused); + #endif - NB_EXTRA_BYTES, /* extra */ - IDC_ARROW, /* cursor */ - 0 /* brush */ + +static const pfThemedPaint btnThemedPaintFunc[MAX_BTN_TYPE] = +{ + PB_ThemedPaint, /* BS_PUSHBUTTON */ + PB_ThemedPaint, /* BS_DEFPUSHBUTTON */ + CB_ThemedPaint, /* BS_CHECKBOX */ + CB_ThemedPaint, /* BS_AUTOCHECKBOX */ + CB_ThemedPaint, /* BS_RADIOBUTTON */ + CB_ThemedPaint, /* BS_3STATE */ + CB_ThemedPaint, /* BS_AUTO3STATE */ + GB_ThemedPaint, /* BS_GROUPBOX */ + NULL, /* BS_USERBUTTON */ + CB_ThemedPaint, /* BS_AUTORADIOBUTTON */ + NULL, /* BS_PUSHBOX */ + NULL, /* BS_OWNERDRAW */ + NULL, /* BS_SPLITBUTTON */ + NULL, /* BS_DEFSPLITBUTTON */ + NULL, /* BS_COMMANDLINK */ + NULL, /* BS_DEFCOMMANDLINK */ }; - -static inline LONG get_button_state( HWND hwnd ) -{ - return GetWindowLongPtrW( hwnd, STATE_GWL_OFFSET ); -} - -static inline void set_button_state( HWND hwnd, LONG state ) -{ - SetWindowLongPtrW( hwnd, STATE_GWL_OFFSET, state ); -} - -#ifdef __REACTOS__ - -static __inline void set_ui_state( HWND hwnd, LONG flags ) -{ - SetWindowLongPtrW( hwnd, UISTATE_GWL_OFFSET, flags ); -} - -static __inline LONG get_ui_state( HWND hwnd ) -{ - return GetWindowLongPtrW( hwnd, UISTATE_GWL_OFFSET ); -} - -#endif /* __REACTOS__ */ - -static inline HFONT get_button_font( HWND hwnd ) -{ - return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); -} - -static inline void set_button_font( HWND hwnd, HFONT font ) -{ - SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font ); -} - static inline UINT get_button_type( LONG window_style ) { return (window_style & BS_TYPEMASK); } +#ifndef __REACTOS__ /* paint a button of any type */ -static inline void paint_button( HWND hwnd, LONG style, UINT action ) +static inline void paint_button( BUTTON_INFO *infoPtr, LONG style, UINT action ) { - if (btnPaintFunc[style] && IsWindowVisible(hwnd)) + if (btnPaintFunc[style] && IsWindowVisible(infoPtr->hwnd)) { - HDC hdc = GetDC( hwnd ); - btnPaintFunc[style]( hwnd, hdc, action ); - ReleaseDC( hwnd, hdc ); + HDC hdc = GetDC( infoPtr->hwnd ); + btnPaintFunc[style]( infoPtr, hdc, action ); + ReleaseDC( infoPtr->hwnd, hdc ); } } +#endif -#else - -#define NtUserAlterWindowStyle SetWindowLongPtrW - -static inline void _SetButtonData(HWND hwnd, PBUTTON_DATA data) +/* retrieve the button text; returned buffer must be freed by caller */ +static inline WCHAR *get_button_text( const BUTTON_INFO *infoPtr ) { - SetWindowLongPtrW( hwnd, 0, (LONG)data ); + INT len = GetWindowTextLengthW( infoPtr->hwnd ); + WCHAR *buffer = heap_alloc( (len + 1) * sizeof(WCHAR) ); + if (buffer) + GetWindowTextW( infoPtr->hwnd, buffer, len + 1 ); + return buffer; } HRGN set_control_clipping( HDC hdc, const RECT *rect ) @@ -236,81 +256,153 @@ HRGN set_control_clipping( HDC hdc, const RECT *rect ) return hrgn; } -BOOL BUTTON_PaintWithTheme(HTHEME theme, HWND hwnd, HDC hParamDC, LPARAM prfFlag); -WCHAR *get_button_text( HWND hwnd ); - -static inline LONG_PTR get_button_image(HWND hwnd) +/********************************************************************** + * Convert button styles to flags used by DrawText. + */ +static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style ) { - return _GetButtonData(hwnd)->image; + UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */ + + /* "Convert" pushlike buttons to pushbuttons */ + if (style & BS_PUSHLIKE) + style &= ~BS_TYPEMASK; + + if (!(style & BS_MULTILINE)) + dtStyle |= DT_SINGLELINE; + else + dtStyle |= DT_WORDBREAK; + + switch (style & BS_CENTER) + { + case BS_LEFT: /* DT_LEFT is 0 */ break; + case BS_RIGHT: dtStyle |= DT_RIGHT; break; + case BS_CENTER: dtStyle |= DT_CENTER; break; + default: + /* Pushbutton's text is centered by default */ + if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER; + /* all other flavours have left aligned text */ + } + + if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER)); + + /* DrawText ignores vertical alignment for multiline text, + * but we use these flags to align label manually. + */ + if (get_button_type(style) != BS_GROUPBOX) + { + switch (style & BS_VCENTER) + { + case BS_TOP: /* DT_TOP is 0 */ break; + case BS_BOTTOM: dtStyle |= DT_BOTTOM; break; + case BS_VCENTER: /* fall through */ + default: dtStyle |= DT_VCENTER; break; + } + } + else + /* GroupBox's text is always single line and is top aligned. */ + dtStyle |= DT_SINGLELINE; + + return dtStyle; } -static inline LONG_PTR set_button_image(HWND hwnd, LONG_PTR image) -{ - PBUTTON_DATA data = _GetButtonData(hwnd); - LONG_PTR ret = data->image; - data->image = image; - return ret; -} -static inline LONG get_button_state( HWND hwnd ) +#ifdef __REACTOS__ +BOOL BUTTON_PaintWithTheme(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hParamDC, LPARAM prfFlag) { - return _GetButtonData(hwnd)->state; -} + DWORD dwStyle; + DWORD dwStyleEx; + DWORD type; + UINT dtFlags; + ButtonState drawState; + pfThemedPaint paint; -static inline void set_button_state( HWND hwnd, LONG state ) -{ - _GetButtonData(hwnd)->state = state; -} + /* Don't draw with themes on a button with BS_ICON or BS_BITMAP */ + if (infoPtr->u.image != 0) + return FALSE; -static __inline void set_ui_state( HWND hwnd, LONG flags ) -{ - _GetButtonData(hwnd)->ui_state = flags; -} + dwStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); + type = get_button_type(dwStyle); -static __inline LONG get_ui_state( HWND hwnd ) -{ - return _GetButtonData(hwnd)->ui_state; -} + if (type != BS_PUSHBUTTON && type != BS_DEFPUSHBUTTON && (dwStyle & BS_PUSHLIKE)) + type = BS_PUSHBUTTON; -static inline HFONT get_button_font( HWND hwnd ) -{ - return (HFONT)_GetButtonData(hwnd)->font; -} + paint = btnThemedPaintFunc[type]; + if (!paint) + return FALSE; -static inline void set_button_font( HWND hwnd, HFONT font ) -{ - _GetButtonData(hwnd)->font = font; -} + dwStyleEx = GetWindowLongW(infoPtr->hwnd, GWL_EXSTYLE); + dtFlags = BUTTON_BStoDT(dwStyle, dwStyleEx); -static inline UINT get_button_type( LONG window_style ) -{ - return (window_style & BS_TYPEMASK); + if(dwStyle & WS_DISABLED) + drawState = STATE_DISABLED; + else if(infoPtr->state & BST_PUSHED) + drawState = STATE_PRESSED; + else if ((dwStyle & BS_PUSHLIKE) && (infoPtr->state & (BST_CHECKED|BST_INDETERMINATE))) + drawState = STATE_PRESSED; + else if(infoPtr->state & BST_HOT) + drawState = STATE_HOT; + else if((infoPtr->state & BST_FOCUS) || (dwStyle & BS_DEFPUSHBUTTON)) + drawState = STATE_DEFAULTED; + else + drawState = STATE_NORMAL; + + if (paint == PB_ThemedPaint || paint == CB_ThemedPaint) + { + HDC hdc; + HBITMAP hbmp; + RECT rc; + + GetClientRect(infoPtr->hwnd, &rc); + hdc = CreateCompatibleDC(hParamDC); + hbmp = CreateCompatibleBitmap(hParamDC, rc.right, rc.bottom); + if (hdc && hbmp) + { + SelectObject(hdc, hbmp); + + paint(theme, infoPtr, hdc, drawState, dtFlags, infoPtr->state & BST_FOCUS, prfFlag); + + BitBlt(hParamDC, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY); + DeleteObject(hbmp); + DeleteDC(hdc); + return TRUE; + } + else + { + ERR("Failed to create DC and bitmap for double buffering\n"); + if (hbmp) + DeleteObject(hbmp); + if (hdc) + DeleteDC(hdc); + } + } + + paint(theme, infoPtr, hParamDC, drawState, dtFlags, infoPtr->state & BST_FOCUS, prfFlag); + return TRUE; } /* paint a button of any type */ -static inline void paint_button( HWND hwnd, LONG style, UINT action ) +static inline void paint_button( BUTTON_INFO *infoPtr, LONG style, UINT action ) { - HTHEME theme = GetWindowTheme(hwnd); + HTHEME theme = GetWindowTheme(infoPtr->hwnd); RECT rc; - HDC hdc = GetDC( hwnd ); + HDC hdc = GetDC( infoPtr->hwnd ); /* GetDC appears to give a dc with a clip rect that includes the whoe parent, not sure if it is correct or not. */ - GetClientRect(hwnd, &rc); + GetClientRect(infoPtr->hwnd, &rc); IntersectClipRect (hdc, rc.left, rc. top, rc.right, rc.bottom); - if (theme && BUTTON_PaintWithTheme(theme, hwnd, hdc, 0)) + if (theme && BUTTON_PaintWithTheme(theme, infoPtr, hdc, 0)) { - ReleaseDC( hwnd, hdc ); + ReleaseDC( infoPtr->hwnd, hdc ); return; } - if (btnPaintFunc[style] && IsWindowVisible(hwnd)) + if (btnPaintFunc[style] && IsWindowVisible(infoPtr->hwnd)) { - btnPaintFunc[style]( hwnd, hdc, action ); + btnPaintFunc[style]( infoPtr, hdc, action ); } - ReleaseDC( hwnd, hdc ); + ReleaseDC( infoPtr->hwnd, hdc ); } -BOOL BUTTON_GetIdealSize(HTHEME theme, HWND hwnd, SIZE* psize) +BOOL BUTTON_GetIdealSize(BUTTON_INFO *infoPtr, HTHEME theme, SIZE* psize) { - PBUTTON_DATA pdata; HDC hdc; WCHAR *text; HFONT hFont = 0, hPrevFont = 0; @@ -318,10 +410,9 @@ BOOL BUTTON_GetIdealSize(HTHEME theme, HWND hwnd, SIZE* psize) BOOL ret = FALSE; LOGFONTW logfont = {0}; - pdata = _GetButtonData(hwnd); - text = get_button_text( hwnd ); - hdc = GetDC(hwnd); - if (!pdata || !text || !hdc || !text[0]) + text = get_button_text(infoPtr); + hdc = GetDC(infoPtr->hwnd); + if (!text || !hdc || !text[0]) goto cleanup; /* FIXME : Should use GetThemeTextExtent but unfortunately uses DrawTextW which is broken */ @@ -337,8 +428,8 @@ BOOL BUTTON_GetIdealSize(HTHEME theme, HWND hwnd, SIZE* psize) } else { - if (pdata->font) - hPrevFont = SelectObject( hdc, pdata->font ); + if (infoPtr->font) + hPrevFont = SelectObject( hdc, infoPtr->font ); } GetTextExtentPoint32W(hdc, text, wcslen(text), &TextSize); @@ -352,13 +443,13 @@ BOOL BUTTON_GetIdealSize(HTHEME theme, HWND hwnd, SIZE* psize) if (hPrevFont) SelectObject( hdc, hPrevFont ); - TextSize.cy += pdata->rcTextMargin.top + pdata->rcTextMargin.bottom; - TextSize.cx += pdata->rcTextMargin.left + pdata->rcTextMargin.right; + TextSize.cy += infoPtr->rcTextMargin.top + infoPtr->rcTextMargin.bottom; + TextSize.cx += infoPtr->rcTextMargin.left + infoPtr->rcTextMargin.right; - if (pdata->imlData.himl && ImageList_GetIconSize(pdata->imlData.himl, &ImageSize.cx, &ImageSize.cy)) + if (infoPtr->imlData.himl && ImageList_GetIconSize(infoPtr->imlData.himl, &ImageSize.cx, &ImageSize.cy)) { - ImageSize.cx += pdata->imlData.margin.left + pdata->imlData.margin.right; - ImageSize.cy += pdata->imlData.margin.top + pdata->imlData.margin.bottom; + ImageSize.cx += infoPtr->imlData.margin.left + infoPtr->imlData.margin.right; + ImageSize.cy += infoPtr->imlData.margin.top + infoPtr->imlData.margin.bottom; } else { @@ -390,12 +481,12 @@ cleanup: if (text) HeapFree( GetProcessHeap(), 0, text ); if (hdc) - ReleaseDC(hwnd, hdc); + ReleaseDC(infoPtr->hwnd, hdc); return ret; } -BOOL BUTTON_DrawIml(HDC hDC, BUTTON_IMAGELIST *pimlData, RECT *prc, BOOL bOnlyCalc, int index) +BOOL BUTTON_DrawIml(HDC hDC, const BUTTON_IMAGELIST *pimlData, RECT *prc, BOOL bOnlyCalc, int index) { SIZE ImageSize; int left, top, count; @@ -451,13 +542,12 @@ BOOL BUTTON_DrawIml(HDC hDC, BUTTON_IMAGELIST *pimlData, RECT *prc, BOOL bOnlyCa return TRUE; } -DWORD BUTTON_SendCustomDraw(HWND hwnd, HDC hDC, DWORD dwDrawStage, RECT* prc) +DWORD BUTTON_SendCustomDraw(const BUTTON_INFO *infoPtr, HDC hDC, DWORD dwDrawStage, RECT* prc) { NMCUSTOMDRAW nmcs; - LONG state = get_button_state( hwnd ); - nmcs.hdr.hwndFrom = hwnd; - nmcs.hdr.idFrom = GetWindowLongPtrW (hwnd, GWLP_ID); + nmcs.hdr.hwndFrom = infoPtr->hwnd; + nmcs.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwnd, GWLP_ID); nmcs.hdr.code = NM_CUSTOMDRAW ; nmcs.dwDrawStage = dwDrawStage; nmcs.hdc = hDC; @@ -465,48 +555,28 @@ DWORD BUTTON_SendCustomDraw(HWND hwnd, HDC hDC, DWORD dwDrawStage, RECT* prc) nmcs.dwItemSpec = 0; nmcs.uItemState = 0; nmcs.lItemlParam = 0; - if(!IsWindowEnabled(hwnd)) + if(!IsWindowEnabled(infoPtr->hwnd)) nmcs.uItemState |= CDIS_DISABLED; - if (state & (BST_CHECKED | BST_INDETERMINATE)) + if (infoPtr->state & (BST_CHECKED | BST_INDETERMINATE)) nmcs.uItemState |= CDIS_CHECKED; - if (state & BST_FOCUS) + if (infoPtr->state & BST_FOCUS) nmcs.uItemState |= CDIS_FOCUS; - if (state & BST_PUSHED) + if (infoPtr->state & BST_PUSHED) nmcs.uItemState |= CDIS_SELECTED; - if (!(get_ui_state(hwnd) & UISF_HIDEACCEL)) + if (!(infoPtr->ui_state & UISF_HIDEACCEL)) nmcs.uItemState |= CDIS_SHOWKEYBOARDCUES; - return SendMessageW(GetParent(hwnd), WM_NOTIFY, nmcs.hdr.idFrom, (LPARAM)&nmcs); + return SendMessageW(GetParent(infoPtr->hwnd), WM_NOTIFY, nmcs.hdr.idFrom, (LPARAM)&nmcs); } -#endif - - -/* retrieve the button text; returned buffer must be freed by caller */ -inline WCHAR *get_button_text( HWND hwnd ) -{ - INT len = 512; - WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ); - if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 ); - return buffer; -} - -#ifdef __REACTOS__ /* Retrieve the UI state for the control */ -static BOOL button_update_uistate(HWND hwnd, BOOL unicode) +static BOOL button_update_uistate(BUTTON_INFO *infoPtr) { - LONG flags, prevflags; + LONG flags = DefWindowProcW(infoPtr->hwnd, WM_QUERYUISTATE, 0, 0); - if (unicode) - flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0); - else - flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0); - - prevflags = get_ui_state(hwnd); - - if (prevflags != flags) + if (infoPtr->ui_state != flags) { - set_ui_state(hwnd, flags); + infoPtr->ui_state = flags; return TRUE; } @@ -514,222 +584,65 @@ static BOOL button_update_uistate(HWND hwnd, BOOL unicode) } #endif -/*********************************************************************** - * ButtonWndProc_common - */ -LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, - WPARAM wParam, LPARAM lParam, BOOL unicode ) +static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + BUTTON_INFO *infoPtr = (BUTTON_INFO *)GetWindowLongPtrW(hWnd, 0); RECT rect; POINT pt; - LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE ); + LONG style = GetWindowLongW( hWnd, GWL_STYLE ); UINT btn_type = get_button_type( style ); - LONG state; + LONG state, new_state; HANDLE oldHbitmap; -#if defined(__REACTOS__) && defined(_USER32_) - PWND pWnd; + HTHEME theme; - pWnd = ValidateHwnd(hWnd); - if (pWnd) - { - if (!pWnd->fnid) - { - NtUserSetWindowFNID(hWnd, FNID_BUTTON); - } - else - { - if (pWnd->fnid != FNID_BUTTON) - { - ERR("Wrong window class for Button! fnId 0x%x\n",pWnd->fnid); - return 0; - } - } - } - else - return 0; -#else if (!IsWindow( hWnd )) return 0; -#endif + + if (!infoPtr && (uMsg != WM_NCCREATE)) + return DefWindowProcW(hWnd, uMsg, wParam, lParam); pt.x = (short)LOWORD(lParam); pt.y = (short)HIWORD(lParam); -#ifndef _USER32_ - switch (uMsg) - { - case WM_NCCREATE: - { - PBUTTON_DATA data = HeapAlloc( GetProcessHeap(), 0, sizeof(BUTTON_DATA) ); - if (!data) - { - ERR("Failed to alloc internal button data\n"); - return -1; - } - - memset(data, 0, sizeof(BUTTON_DATA)); - SetRect(&data->rcTextMargin, 1,1,1,1); - - _SetButtonData(hWnd, data); - break; - } - case WM_NCDESTROY: - { - PBUTTON_DATA data = _GetButtonData(hWnd); - if (!data) - { - ERR("No data"); - return 0; - } - HeapFree( GetProcessHeap(), 0, data ); - _SetButtonData(hWnd, NULL); - } - case WM_CREATE: - OpenThemeData(hWnd, WC_BUTTONW); - break; - case WM_DESTROY: - CloseThemeData (GetWindowTheme(hWnd)); - break; - case WM_THEMECHANGED: - CloseThemeData (GetWindowTheme(hWnd)); - OpenThemeData(hWnd, WC_BUTTONW); - InvalidateRect(hWnd, NULL, TRUE); - break; - case WM_MOUSELEAVE: - { - state = get_button_state( hWnd ); - if (state & BST_HOT) - { - NMBCHOTITEM nmhotitem; - - state &= ~BST_HOT; - set_button_state(hWnd, state); - - nmhotitem.hdr.hwndFrom = hWnd; - nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); - nmhotitem.hdr.code = BCN_HOTITEMCHANGE; - nmhotitem.dwFlags = HICF_LEAVING; - SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); - - InvalidateRect(hWnd, NULL, TRUE); - } - break; - } - case WM_MOUSEMOVE: - { - TRACKMOUSEEVENT mouse_event; - state = get_button_state( hWnd ); - if ((state & BST_HOT) == 0) - { - NMBCHOTITEM nmhotitem; - - state |= BST_HOT; - set_button_state(hWnd, state); - - nmhotitem.hdr.hwndFrom = hWnd; - nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); - nmhotitem.hdr.code = BCN_HOTITEMCHANGE; - nmhotitem.dwFlags = HICF_ENTERING; - SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); - - InvalidateRect(hWnd, NULL, TRUE); - } - - mouse_event.cbSize = sizeof(TRACKMOUSEEVENT); - mouse_event.dwFlags = TME_QUERY; - if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&TME_LEAVE)) - { - mouse_event.dwFlags = TME_LEAVE; - mouse_event.hwndTrack = hWnd; - mouse_event.dwHoverTime = 1; - TrackMouseEvent(&mouse_event); - } - break; - } - case BCM_GETTEXTMARGIN: - { - RECT* prc = (RECT*)lParam; - PBUTTON_DATA data = _GetButtonData(hWnd); - if (!prc || !data) - return FALSE; - *prc = data->rcTextMargin; - return TRUE; - } - case BCM_SETTEXTMARGIN: - { - RECT* prc = (RECT*)lParam; - PBUTTON_DATA data = _GetButtonData(hWnd); - if (!prc || !data) - return FALSE; - data->rcTextMargin = *prc; - return TRUE; - } - case BCM_SETIMAGELIST: - { - BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; - PBUTTON_DATA data = _GetButtonData(hWnd); - if (!data || !pimldata || !pimldata->himl) - return FALSE; - data->imlData = *pimldata; - return TRUE; - } - case BCM_GETIMAGELIST: - { - BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; - PBUTTON_DATA data = _GetButtonData(hWnd); - if (!data|| !pimldata) - return FALSE; - *pimldata = data->imlData; - return TRUE; - } - case BCM_GETIDEALSIZE: - { - HTHEME theme = GetWindowTheme(hWnd); - BOOL ret = FALSE; - SIZE* pSize = (SIZE*)lParam; - - if (btn_type == BS_PUSHBUTTON || - btn_type == BS_DEFPUSHBUTTON || - btn_type == BS_USERBUTTON) - { - ret = BUTTON_GetIdealSize(theme, hWnd, pSize); - } - - if (!ret) - { - GetClientRect(hWnd, &rect); - pSize->cx = rect.right; - pSize->cy = rect.bottom; - } - - return TRUE; - } - } - - if (!_GetButtonData(hWnd)) - { - ERR("no data!\n"); - return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) : - DefWindowProcA(hWnd, uMsg, wParam, lParam); - } - -#endif - switch (uMsg) { case WM_GETDLGCODE: switch(btn_type) { + case BS_COMMANDLINK: case BS_USERBUTTON: case BS_PUSHBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON; + case BS_DEFCOMMANDLINK: case BS_DEFPUSHBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON; case BS_RADIOBUTTON: case BS_AUTORADIOBUTTON: return DLGC_BUTTON | DLGC_RADIOBUTTON; case BS_GROUPBOX: return DLGC_STATIC; + case BS_SPLITBUTTON: return DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_WANTARROWS; + case BS_DEFSPLITBUTTON: return DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_WANTARROWS; default: return DLGC_BUTTON; } case WM_ENABLE: - paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); +#ifndef __REACTOS__ + theme = GetWindowTheme( hWnd ); + if (theme) + RedrawWindow( hWnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW ); + else +#endif + paint_button( infoPtr, btn_type, ODA_DRAWENTIRE ); + break; + + case WM_NCCREATE: + infoPtr = heap_alloc_zero( sizeof(*infoPtr) ); + SetWindowLongPtrW( hWnd, 0, (LONG_PTR)infoPtr ); + infoPtr->hwnd = hWnd; +#ifdef __REACTOS__ + SetRect(&infoPtr->rcTextMargin, 1,1,1,1); +#endif + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + + case WM_NCDESTROY: + SetWindowLongPtrW( hWnd, 0, 0 ); + heap_free(infoPtr); break; case WM_CREATE: @@ -740,24 +653,26 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, if (btn_type == BS_USERBUTTON ) { style = (style & ~BS_TYPEMASK) | BS_PUSHBUTTON; -#ifdef __REACTOS__ - NtUserAlterWindowStyle(hWnd, GWL_STYLE, style ); -#else - WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); -#endif + SetWindowLongW( hWnd, GWL_STYLE, style ); } - set_button_state( hWnd, BST_UNCHECKED ); -#ifdef __REACTOS__ - button_update_uistate( hWnd, unicode ); -#endif + infoPtr->state = BST_UNCHECKED; + OpenThemeData( hWnd, WC_BUTTONW ); return 0; -#if defined(__REACTOS__) && defined(_USER32_) - case WM_NCDESTROY: - NtUserSetWindowFNID(hWnd, FNID_DESTROY); case WM_DESTROY: + theme = GetWindowTheme( hWnd ); + CloseThemeData( theme ); break; + + case WM_THEMECHANGED: + theme = GetWindowTheme( hWnd ); + CloseThemeData( theme ); + OpenThemeData( hWnd, WC_BUTTONW ); +#ifdef __REACTOS__ + InvalidateRect(hWnd, NULL, TRUE); #endif + break; + case WM_ERASEBKGND: if (btn_type == BS_OWNERDRAW) { @@ -766,14 +681,10 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, HBRUSH hBrush; HWND parent = GetParent(hWnd); if (!parent) parent = hWnd; -#if defined(__REACTOS__) && defined(_USER32_) - hBrush = GetControlColor( parent, hWnd, hdc, WM_CTLCOLORBTN); -#else hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); if (!hBrush) /* did the app forget to call defwindowproc ? */ hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); -#endif GetClientRect(hWnd, &rc); FillRect(hdc, &rc, hBrush); } @@ -783,21 +694,44 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, case WM_PAINT: { PAINTSTRUCT ps; - HDC hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps ); -#ifndef _USER32_ - HTHEME theme = GetWindowTheme(hWnd); - if (theme && BUTTON_PaintWithTheme(theme, hWnd, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0)) + HDC hdc; + + theme = GetWindowTheme( hWnd ); + hdc = wParam ? (HDC)wParam : BeginPaint( hWnd, &ps ); + +#ifdef __REACTOS__ + if (theme && BUTTON_PaintWithTheme(theme, infoPtr, hdc, uMsg == WM_PRINTCLIENT ? lParam : 0)) { if ( !wParam ) EndPaint( hWnd, &ps ); return 0; } +#else + if (theme && btnThemedPaintFunc[btn_type]) + { + ButtonState drawState; + UINT dtflags; + + if (IsWindowEnabled( hWnd )) + { + if (infoPtr->state & BST_PUSHED) drawState = STATE_PRESSED; + else if (infoPtr->state & BST_HOT) drawState = STATE_HOT; + else if (infoPtr->state & BST_FOCUS) drawState = STATE_DEFAULTED; + else drawState = STATE_NORMAL; + } + else + drawState = STATE_DISABLED; + + dtflags = BUTTON_BStoDT(style, GetWindowLongW(hWnd, GWL_EXSTYLE)); + btnThemedPaintFunc[btn_type](theme, infoPtr, hdc, drawState, dtflags, infoPtr->state & BST_FOCUS); + } #endif - if (btnPaintFunc[btn_type]) + else if (btnPaintFunc[btn_type]) { int nOldMode = SetBkMode( hdc, OPAQUE ); - (btnPaintFunc[btn_type])( hWnd, hdc, ODA_DRAWENTIRE ); + btnPaintFunc[btn_type]( infoPtr, hdc, ODA_DRAWENTIRE ); SetBkMode(hdc, nOldMode); /* reset painting mode */ } + if ( !wParam ) EndPaint( hWnd, &ps ); break; } @@ -806,7 +740,7 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, if (wParam == VK_SPACE) { SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); - set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED ); + infoPtr->state |= BUTTON_BTNPRESSED; SetCapture( hWnd ); } break; @@ -824,7 +758,7 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, case WM_LBUTTONDOWN: SetCapture( hWnd ); SetFocus( hWnd ); - set_button_state( hWnd, get_button_state( hWnd ) | BUTTON_BTNPRESSED ); + infoPtr->state |= BUTTON_BTNPRESSED; SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); break; @@ -833,13 +767,9 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, break; /* fall through */ case WM_LBUTTONUP: -#ifdef _REACTOS_ - BOOL TellParent = FALSE; //// ReactOS see note below. -#endif - state = get_button_state( hWnd ); + state = infoPtr->state; if (!(state & BUTTON_BTNPRESSED)) break; - state &= BUTTON_NSTATES; - set_button_state( hWnd, state ); + infoPtr->state &= BUTTON_NSTATES; if (!(state & BST_PUSHED)) { ReleaseCapture(); @@ -849,68 +779,196 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, GetClientRect( hWnd, &rect ); if (uMsg == WM_KEYUP || PtInRect( &rect, pt )) { - state = get_button_state( hWnd ); switch(btn_type) { case BS_AUTOCHECKBOX: - SendMessageW( hWnd, BM_SETCHECK, !(state & BST_CHECKED), 0 ); + SendMessageW( hWnd, BM_SETCHECK, !(infoPtr->state & BST_CHECKED), 0 ); break; case BS_AUTORADIOBUTTON: SendMessageW( hWnd, BM_SETCHECK, TRUE, 0 ); break; case BS_AUTO3STATE: - SendMessageW( hWnd, BM_SETCHECK, - (state & BST_INDETERMINATE) ? 0 : ((state & 3) + 1), 0 ); + SendMessageW( hWnd, BM_SETCHECK, (infoPtr->state & BST_INDETERMINATE) ? 0 : + ((infoPtr->state & 3) + 1), 0 ); break; } -#ifdef _REACTOS_ - TellParent = TRUE; // <---- Fix CORE-10194, Notify parent after capture is released. -#else +#ifdef __REACTOS__ + // Fix CORE-10194, Notify parent after capture is released. ReleaseCapture(); BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); +#else + BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); + ReleaseCapture(); #endif } -#ifdef _REACTOS_ - ReleaseCapture(); - if (TellParent) BUTTON_NOTIFY_PARENT(hWnd, BN_CLICKED); -#else else { ReleaseCapture(); } -#endif + break; case WM_CAPTURECHANGED: TRACE("WM_CAPTURECHANGED %p\n", hWnd); if (hWnd == (HWND)lParam) break; - state = get_button_state( hWnd ); - if (state & BUTTON_BTNPRESSED) + if (infoPtr->state & BUTTON_BTNPRESSED) { - state &= BUTTON_NSTATES; - set_button_state( hWnd, state ); - if (state & BST_PUSHED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); + infoPtr->state &= BUTTON_NSTATES; + if (infoPtr->state & BST_PUSHED) + SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); } break; case WM_MOUSEMOVE: + { + TRACKMOUSEEVENT mouse_event; + mouse_event.cbSize = sizeof(TRACKMOUSEEVENT); + mouse_event.dwFlags = TME_QUERY; + +#ifdef __REACTOS__ + if ((infoPtr->state & BST_HOT) == 0) + { + NMBCHOTITEM nmhotitem; + + infoPtr->state |= BST_HOT; + + nmhotitem.hdr.hwndFrom = hWnd; + nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); + nmhotitem.hdr.code = BCN_HOTITEMCHANGE; + nmhotitem.dwFlags = HICF_ENTERING; + SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); + + InvalidateRect(hWnd, NULL, TRUE); + } + + if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&TME_LEAVE)) + { + mouse_event.dwFlags = TME_LEAVE; + mouse_event.hwndTrack = hWnd; + mouse_event.dwHoverTime = 1; + TrackMouseEvent(&mouse_event); + } + break; +#else + + if (!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags & (TME_HOVER | TME_LEAVE))) + { + mouse_event.dwFlags = TME_HOVER | TME_LEAVE; + mouse_event.hwndTrack = hWnd; + mouse_event.dwHoverTime = 1; + TrackMouseEvent(&mouse_event); + } + if ((wParam & MK_LBUTTON) && GetCapture() == hWnd) { GetClientRect( hWnd, &rect ); SendMessageW( hWnd, BM_SETSTATE, PtInRect(&rect, pt), 0 ); } break; +#endif + } + +#ifndef __REACTOS__ + case WM_MOUSEHOVER: + { + infoPtr->state |= BST_HOT; + InvalidateRect( hWnd, NULL, FALSE ); + break; + } +#endif + + case WM_MOUSELEAVE: + { +#ifdef __REACTOS__ + if (infoPtr->state & BST_HOT) + { + NMBCHOTITEM nmhotitem; + + infoPtr->state &= ~BST_HOT; + + nmhotitem.hdr.hwndFrom = hWnd; + nmhotitem.hdr.idFrom = GetWindowLongPtrW (hWnd, GWLP_ID); + nmhotitem.hdr.code = BCN_HOTITEMCHANGE; + nmhotitem.dwFlags = HICF_LEAVING; + SendMessageW(GetParent(hWnd), WM_NOTIFY, nmhotitem.hdr.idFrom, (LPARAM)&nmhotitem); + + InvalidateRect(hWnd, NULL, TRUE); + } + break; +#else + infoPtr->state &= ~BST_HOT; + InvalidateRect( hWnd, NULL, FALSE ); + break; +#endif + } + +#ifdef __REACTOS__ + case BCM_GETTEXTMARGIN: + { + RECT* prc = (RECT*)lParam; + if (!prc) + return FALSE; + *prc = infoPtr->rcTextMargin; + return TRUE; + } + case BCM_SETTEXTMARGIN: + { + RECT* prc = (RECT*)lParam; + if (!prc) + return FALSE; + infoPtr->rcTextMargin = *prc; + return TRUE; + } + case BCM_SETIMAGELIST: + { + BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; + if (!pimldata || !pimldata->himl) + return FALSE; + infoPtr->imlData = *pimldata; + return TRUE; + } + case BCM_GETIMAGELIST: + { + BUTTON_IMAGELIST * pimldata = (BUTTON_IMAGELIST *)lParam; + if (!pimldata) + return FALSE; + *pimldata = infoPtr->imlData; + return TRUE; + } + case BCM_GETIDEALSIZE: + { + HTHEME theme = GetWindowTheme(hWnd); + BOOL ret = FALSE; + SIZE* pSize = (SIZE*)lParam; + + if (btn_type == BS_PUSHBUTTON || + btn_type == BS_DEFPUSHBUTTON || + btn_type == BS_USERBUTTON) + { + ret = BUTTON_GetIdealSize(infoPtr, theme, pSize); + } + + if (!ret) + { + GetClientRect(hWnd, &rect); + pSize->cx = rect.right; + pSize->cy = rect.bottom; + } + + return TRUE; + } +#endif case WM_SETTEXT: { /* Clear an old text here as Windows does */ +#ifdef __REACTOS__ // // ReactOS Note : // wine Bug: http://bugs.winehq.org/show_bug.cgi?id=25790 // Patch: http://source.winehq.org/patches/data/70889 // By: Alexander LAW, Replicate Windows behavior of WM_SETTEXT handler regarding WM_CTLCOLOR* // -#ifdef __REACTOS__ if (style & WS_VISIBLE) #else if (IsWindowVisible(hWnd)) @@ -922,28 +980,23 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, HWND parent = GetParent(hWnd); UINT message = (btn_type == BS_PUSHBUTTON || btn_type == BS_DEFPUSHBUTTON || - btn_type == BS_PUSHLIKE || btn_type == BS_USERBUTTON || btn_type == BS_OWNERDRAW) ? WM_CTLCOLORBTN : WM_CTLCOLORSTATIC; if (!parent) parent = hWnd; -#if defined(__REACTOS__) && defined(_USER32_) - hbrush = GetControlColor(parent, hWnd, hdc, message); -#else hbrush = (HBRUSH)SendMessageW(parent, message, (WPARAM)hdc, (LPARAM)hWnd); if (!hbrush) /* did the app forget to call DefWindowProc ? */ hbrush = (HBRUSH)DefWindowProcW(parent, message, (WPARAM)hdc, (LPARAM)hWnd); -#endif GetClientRect(hWnd, &client); rc = client; /* FIXME: check other BS_* handlers */ if (btn_type == BS_GROUPBOX) InflateRect(&rc, -7, 1); /* GB_Paint does this */ - BUTTON_CalcLabelRect(hWnd, hdc, &rc); + BUTTON_CalcLabelRect(infoPtr, hdc, &rc); /* Clip by client rect bounds */ if (rc.right > client.right) rc.right = client.right; if (rc.bottom > client.bottom) rc.bottom = client.bottom; @@ -951,45 +1004,43 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, ReleaseDC(hWnd, hdc); } - if (unicode) DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam ); - else DefWindowProcA( hWnd, WM_SETTEXT, wParam, lParam ); + DefWindowProcW( hWnd, WM_SETTEXT, wParam, lParam ); if (btn_type == BS_GROUPBOX) /* Yes, only for BS_GROUPBOX */ InvalidateRect( hWnd, NULL, TRUE ); else - paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); + paint_button( infoPtr, btn_type, ODA_DRAWENTIRE ); return 1; /* success. FIXME: check text length */ } case WM_SETFONT: - set_button_font( hWnd, (HFONT)wParam ); + infoPtr->font = (HFONT)wParam; if (lParam) InvalidateRect(hWnd, NULL, TRUE); break; case WM_GETFONT: - return (LRESULT)get_button_font( hWnd ); + return (LRESULT)infoPtr->font; case WM_SETFOCUS: TRACE("WM_SETFOCUS %p\n",hWnd); - set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS ); -#ifndef _USER32_ + infoPtr->state |= BST_FOCUS; +#ifdef __REACTOS__ if (btn_type != BS_OWNERDRAW) InvalidateRect(hWnd, NULL, FALSE); else #endif - paint_button( hWnd, btn_type, ODA_FOCUS ); + paint_button( infoPtr, btn_type, ODA_FOCUS ); if (style & BS_NOTIFY) BUTTON_NOTIFY_PARENT(hWnd, BN_SETFOCUS); break; case WM_KILLFOCUS: TRACE("WM_KILLFOCUS %p\n",hWnd); - state = get_button_state( hWnd ); - set_button_state( hWnd, state & ~BST_FOCUS ); -#ifdef _USER32_ - paint_button( hWnd, btn_type, ODA_FOCUS ); + infoPtr->state &= ~BST_FOCUS; +#ifndef __REACTOS__ + paint_button( infoPtr, btn_type, ODA_FOCUS ); #endif - if ((state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) + if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) ReleaseCapture(); if (style & BS_NOTIFY) BUTTON_NOTIFY_PARENT(hWnd, BN_KILLFOCUS); @@ -1004,11 +1055,7 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, case BM_SETSTYLE: btn_type = wParam & BS_TYPEMASK; style = (style & ~BS_TYPEMASK) | btn_type; -#ifdef __REACTOS__ - NtUserAlterWindowStyle(hWnd, GWL_STYLE, style); -#else - WIN_SetStyle( hWnd, style, BS_TYPEMASK & ~style ); -#endif + SetWindowLongW( hWnd, GWL_STYLE, style ); /* Only redraw if lParam flag is set.*/ if (lParam) @@ -1018,18 +1065,15 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, case BM_CLICK: #ifdef __REACTOS__ - state = get_button_state(hWnd); - if (state & BUTTON_BMCLICK) - break; - set_button_state(hWnd, state | BUTTON_BMCLICK); // Tracked in STATE_GWL_OFFSET. + /* Fix for core CORE-6024 */ + if (infoPtr->state & BUTTON_BMCLICK) + break; + infoPtr->state |= BUTTON_BMCLICK; #endif SendMessageW( hWnd, WM_LBUTTONDOWN, 0, 0 ); SendMessageW( hWnd, WM_LBUTTONUP, 0, 0 ); #ifdef __REACTOS__ - state = get_button_state(hWnd); - if (!(state & BUTTON_BMCLICK)) break; - state &= ~BUTTON_BMCLICK; - set_button_state(hWnd, state); + infoPtr->state &= ~BUTTON_BMCLICK; #endif break; @@ -1046,77 +1090,61 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, default: return 0; } -#ifdef _USER32_ - oldHbitmap = (HBITMAP)SetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET, lParam ); -#else - oldHbitmap = (HBITMAP)set_button_image(hWnd, lParam ); -#endif + oldHbitmap = infoPtr->u.image; + infoPtr->u.image = (HANDLE)lParam; InvalidateRect( hWnd, NULL, FALSE ); return (LRESULT)oldHbitmap; case BM_GETIMAGE: -#ifdef _USER32_ - return GetWindowLongPtrW( hWnd, HIMAGE_GWL_OFFSET ); -#else - return get_button_image(hWnd); -#endif + return (LRESULT)infoPtr->u.image; case BM_GETCHECK: - return get_button_state( hWnd ) & 3; + return infoPtr->state & 3; case BM_SETCHECK: if (wParam > maxCheckState[btn_type]) wParam = maxCheckState[btn_type]; - state = get_button_state( hWnd ); if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON)) { -#ifdef __REACTOS__ - if (wParam) style |= WS_TABSTOP; - else style &= ~WS_TABSTOP; - NtUserAlterWindowStyle(hWnd, GWL_STYLE, style); -#else - if (wParam) WIN_SetStyle( hWnd, WS_TABSTOP, 0 ); - else WIN_SetStyle( hWnd, 0, WS_TABSTOP ); -#endif + style = wParam ? style | WS_TABSTOP : style & ~WS_TABSTOP; + SetWindowLongW( hWnd, GWL_STYLE, style ); } - if ((state & 3) != wParam) + if ((infoPtr->state & 3) != wParam) { - set_button_state( hWnd, (state & ~3) | wParam ); -#ifdef _USER32 - paint_button( hWnd, btn_type, ODA_SELECT ); -#else - InvalidateRect(hWnd, NULL, FALSE); -#endif + infoPtr->state = (infoPtr->state & ~3) | wParam; + InvalidateRect( hWnd, NULL, FALSE ); } if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD)) BUTTON_CheckAutoRadioButton( hWnd ); break; case BM_GETSTATE: - return get_button_state( hWnd ); + return infoPtr->state; case BM_SETSTATE: - state = get_button_state( hWnd ); - if (wParam) - set_button_state( hWnd, state | BST_PUSHED ); - else - set_button_state( hWnd, state & ~BST_PUSHED ); + state = infoPtr->state; + new_state = wParam ? BST_PUSHED : 0; -#ifdef _USER32_ - paint_button( hWnd, btn_type, ODA_SELECT ); -#else - InvalidateRect(hWnd, NULL, FALSE); -#endif + if ((state ^ new_state) & BST_PUSHED) + { + if (wParam) + state |= BST_PUSHED; + else + state &= ~BST_PUSHED; + + if (btn_type == BS_USERBUTTON) + BUTTON_NOTIFY_PARENT( hWnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE ); + infoPtr->state = state; + + InvalidateRect( hWnd, NULL, FALSE ); + } break; #ifdef __REACTOS__ case WM_UPDATEUISTATE: - if (unicode) - DefWindowProcW(hWnd, uMsg, wParam, lParam); - else - DefWindowProcA(hWnd, uMsg, wParam, lParam); + DefWindowProcW(hWnd, uMsg, wParam, lParam); - if (button_update_uistate(hWnd, unicode)) - paint_button( hWnd, btn_type, ODA_DRAWENTIRE ); + if (button_update_uistate(infoPtr)) + paint_button( infoPtr, btn_type, ODA_DRAWENTIRE ); break; #endif @@ -1124,86 +1152,11 @@ LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, if(btn_type == BS_GROUPBOX) return HTTRANSPARENT; /* fall through */ default: - return unicode ? DefWindowProcW(hWnd, uMsg, wParam, lParam) : - DefWindowProcA(hWnd, uMsg, wParam, lParam); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); } return 0; } -#ifdef __REACTOS__ - -/*********************************************************************** - * ButtonWndProcW - * The button window procedure. This is just a wrapper which locks - * the passed HWND and calls the real window procedure (with a WND* - * pointer pointing to the locked windowstructure). - */ -LRESULT WINAPI ButtonWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (!IsWindow(hWnd)) return 0; - return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); -} - -/*********************************************************************** - * ButtonWndProcA - */ -LRESULT WINAPI ButtonWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (!IsWindow(hWnd)) return 0; - return ButtonWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); -} - -#endif /* __REACTOS__ */ - -/********************************************************************** - * Convert button styles to flags used by DrawText. - */ -static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style ) -{ - UINT dtStyle = DT_NOCLIP; /* We use SelectClipRgn to limit output */ - - /* "Convert" pushlike buttons to pushbuttons */ - if (style & BS_PUSHLIKE) - style &= ~BS_TYPEMASK; - - if (!(style & BS_MULTILINE)) - dtStyle |= DT_SINGLELINE; - else - dtStyle |= DT_WORDBREAK; - - switch (style & BS_CENTER) - { - case BS_LEFT: /* DT_LEFT is 0 */ break; - case BS_RIGHT: dtStyle |= DT_RIGHT; break; - case BS_CENTER: dtStyle |= DT_CENTER; break; - default: - /* Pushbutton's text is centered by default */ - if (get_button_type(style) <= BS_DEFPUSHBUTTON) dtStyle |= DT_CENTER; - /* all other flavours have left aligned text */ - } - - if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER)); - - /* DrawText ignores vertical alignment for multiline text, - * but we use these flags to align label manually. - */ - if (get_button_type(style) != BS_GROUPBOX) - { - switch (style & BS_VCENTER) - { - case BS_TOP: /* DT_TOP is 0 */ break; - case BS_BOTTOM: dtStyle |= DT_BOTTOM; break; - case BS_VCENTER: /* fall through */ - default: dtStyle |= DT_VCENTER; break; - } - } - else - /* GroupBox's text is always single line and is top aligned. */ - dtStyle |= DT_SINGLELINE; - - return dtStyle; -} - /********************************************************************** * BUTTON_CalcLabelRect * @@ -1214,10 +1167,10 @@ static UINT BUTTON_BStoDT( DWORD style, DWORD ex_style ) * (pushed, etc.). If there is nothing to draw (no text/image) output * rectangle is empty, and return value is (UINT)-1. */ -static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) +static UINT BUTTON_CalcLabelRect(const BUTTON_INFO *infoPtr, HDC hdc, RECT *rc) { - LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); - LONG ex_style = GetWindowLongPtrW( hwnd, GWL_EXSTYLE ); + LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE ); + LONG ex_style = GetWindowLongW( infoPtr->hwnd, GWL_EXSTYLE ); WCHAR *text; ICONINFO iconInfo; BITMAP bm; @@ -1225,11 +1178,7 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) RECT r = *rc; INT n; #ifdef __REACTOS__ - PBUTTON_DATA pdata = _GetButtonData(hwnd); -#endif - -#ifndef _USER32_ - BOOL bHasIml = BUTTON_DrawIml(hdc, &pdata->imlData, &r, TRUE, 0); + BOOL bHasIml = BUTTON_DrawIml(hdc, &infoPtr->imlData, &r, TRUE, 0); #endif /* Calculate label rectangle according to label type */ @@ -1239,30 +1188,26 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) { HFONT hFont, hPrevFont = 0; - if (!(text = get_button_text( hwnd ))) goto empty_rect; + if (!(text = get_button_text( infoPtr ))) goto empty_rect; if (!text[0]) { - HeapFree( GetProcessHeap(), 0, text ); + heap_free( text ); goto empty_rect; } - if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hdc, hFont ); + if ((hFont = infoPtr->font)) hPrevFont = SelectObject( hdc, hFont ); DrawTextW(hdc, text, -1, &r, dtStyle | DT_CALCRECT); if (hPrevFont) SelectObject( hdc, hPrevFont ); - HeapFree( GetProcessHeap(), 0, text ); + heap_free( text ); #ifdef __REACTOS__ - if (get_ui_state(hwnd) & UISF_HIDEACCEL) + if (infoPtr->ui_state & UISF_HIDEACCEL) dtStyle |= DT_HIDEPREFIX; #endif break; } case BS_ICON: -#ifdef _USER32_ - if (!GetIconInfo((HICON)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), &iconInfo)) -#else - if (!GetIconInfo((HICON)get_button_image(hwnd), &iconInfo)) -#endif + if (!GetIconInfo(infoPtr->u.icon, &iconInfo)) goto empty_rect; GetObjectW (iconInfo.hbmColor, sizeof(BITMAP), &bm); @@ -1275,11 +1220,7 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) break; case BS_BITMAP: -#ifdef _USER32_ - if (!GetObjectW( (HANDLE)GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ), sizeof(BITMAP), &bm)) -#else - if (!GetObjectW( (HANDLE)get_button_image(hwnd), sizeof(BITMAP), &bm)) -#endif + if (!GetObjectW( infoPtr->u.bitmap, sizeof(BITMAP), &bm)) goto empty_rect; r.right = r.left + bm.bmWidth; @@ -1288,26 +1229,22 @@ static UINT BUTTON_CalcLabelRect(HWND hwnd, HDC hdc, RECT *rc) default: empty_rect: -#ifndef _USER32_ - if (bHasIml) - break; -#endif rc->right = r.left; rc->bottom = r.top; return (UINT)-1; } -#ifndef _USER32_ +#ifdef __REACTOS__ if (bHasIml) { - if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT) - r.left = pdata->imlData.margin.left; - else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) - r.right = pdata->imlData.margin.right; - else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_TOP) - r.top = pdata->imlData.margin.top; - else if (pdata->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) - r.bottom = pdata->imlData.margin.bottom; + if (infoPtr->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT) + r.left = infoPtr->imlData.margin.left; + else if (infoPtr->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) + r.right = infoPtr->imlData.margin.right; + else if (infoPtr->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_TOP) + r.top = infoPtr->imlData.margin.top; + else if (infoPtr->imlData.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) + r.bottom = infoPtr->imlData.margin.bottom; } #endif @@ -1365,19 +1302,15 @@ static BOOL CALLBACK BUTTON_DrawTextCallback(HDC hdc, LPARAM lp, WPARAM wp, int * * Common function for drawing button label. */ - #if defined(_USER32_) -static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, const RECT *rc) -#else -static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc) -#endif +static void BUTTON_DrawLabel(const BUTTON_INFO *infoPtr, HDC hdc, UINT dtFlags, const RECT *rc) { DRAWSTATEPROC lpOutputProc = NULL; LPARAM lp; WPARAM wp = 0; HBRUSH hbr = 0; - UINT flags = IsWindowEnabled(hwnd) ? DSS_NORMAL : DSS_DISABLED; - LONG state = get_button_state( hwnd ); - LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); + UINT flags = IsWindowEnabled(infoPtr->hwnd) ? DSS_NORMAL : DSS_DISABLED; + LONG state = infoPtr->state; + LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE ); WCHAR *text = NULL; /* FIXME: To draw disabled label in Win31 look-and-feel, we probably @@ -1385,9 +1318,9 @@ static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc) * I don't have Win31 on hand to verify that, so I leave it as is. */ -#ifndef _USER32_ - PBUTTON_DATA pdata = _GetButtonData(hwnd); - BUTTON_DrawIml(hdc, &pdata->imlData, rc, FALSE, 0); +#ifdef __REACTOS__ + RECT rcText = *rc; + BUTTON_DrawIml(hdc, &infoPtr->imlData, &rcText, FALSE, 0); #endif if ((style & BS_PUSHLIKE) && (state & BST_INDETERMINATE)) @@ -1401,10 +1334,9 @@ static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc) case BS_TEXT: /* DST_COMPLEX -- is 0 */ lpOutputProc = BUTTON_DrawTextCallback; - if (!(text = get_button_text( hwnd ))) return; + if (!(text = get_button_text( infoPtr ))) return; lp = (LPARAM)text; - wp = (WPARAM)dtFlags; - + wp = dtFlags; #ifdef __REACTOS__ if (dtFlags & DT_HIDEPREFIX) flags |= DSS_HIDEPREFIX; @@ -1413,79 +1345,66 @@ static void BUTTON_DrawLabel(HWND hwnd, HDC hdc, UINT dtFlags, RECT *rc) case BS_ICON: flags |= DST_ICON; -#ifdef _USER32_ - lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ); -#else - lp = get_button_image(hwnd); -#endif + lp = (LPARAM)infoPtr->u.icon; break; case BS_BITMAP: flags |= DST_BITMAP; -#ifdef _USER32_ - lp = GetWindowLongPtrW( hwnd, HIMAGE_GWL_OFFSET ); -#else - lp = get_button_image(hwnd); -#endif + lp = (LPARAM)infoPtr->u.bitmap; break; default: return; } +#ifdef __REACTOS__ + DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rcText.left, rcText.top, + rcText.right - rcText.left, rcText.bottom - rcText.top, flags); +#else DrawStateW(hdc, hbr, lpOutputProc, lp, wp, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, flags); - HeapFree( GetProcessHeap(), 0, text ); +#endif + heap_free( text ); } /********************************************************************** * Push Button Functions */ -static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) +static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) { RECT rc, r; UINT dtFlags, uState; - HPEN hOldPen; + HPEN hOldPen, hpen; HBRUSH hOldBrush; INT oldBkMode; COLORREF oldTxtColor; HFONT hFont; - LONG state = get_button_state( hwnd ); - LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); + LONG state = infoPtr->state; + LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE ); BOOL pushedState = (state & BST_PUSHED); HWND parent; HRGN hrgn; -#ifndef _USER32_ +#ifdef __REACTOS__ DWORD cdrf; #endif - GetClientRect( hwnd, &rc ); + GetClientRect( infoPtr->hwnd, &rc ); /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */ - if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); - parent = GetParent(hwnd); - if (!parent) parent = hwnd; -#if defined(__REACTOS__) && defined(_USER32_) - GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); -#else - SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); -#endif + if ((hFont = infoPtr->font)) SelectObject( hDC, hFont ); + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd ); hrgn = set_control_clipping( hDC, &rc ); -#ifdef __REACTOS__ - hOldPen = SelectObject(hDC, GetStockObject(DC_PEN)); - SetDCPenColor(hDC, GetSysColor(COLOR_WINDOWFRAME)); -#else - hOldPen = SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME)); -#endif + + hpen = CreatePen( PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME)); + hOldPen = SelectObject(hDC, hpen); hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE)); oldBkMode = SetBkMode(hDC, TRANSPARENT); - /* completely skip the drawing if only focus has changed */ - if (action == ODA_FOCUS) goto draw_focus; - -#ifndef _USER32_ - cdrf = BUTTON_SendCustomDraw(hwnd, hDC, CDDS_PREERASE, &rc); +#ifdef __REACTOS__ + cdrf = BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_PREERASE, &rc); if (cdrf == CDRF_SKIPDEFAULT) goto cleanup; #endif @@ -1497,6 +1416,9 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) InflateRect( &rc, -1, -1 ); } + /* completely skip the drawing if only focus has changed */ + if (action == ODA_FOCUS) goto draw_focus; + uState = DFCS_BUTTONPUSH; if (style & BS_FLAT) @@ -1514,18 +1436,18 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) DrawFrameControl( hDC, &rc, DFC_BUTTON, uState ); -#ifndef _USER32_ +#ifdef __REACTOS__ if (cdrf == CDRF_NOTIFYPOSTERASE) - BUTTON_SendCustomDraw(hwnd, hDC, CDDS_POSTERASE, &rc); + BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_POSTERASE, &rc); - cdrf = BUTTON_SendCustomDraw(hwnd, hDC, CDDS_PREPAINT, &rc); + cdrf = BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_PREPAINT, &rc); if (cdrf == CDRF_SKIPDEFAULT) goto cleanup; #endif /* draw button label */ r = rc; - dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &r); + dtFlags = BUTTON_CalcLabelRect(infoPtr, hDC, &r); if (dtFlags == (UINT)-1L) goto cleanup; @@ -1535,20 +1457,20 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) ); - BUTTON_DrawLabel(hwnd, hDC, dtFlags, &r); + BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &r); SetTextColor( hDC, oldTxtColor ); -#ifndef _USER32_ +#ifdef __REACTOS__ if (cdrf == CDRF_NOTIFYPOSTPAINT) - BUTTON_SendCustomDraw(hwnd, hDC, CDDS_POSTPAINT, &rc); + BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_POSTPAINT, &rc); #endif draw_focus: if (action == ODA_FOCUS || (state & BST_FOCUS)) { #ifdef __REACTOS__ - if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) + if (!(infoPtr->ui_state & UISF_HIDEFOCUS)) { #endif InflateRect( &rc, -2, -2 ); @@ -1564,64 +1486,57 @@ draw_focus: SetBkMode(hDC, oldBkMode); SelectClipRgn( hDC, hrgn ); if (hrgn) DeleteObject( hrgn ); + DeleteObject( hpen ); } /********************************************************************** * Check Box & Radio Button Functions */ -static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) +static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) { RECT rbox, rtext, client; HBRUSH hBrush; int delta, text_offset, checkBoxWidth, checkBoxHeight; UINT dtFlags; HFONT hFont; - LONG state = get_button_state( hwnd ); - LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); - LONG ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE ); + LONG state = infoPtr->state; + LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE ); + LONG ex_style = GetWindowLongW( infoPtr->hwnd, GWL_EXSTYLE ); HWND parent; HRGN hrgn; if (style & BS_PUSHLIKE) { - PB_Paint( hwnd, hDC, action ); + PB_Paint( infoPtr, hDC, action ); return; } - GetClientRect(hwnd, &client); + GetClientRect(infoPtr->hwnd, &client); rbox = rtext = client; checkBoxWidth = 12 * GetDeviceCaps( hDC, LOGPIXELSX ) / 96 + 1; checkBoxHeight = 12 * GetDeviceCaps( hDC, LOGPIXELSY ) / 96 + 1; - if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); + if ((hFont = infoPtr->font)) SelectObject( hDC, hFont ); GetCharWidthW( hDC, '0', '0', &text_offset ); text_offset /= 2; - parent = GetParent(hwnd); - if (!parent) parent = hwnd; -#if defined(__REACTOS__) && defined(_USER32_) - hBrush = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC); -#else - hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, - (WPARAM)hDC, (LPARAM)hwnd); + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd); if (!hBrush) /* did the app forget to call defwindowproc ? */ - hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, - (WPARAM)hDC, (LPARAM)hwnd ); -#endif + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd); hrgn = set_control_clipping( hDC, &client ); if (style & BS_LEFTTEXT || ex_style & WS_EX_RIGHT) { - /* magic +4 is what CTL3D expects */ - - rtext.right -= checkBoxWidth + text_offset;; + rtext.right -= checkBoxWidth + text_offset; rbox.left = rbox.right - checkBoxWidth; } else { - rtext.left += checkBoxWidth + text_offset;; + rtext.left += checkBoxWidth + text_offset; rbox.right = checkBoxWidth; } @@ -1631,8 +1546,8 @@ static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) /* Draw label */ client = rtext; - dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rtext); - + dtFlags = BUTTON_CalcLabelRect(infoPtr, hDC, &rtext); + /* Only adjust rbox when rtext is valid */ if (dtFlags != (UINT)-1L) { @@ -1657,11 +1572,11 @@ static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) /* rbox must have the correct height */ delta = rbox.bottom - rbox.top - checkBoxHeight; - + if (style & BS_TOP) { if (delta > 0) { rbox.bottom = rbox.top + checkBoxHeight; - } else { + } else { rbox.top -= -delta/2 + 1; rbox.bottom = rbox.top + checkBoxHeight; } @@ -1691,22 +1606,15 @@ static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) return; if (action == ODA_DRAWENTIRE) - BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rtext); + BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &rtext); /* ... and focus */ if (action == ODA_FOCUS || (state & BST_FOCUS)) { -#ifdef __REACTOS__ - if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) - { -#endif - rtext.left--; - rtext.right++; - IntersectRect(&rtext, &rtext, &client); - DrawFocusRect( hDC, &rtext ); -#ifdef __REACTOS__ - } -#endif + rtext.left--; + rtext.right++; + IntersectRect(&rtext, &rtext, &client); + DrawFocusRect( hDC, &rtext ); } SelectClipRgn( hDC, hrgn ); if (hrgn) DeleteObject( hrgn ); @@ -1729,7 +1637,7 @@ static void BUTTON_CheckAutoRadioButton( HWND hwnd ) { if (!sibling) break; if ((hwnd != sibling) && - ((GetWindowLongPtrW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON)) + ((GetWindowLongW( sibling, GWL_STYLE) & BS_TYPEMASK) == BS_AUTORADIOBUTTON)) SendMessageW( sibling, BM_SETCHECK, BST_UNCHECKED, 0 ); sibling = GetNextDlgGroupItem( parent, sibling, FALSE ); } while (sibling != start); @@ -1740,30 +1648,25 @@ static void BUTTON_CheckAutoRadioButton( HWND hwnd ) * Group Box Functions */ -static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) +static void GB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) { RECT rc, rcFrame; HBRUSH hbr; HFONT hFont; UINT dtFlags; TEXTMETRICW tm; - LONG style = GetWindowLongPtrW( hwnd, GWL_STYLE ); + LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE ); HWND parent; HRGN hrgn; - if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); + if ((hFont = infoPtr->font)) SelectObject( hDC, hFont ); /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */ - parent = GetParent(hwnd); - if (!parent) parent = hwnd; -#if defined(__REACTOS__) && defined(_USER32_) - hbr = GetControlColor(parent, hwnd, hDC, WM_CTLCOLORSTATIC); -#else - hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd); if (!hbr) /* did the app forget to call defwindowproc ? */ - hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, - (WPARAM)hDC, (LPARAM)hwnd); -#endif - GetClientRect( hwnd, &rc); + hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)infoPtr->hwnd); + GetClientRect( infoPtr->hwnd, &rc); rcFrame = rc; hrgn = set_control_clipping( hDC, &rc ); @@ -1772,9 +1675,9 @@ static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) DrawEdge (hDC, &rcFrame, EDGE_ETCHED, BF_RECT | ((style & BS_FLAT) ? BF_FLAT : 0)); InflateRect(&rc, -7, 1); - dtFlags = BUTTON_CalcLabelRect(hwnd, hDC, &rc); + dtFlags = BUTTON_CalcLabelRect(infoPtr, hDC, &rc); - if (dtFlags != (UINT)-1L) + if (dtFlags != (UINT)-1) { /* Because buttons have CS_PARENTDC class style, there is a chance * that label will be drawn out of client rect. @@ -1786,7 +1689,7 @@ static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) FillRect(hDC, &rc, hbr); rc.left++; rc.right--; rc.bottom--; - BUTTON_DrawLabel(hwnd, hDC, dtFlags, &rc); + BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &rc); } SelectClipRgn( hDC, hrgn ); if (hrgn) DeleteObject( hrgn ); @@ -1797,52 +1700,39 @@ static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) * User Button Functions */ -static void UB_Paint( HWND hwnd, HDC hDC, UINT action ) +static void UB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) { RECT rc; HBRUSH hBrush; HFONT hFont; - LONG state = get_button_state( hwnd ); + LONG state = infoPtr->state; HWND parent; - GetClientRect( hwnd, &rc); + GetClientRect( infoPtr->hwnd, &rc); - if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); + if ((hFont = infoPtr->font)) SelectObject( hDC, hFont ); - parent = GetParent(hwnd); - if (!parent) parent = hwnd; -#if defined(__REACTOS__) && defined(_USER32_) - hBrush = GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); -#else - hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd); + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd); if (!hBrush) /* did the app forget to call defwindowproc ? */ - hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, - (WPARAM)hDC, (LPARAM)hwnd); -#endif + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd); FillRect( hDC, &rc, hBrush ); if (action == ODA_FOCUS || (state & BST_FOCUS)) -#ifdef __REACTOS__ - { - if (!(get_ui_state(hwnd) & UISF_HIDEFOCUS)) -#endif - DrawFocusRect( hDC, &rc ); -#ifdef __REACTOS__ - } -#endif + DrawFocusRect( hDC, &rc ); switch (action) { case ODA_FOCUS: - BUTTON_NOTIFY_PARENT( hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS ); + BUTTON_NOTIFY_PARENT( infoPtr->hwnd, (state & BST_FOCUS) ? BN_SETFOCUS : BN_KILLFOCUS ); break; case ODA_SELECT: - BUTTON_NOTIFY_PARENT( hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE ); + BUTTON_NOTIFY_PARENT( infoPtr->hwnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE ); break; default: - BUTTON_NOTIFY_PARENT( hwnd, BN_PAINT ); break; } } @@ -1852,13 +1742,13 @@ static void UB_Paint( HWND hwnd, HDC hDC, UINT action ) * Ownerdrawn Button Functions */ -static void OB_Paint( HWND hwnd, HDC hDC, UINT action ) +static void OB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) { - LONG state = get_button_state( hwnd ); + LONG state = infoPtr->state; DRAWITEMSTRUCT dis; - LONG_PTR id = GetWindowLongPtrW( hwnd, GWLP_ID ); + LONG_PTR id = GetWindowLongPtrW( infoPtr->hwnd, GWLP_ID ); HWND parent; - HFONT hFont, hPrevFont = 0; + HFONT hFont; HRGN hrgn; dis.CtlType = ODT_BUTTON; @@ -1867,46 +1757,349 @@ static void OB_Paint( HWND hwnd, HDC hDC, UINT action ) dis.itemAction = action; dis.itemState = ((state & BST_FOCUS) ? ODS_FOCUS : 0) | ((state & BST_PUSHED) ? ODS_SELECTED : 0) | - (IsWindowEnabled(hwnd) ? 0: ODS_DISABLED); - dis.hwndItem = hwnd; + (IsWindowEnabled(infoPtr->hwnd) ? 0: ODS_DISABLED); + dis.hwndItem = infoPtr->hwnd; dis.hDC = hDC; dis.itemData = 0; - GetClientRect( hwnd, &dis.rcItem ); + GetClientRect( infoPtr->hwnd, &dis.rcItem ); - if ((hFont = get_button_font( hwnd ))) hPrevFont = SelectObject( hDC, hFont ); - parent = GetParent(hwnd); - if (!parent) parent = hwnd; -#if defined(__REACTOS__) && defined(_USER32_) - GetControlColor( parent, hwnd, hDC, WM_CTLCOLORBTN); -#else - SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); -#endif + if ((hFont = infoPtr->font)) SelectObject( hDC, hFont ); + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd ); hrgn = set_control_clipping( hDC, &dis.rcItem ); - SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); - if (hPrevFont) SelectObject(hDC, hPrevFont); + SendMessageW( GetParent(infoPtr->hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); SelectClipRgn( hDC, hrgn ); if (hrgn) DeleteObject( hrgn ); } -#ifndef _USER32_ -void BUTTON_Register() +#ifdef __REACTOS__ /* r73885 */ +static void PB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, ButtonState drawState, UINT dtFlags, BOOL focused, LPARAM prfFlag) +#else +static void PB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, ButtonState drawState, UINT dtFlags, BOOL focused) +#endif +{ + static const int states[] = { PBS_NORMAL, PBS_DISABLED, PBS_HOT, PBS_PRESSED, PBS_DEFAULTED }; + + RECT bgRect, textRect; + HFONT font = infoPtr->font; + HFONT hPrevFont = font ? SelectObject(hDC, font) : NULL; + int state = states[ drawState ]; + WCHAR *text = get_button_text(infoPtr); +#ifdef __REACTOS__ + HWND parent; + DWORD cdrf; + + GetClientRect(infoPtr->hwnd, &bgRect); + GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &bgRect, &textRect); + + if (prfFlag == 0) + { + if (IsThemeBackgroundPartiallyTransparent(theme, BP_PUSHBUTTON, state)) + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + } + + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd ); + + cdrf = BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_PREERASE, &bgRect); + if (cdrf == CDRF_SKIPDEFAULT) + goto cleanup; + + DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &bgRect, NULL); + + if (cdrf == CDRF_NOTIFYPOSTERASE) + BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_POSTERASE, &bgRect); + + cdrf = BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_PREPAINT, &bgRect); + if (cdrf == CDRF_SKIPDEFAULT) + goto cleanup; + + BUTTON_DrawIml(hDC, &infoPtr->imlData, &textRect, FALSE, drawState); +#else + GetClientRect(infoPtr->hwnd, &bgRect); + GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &bgRect, &textRect); + + if (IsThemeBackgroundPartiallyTransparent(theme, BP_PUSHBUTTON, state)) + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + + DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &bgRect, NULL); +#endif + + if (text) + { + DrawThemeText(theme, hDC, BP_PUSHBUTTON, state, text, lstrlenW(text), dtFlags, 0, &textRect); + heap_free(text); + } + + if (focused) + { + MARGINS margins; + RECT focusRect = bgRect; + + GetThemeMargins(theme, hDC, BP_PUSHBUTTON, state, TMT_CONTENTMARGINS, NULL, &margins); + + focusRect.left += margins.cxLeftWidth; + focusRect.top += margins.cyTopHeight; + focusRect.right -= margins.cxRightWidth; + focusRect.bottom -= margins.cyBottomHeight; + + DrawFocusRect( hDC, &focusRect ); + } + +#ifdef __REACTOS__ + if (cdrf == CDRF_NOTIFYPOSTPAINT) + BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_POSTPAINT, &bgRect); +cleanup: +#endif + if (hPrevFont) SelectObject(hDC, hPrevFont); +} + +#ifdef __REACTOS__ /* r73885 */ +static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, ButtonState drawState, UINT dtFlags, BOOL focused, LPARAM prfFlag) +#else +static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, ButtonState drawState, UINT dtFlags, BOOL focused) +#endif +{ + static const int cb_states[3][5] = + { + { CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED, CBS_UNCHECKEDDISABLED, CBS_UNCHECKEDNORMAL }, + { CBS_CHECKEDNORMAL, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED, CBS_CHECKEDDISABLED, CBS_CHECKEDNORMAL }, + { CBS_MIXEDNORMAL, CBS_MIXEDHOT, CBS_MIXEDPRESSED, CBS_MIXEDDISABLED, CBS_MIXEDNORMAL } + }; + + static const int rb_states[2][5] = + { + { RBS_UNCHECKEDNORMAL, RBS_UNCHECKEDHOT, RBS_UNCHECKEDPRESSED, RBS_UNCHECKEDDISABLED, RBS_UNCHECKEDNORMAL }, + { RBS_CHECKEDNORMAL, RBS_CHECKEDHOT, RBS_CHECKEDPRESSED, RBS_CHECKEDDISABLED, RBS_CHECKEDNORMAL } + }; + + SIZE sz; + RECT bgRect, textRect; + HFONT font, hPrevFont = NULL; + int checkState = infoPtr->state & 3; + DWORD dwStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); + UINT btn_type = get_button_type( dwStyle ); + int part = (btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON) ? BP_RADIOBUTTON : BP_CHECKBOX; + int state = (part == BP_CHECKBOX) + ? cb_states[ checkState ][ drawState ] + : rb_states[ checkState ][ drawState ]; + WCHAR *text = get_button_text(infoPtr); + LOGFONTW lf; + BOOL created_font = FALSE; +#ifdef __REACTOS__ + HWND parent; + HBRUSH hBrush; + DWORD cdrf; +#endif + + HRESULT hr = GetThemeFont(theme, hDC, part, state, TMT_FONT, &lf); + if (SUCCEEDED(hr)) { + font = CreateFontIndirectW(&lf); + if (!font) + TRACE("Failed to create font\n"); + else { + TRACE("font = %s\n", debugstr_w(lf.lfFaceName)); + hPrevFont = SelectObject(hDC, font); + created_font = TRUE; + } + } else { +#ifdef __REACTOS__ /* r73885 */ + font = infoPtr->font; +#else + font = (HFONT)SendMessageW(infoPtr->hwnd, WM_GETFONT, 0, 0); +#endif + hPrevFont = SelectObject(hDC, font); + } + + if (FAILED(GetThemePartSize(theme, hDC, part, state, NULL, TS_DRAW, &sz))) + sz.cx = sz.cy = 13; + + GetClientRect(infoPtr->hwnd, &bgRect); + +#ifdef __REACTOS__ + if (prfFlag == 0) + { + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + } + + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, + (WPARAM)hDC, (LPARAM)infoPtr->hwnd); + if (!hBrush) /* did the app forget to call defwindowproc ? */ + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, + (WPARAM)hDC, (LPARAM)infoPtr->hwnd ); + FillRect( hDC, &bgRect, hBrush ); + + cdrf = BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_PREERASE, &bgRect); + if (cdrf == CDRF_SKIPDEFAULT) + goto cleanup; +#endif + + GetThemeBackgroundContentRect(theme, hDC, part, state, &bgRect, &textRect); + + if (dtFlags & DT_SINGLELINE) /* Center the checkbox / radio button to the text. */ + bgRect.top = bgRect.top + (textRect.bottom - textRect.top - sz.cy) / 2; + + /* adjust for the check/radio marker */ + bgRect.bottom = bgRect.top + sz.cy; + bgRect.right = bgRect.left + sz.cx; + textRect.left = bgRect.right + 6; + +#ifdef __REACTOS__ + DrawThemeBackground(theme, hDC, part, state, &bgRect, NULL); + + if (cdrf == CDRF_NOTIFYPOSTERASE) + BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_POSTERASE, &bgRect); + + cdrf = BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_PREPAINT, &bgRect); + if (cdrf == CDRF_SKIPDEFAULT) + goto cleanup; + +#else + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + DrawThemeBackground(theme, hDC, part, state, &bgRect, NULL); +#endif + if (text) + { + DrawThemeText(theme, hDC, part, state, text, lstrlenW(text), dtFlags, 0, &textRect); + + if (focused) + { + RECT focusRect; + + focusRect = textRect; + + DrawTextW(hDC, text, lstrlenW(text), &focusRect, dtFlags | DT_CALCRECT); + + if (focusRect.right < textRect.right) focusRect.right++; + focusRect.bottom = textRect.bottom; + + DrawFocusRect( hDC, &focusRect ); + } + + heap_free(text); + } + +#ifdef __REACTOS__ + if (cdrf == CDRF_NOTIFYPOSTPAINT) + BUTTON_SendCustomDraw(infoPtr, hDC, CDDS_POSTPAINT, &bgRect); +cleanup: +#endif + if (created_font) DeleteObject(font); + if (hPrevFont) SelectObject(hDC, hPrevFont); +} + +#ifdef __REACTOS__ /* r73885 */ +static void GB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, ButtonState drawState, UINT dtFlags, BOOL focused, LPARAM prfFlag) +#else +static void GB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, ButtonState drawState, UINT dtFlags, BOOL focused) +#endif +{ + static const int states[] = { GBS_NORMAL, GBS_DISABLED, GBS_NORMAL, GBS_NORMAL, GBS_NORMAL }; + + RECT bgRect, textRect, contentRect; + int state = states[ drawState ]; + WCHAR *text = get_button_text(infoPtr); + LOGFONTW lf; + HFONT font, hPrevFont = NULL; + BOOL created_font = FALSE; +#ifdef __REACTOS__ /* r74406 */ + HWND parent; + HBRUSH hBrush; + RECT clientRect; +#endif + + HRESULT hr = GetThemeFont(theme, hDC, BP_GROUPBOX, state, TMT_FONT, &lf); + if (SUCCEEDED(hr)) { + font = CreateFontIndirectW(&lf); + if (!font) + TRACE("Failed to create font\n"); + else { + hPrevFont = SelectObject(hDC, font); + created_font = TRUE; + } + } else { +#ifdef __REACTOS__ /* r73885 */ + font = infoPtr->font; +#else + font = (HFONT)SendMessageW(infoPtr->hwnd, WM_GETFONT, 0, 0); +#endif + hPrevFont = SelectObject(hDC, font); + } + + GetClientRect(infoPtr->hwnd, &bgRect); + textRect = bgRect; + + if (text) + { + SIZE textExtent; + GetTextExtentPoint32W(hDC, text, lstrlenW(text), &textExtent); + bgRect.top += (textExtent.cy / 2); + textRect.left += 10; + textRect.bottom = textRect.top + textExtent.cy; + textRect.right = textRect.left + textExtent.cx + 4; + + ExcludeClipRect(hDC, textRect.left, textRect.top, textRect.right, textRect.bottom); + } + + GetThemeBackgroundContentRect(theme, hDC, BP_GROUPBOX, state, &bgRect, &contentRect); + ExcludeClipRect(hDC, contentRect.left, contentRect.top, contentRect.right, contentRect.bottom); + +#ifdef __REACTOS__ /* r73885 & r74149 */ + if (prfFlag == 0) +#endif + if (IsThemeBackgroundPartiallyTransparent(theme, BP_GROUPBOX, state)) + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + +#ifdef __REACTOS__ /* r74406 */ + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, + (WPARAM)hDC, (LPARAM)infoPtr->hwnd); + if (!hBrush) /* did the app forget to call defwindowproc ? */ + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, + (WPARAM)hDC, (LPARAM)infoPtr->hwnd ); + GetClientRect(infoPtr->hwnd, &clientRect); + FillRect( hDC, &clientRect, hBrush ); +#endif + + DrawThemeBackground(theme, hDC, BP_GROUPBOX, state, &bgRect, NULL); + + SelectClipRgn(hDC, NULL); + + if (text) + { + InflateRect(&textRect, -2, 0); + DrawThemeText(theme, hDC, BP_GROUPBOX, state, text, lstrlenW(text), 0, 0, &textRect); + heap_free(text); + } + + if (created_font) DeleteObject(font); + if (hPrevFont) SelectObject(hDC, hPrevFont); +} + +void BUTTON_Register(void) { WNDCLASSW wndClass; - ZeroMemory(&wndClass, sizeof(WNDCLASSW)); - wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC; - wndClass.lpfnWndProc = ButtonWndProcW; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = sizeof(PBUTTON_DATA); - wndClass.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); - wndClass.hbrBackground = 0; + memset(&wndClass, 0, sizeof(wndClass)); + wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC; + wndClass.lpfnWndProc = BUTTON_WindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = sizeof(BUTTON_INFO *); + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; wndClass.lpszClassName = WC_BUTTONW; - RegisterClassW(&wndClass); } + +#ifdef __REACTOS__ void BUTTON_Unregister() { UnregisterClassW(WC_BUTTONW, NULL); diff --git a/dll/win32/comctl32/combo.c b/dll/win32/comctl32/combo.c new file mode 100644 index 00000000000..712e27276a5 --- /dev/null +++ b/dll/win32/comctl32/combo.c @@ -0,0 +1,2160 @@ +/* + * Combo controls + * + * Copyright 1997 Alex Korobka + * Copyright (c) 2005 by Frank Richter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * TODO: + * - ComboBox_[GS]etMinVisible() + * - CB_GETMINVISIBLE, CB_SETMINVISIBLE + * - CB_SETTOPINDEX + */ + +#include +#include + +#define OEMRESOURCE + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "commctrl.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/heap.h" + +#include "comctl32.h" + +WINE_DEFAULT_DEBUG_CHANNEL(combo); + + /* bits in the dwKeyData */ +#define KEYDATA_ALT 0x2000 +#define KEYDATA_PREVSTATE 0x4000 + +/* + * Additional combo box definitions + */ + +#define CB_NOTIFY( lphc, code ) \ + (SendMessageW((lphc)->owner, WM_COMMAND, \ + MAKEWPARAM(GetWindowLongPtrW((lphc)->self,GWLP_ID), (code)), (LPARAM)(lphc)->self)) + +#define CB_DISABLED( lphc ) (!IsWindowEnabled((lphc)->self)) +#define CB_OWNERDRAWN( lphc ) ((lphc)->dwStyle & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) +#define CB_HASSTRINGS( lphc ) ((lphc)->dwStyle & CBS_HASSTRINGS) +#define CB_HWND( lphc ) ((lphc)->self) +#define CB_GETTYPE( lphc ) ((lphc)->dwStyle & (CBS_DROPDOWNLIST)) + +#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03) + +/* + * Drawing globals + */ +static HBITMAP hComboBmp = 0; +static UINT CBitHeight, CBitWidth; + +/* + * Look and feel dependent "constants" + */ + +#define COMBO_YBORDERGAP 5 +#define COMBO_XBORDERSIZE() 2 +#define COMBO_YBORDERSIZE() 2 +#define COMBO_EDITBUTTONSPACE() 0 +#define EDIT_CONTROL_PADDING() 1 + +#define ID_CB_LISTBOX 1000 +#define ID_CB_EDIT 1001 + +/*********************************************************************** + * COMBO_Init + * + * Load combo button bitmap. + */ +static BOOL COMBO_Init(void) +{ + HDC hDC; + + if( hComboBmp ) return TRUE; + if( (hDC = CreateCompatibleDC(0)) ) + { + BOOL bRet = FALSE; + if( (hComboBmp = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_COMBO))) ) + { + BITMAP bm; + HBITMAP hPrevB; + RECT r; + + GetObjectW( hComboBmp, sizeof(bm), &bm ); + CBitHeight = bm.bmHeight; + CBitWidth = bm.bmWidth; + + TRACE("combo bitmap [%i,%i]\n", CBitWidth, CBitHeight ); + + hPrevB = SelectObject( hDC, hComboBmp); + SetRect( &r, 0, 0, CBitWidth, CBitHeight ); + InvertRect( hDC, &r ); + SelectObject( hDC, hPrevB ); + bRet = TRUE; + } + DeleteDC( hDC ); + return bRet; + } + return FALSE; +} + +/*********************************************************************** + * COMBO_NCCreate + */ +static LRESULT COMBO_NCCreate(HWND hwnd, LONG style) +{ + HEADCOMBO *lphc; + + if (COMBO_Init() && (lphc = heap_alloc_zero(sizeof(*lphc)))) + { + lphc->self = hwnd; + SetWindowLongPtrW( hwnd, 0, (LONG_PTR)lphc ); + + /* some braindead apps do try to use scrollbar/border flags */ + + lphc->dwStyle = style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL); + SetWindowLongW( hwnd, GWL_STYLE, style & ~(WS_BORDER | WS_HSCROLL | WS_VSCROLL) ); + + /* + * We also have to remove the client edge style to make sure + * we don't end-up with a non client area. + */ + SetWindowLongW( hwnd, GWL_EXSTYLE, + GetWindowLongW( hwnd, GWL_EXSTYLE ) & ~WS_EX_CLIENTEDGE ); + + if( !(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) ) + lphc->dwStyle |= CBS_HASSTRINGS; + if( !(GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) ) + lphc->wState |= CBF_NOTIFY; + + TRACE("[%p], style = %08x\n", lphc, lphc->dwStyle ); + return TRUE; + } + return FALSE; +} + +/*********************************************************************** + * COMBO_NCDestroy + */ +static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc ) +{ + if (lphc) + { + TRACE("[%p]: freeing storage\n", lphc->self); + + if ( (CB_GETTYPE(lphc) != CBS_SIMPLE) && lphc->hWndLBox ) + DestroyWindow( lphc->hWndLBox ); + + SetWindowLongPtrW( lphc->self, 0, 0 ); + heap_free( lphc ); + } + + return 0; +} + +/*********************************************************************** + * CBGetTextAreaHeight + * + * This method will calculate the height of the text area of the + * combobox. + * The height of the text area is set in two ways. + * It can be set explicitly through a combobox message or through a + * WM_MEASUREITEM callback. + * If this is not the case, the height is set to font height + 4px + * This height was determined through experimentation. + * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border + */ +static INT CBGetTextAreaHeight( + HWND hwnd, + LPHEADCOMBO lphc) +{ + INT iTextItemHeight; + + if( lphc->editHeight ) /* explicitly set height */ + { + iTextItemHeight = lphc->editHeight; + } + else + { + TEXTMETRICW tm; + HDC hDC = GetDC(hwnd); + HFONT hPrevFont = 0; + INT baseUnitY; + + if (lphc->hFont) + hPrevFont = SelectObject( hDC, lphc->hFont ); + + GetTextMetricsW(hDC, &tm); + + baseUnitY = tm.tmHeight; + + if( hPrevFont ) + SelectObject( hDC, hPrevFont ); + + ReleaseDC(hwnd, hDC); + + iTextItemHeight = baseUnitY + 4; + } + + /* + * Check the ownerdraw case if we haven't asked the parent the size + * of the item yet. + */ + if ( CB_OWNERDRAWN(lphc) && + (lphc->wState & CBF_MEASUREITEM) ) + { + MEASUREITEMSTRUCT measureItem; + RECT clientRect; + INT originalItemHeight = iTextItemHeight; + UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID ); + + /* + * We use the client rect for the width of the item. + */ + GetClientRect(hwnd, &clientRect); + + lphc->wState &= ~CBF_MEASUREITEM; + + /* + * Send a first one to measure the size of the text area + */ + measureItem.CtlType = ODT_COMBOBOX; + measureItem.CtlID = id; + measureItem.itemID = -1; + measureItem.itemWidth = clientRect.right; + measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */ + measureItem.itemData = 0; + SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem); + iTextItemHeight = 6 + measureItem.itemHeight; + + /* + * Send a second one in the case of a fixed ownerdraw list to calculate the + * size of the list items. (we basically do this on behalf of the listbox) + */ + if (lphc->dwStyle & CBS_OWNERDRAWFIXED) + { + measureItem.CtlType = ODT_COMBOBOX; + measureItem.CtlID = id; + measureItem.itemID = 0; + measureItem.itemWidth = clientRect.right; + measureItem.itemHeight = originalItemHeight; + measureItem.itemData = 0; + SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem); + lphc->fixedOwnerDrawHeight = measureItem.itemHeight; + } + + /* + * Keep the size for the next time + */ + lphc->editHeight = iTextItemHeight; + } + + return iTextItemHeight; +} + +/*********************************************************************** + * CBForceDummyResize + * + * The dummy resize is used for listboxes that have a popup to trigger + * a re-arranging of the contents of the combobox and the recalculation + * of the size of the "real" control window. + */ +static void CBForceDummyResize( + LPHEADCOMBO lphc) +{ + RECT windowRect; + int newComboHeight; + + newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE(); + + GetWindowRect(lphc->self, &windowRect); + + /* + * We have to be careful, resizing a combobox also has the meaning that the + * dropped rect will be resized. In this case, we want to trigger a resize + * to recalculate layout but we don't want to change the dropped rectangle + * So, we pass the height of text area of control as the height. + * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING + * message. + */ + SetWindowPos( lphc->self, + NULL, + 0, 0, + windowRect.right - windowRect.left, + newComboHeight, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE ); +} + +/*********************************************************************** + * CBCalcPlacement + * + * Set up component coordinates given valid lphc->RectCombo. + */ +static void CBCalcPlacement( + HWND hwnd, + LPHEADCOMBO lphc, + LPRECT lprEdit, + LPRECT lprButton, + LPRECT lprLB) +{ + /* + * Again, start with the client rectangle. + */ + GetClientRect(hwnd, lprEdit); + + /* + * Remove the borders + */ + InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE()); + + /* + * Chop off the bottom part to fit with the height of the text area. + */ + lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc); + + /* + * The button starts the same vertical position as the text area. + */ + CopyRect(lprButton, lprEdit); + + /* + * If the combobox is "simple" there is no button. + */ + if( CB_GETTYPE(lphc) == CBS_SIMPLE ) + lprButton->left = lprButton->right = lprButton->bottom = 0; + else + { + /* + * Let's assume the combobox button is the same width as the + * scrollbar button. + * size the button horizontally and cut-off the text area. + */ + lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL); + lprEdit->right = lprButton->left; + } + + /* + * In the case of a dropdown, there is an additional spacing between the + * text area and the button. + */ + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + { + lprEdit->right -= COMBO_EDITBUTTONSPACE(); + } + + /* + * If we have an edit control, we space it away from the borders slightly. + */ + if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST) + { + InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING()); + } + + /* + * Adjust the size of the listbox popup. + */ + if( CB_GETTYPE(lphc) == CBS_SIMPLE ) + { + /* + * Use the client rectangle to initialize the listbox rectangle + */ + GetClientRect(hwnd, lprLB); + + /* + * Then, chop-off the top part. + */ + lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE(); + } + else + { + /* + * Make sure the dropped width is as large as the combobox itself. + */ + if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE())) + { + lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE()); + + /* + * In the case of a dropdown, the popup listbox is offset to the right. + * so, we want to make sure it's flush with the right side of the + * combobox + */ + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + lprLB->right -= COMBO_EDITBUTTONSPACE(); + } + else + lprLB->right = lprLB->left + lphc->droppedWidth; + } + + /* don't allow negative window width */ + if (lprEdit->right < lprEdit->left) + lprEdit->right = lprEdit->left; + + TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit)); + + TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton)); + + TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB)); +} + +/*********************************************************************** + * CBGetDroppedControlRect + */ +static void CBGetDroppedControlRect( LPHEADCOMBO lphc, LPRECT lpRect) +{ + /* In windows, CB_GETDROPPEDCONTROLRECT returns the upper left corner + of the combo box and the lower right corner of the listbox */ + + GetWindowRect(lphc->self, lpRect); + + lpRect->right = lpRect->left + lphc->droppedRect.right - lphc->droppedRect.left; + lpRect->bottom = lpRect->top + lphc->droppedRect.bottom - lphc->droppedRect.top; + +} + +/*********************************************************************** + * COMBO_Create + */ +static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG style ) +{ + static const WCHAR clbName[] = {'C','o','m','b','o','L','B','o','x',0}; + static const WCHAR editName[] = {'E','d','i','t',0}; + + OpenThemeData( hwnd, WC_COMBOBOXW ); + if( !CB_GETTYPE(lphc) ) lphc->dwStyle |= CBS_SIMPLE; + if( CB_GETTYPE(lphc) != CBS_DROPDOWNLIST ) lphc->wState |= CBF_EDIT; + + lphc->owner = hwndParent; + + /* + * The item height and dropped width are not set when the control + * is created. + */ + lphc->droppedWidth = lphc->editHeight = 0; + + /* + * The first time we go through, we want to measure the ownerdraw item + */ + lphc->wState |= CBF_MEASUREITEM; + + /* M$ IE 3.01 actually creates (and rapidly destroys) an ownerless combobox */ + + if( lphc->owner || !(style & WS_VISIBLE) ) + { + UINT lbeStyle = 0; + UINT lbeExStyle = 0; + + /* + * Initialize the dropped rect to the size of the client area of the + * control and then, force all the areas of the combobox to be + * recalculated. + */ + GetClientRect( hwnd, &lphc->droppedRect ); + CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect ); + + /* + * Adjust the position of the popup listbox if it's necessary + */ + if ( CB_GETTYPE(lphc) != CBS_SIMPLE ) + { + lphc->droppedRect.top = lphc->textRect.bottom + COMBO_YBORDERSIZE(); + + /* + * If it's a dropdown, the listbox is offset + */ + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + lphc->droppedRect.left += COMBO_EDITBUTTONSPACE(); + + if (lphc->droppedRect.bottom < lphc->droppedRect.top) + lphc->droppedRect.bottom = lphc->droppedRect.top; + if (lphc->droppedRect.right < lphc->droppedRect.left) + lphc->droppedRect.right = lphc->droppedRect.left; + MapWindowPoints( hwnd, 0, (LPPOINT)&lphc->droppedRect, 2 ); + } + + /* create listbox popup */ + + lbeStyle = (LBS_NOTIFY | LBS_COMBOBOX | WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD) | + (style & (WS_VSCROLL | CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)); + + if( lphc->dwStyle & CBS_SORT ) + lbeStyle |= LBS_SORT; + if( lphc->dwStyle & CBS_HASSTRINGS ) + lbeStyle |= LBS_HASSTRINGS; + if( lphc->dwStyle & CBS_NOINTEGRALHEIGHT ) + lbeStyle |= LBS_NOINTEGRALHEIGHT; + if( lphc->dwStyle & CBS_DISABLENOSCROLL ) + lbeStyle |= LBS_DISABLENOSCROLL; + + if( CB_GETTYPE(lphc) == CBS_SIMPLE ) /* child listbox */ + { + lbeStyle |= WS_VISIBLE; + + /* + * In win 95 look n feel, the listbox in the simple combobox has + * the WS_EXCLIENTEDGE style instead of the WS_BORDER style. + */ + lbeStyle &= ~WS_BORDER; + lbeExStyle |= WS_EX_CLIENTEDGE; + } + else + { + lbeExStyle |= (WS_EX_TOPMOST | WS_EX_TOOLWINDOW); + } + + lphc->hWndLBox = CreateWindowExW(lbeExStyle, clbName, NULL, lbeStyle, + lphc->droppedRect.left, lphc->droppedRect.top, lphc->droppedRect.right - lphc->droppedRect.left, + lphc->droppedRect.bottom - lphc->droppedRect.top, hwnd, (HMENU)ID_CB_LISTBOX, + (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), lphc ); + if( lphc->hWndLBox ) + { + BOOL bEdit = TRUE; + lbeStyle = WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_COMBO; + + if( lphc->wState & CBF_EDIT ) + { + if( lphc->dwStyle & CBS_OEMCONVERT ) + lbeStyle |= ES_OEMCONVERT; + if( lphc->dwStyle & CBS_AUTOHSCROLL ) + lbeStyle |= ES_AUTOHSCROLL; + if( lphc->dwStyle & CBS_LOWERCASE ) + lbeStyle |= ES_LOWERCASE; + else if( lphc->dwStyle & CBS_UPPERCASE ) + lbeStyle |= ES_UPPERCASE; + + if (!IsWindowEnabled(hwnd)) lbeStyle |= WS_DISABLED; + + lphc->hWndEdit = CreateWindowExW(0, editName, NULL, lbeStyle, + lphc->textRect.left, lphc->textRect.top, + lphc->textRect.right - lphc->textRect.left, + lphc->textRect.bottom - lphc->textRect.top, + hwnd, (HMENU)ID_CB_EDIT, + (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ), NULL ); + if( !lphc->hWndEdit ) + bEdit = FALSE; + } + + if( bEdit ) + { + if( CB_GETTYPE(lphc) != CBS_SIMPLE ) + { + /* Now do the trick with parent */ + SetParent(lphc->hWndLBox, HWND_DESKTOP); + /* + * If the combo is a dropdown, we must resize the control + * to fit only the text area and button. To do this, + * we send a dummy resize and the WM_WINDOWPOSCHANGING message + * will take care of setting the height for us. + */ + CBForceDummyResize(lphc); + } + + TRACE("init done\n"); + return 0; + } + ERR("edit control failure.\n"); + } else ERR("listbox failure.\n"); + } else ERR("no owner for visible combo.\n"); + + /* CreateWindow() will send WM_NCDESTROY to cleanup */ + + return -1; +} + +/*********************************************************************** + * CBPaintButton + * + * Paint combo button (normal, pressed, and disabled states). + */ +static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton) +{ + UINT buttonState = DFCS_SCROLLCOMBOBOX; + + if( lphc->wState & CBF_NOREDRAW ) + return; + + + if (lphc->wState & CBF_BUTTONDOWN) + buttonState |= DFCS_PUSHED; + + if (CB_DISABLED(lphc)) + buttonState |= DFCS_INACTIVE; + + DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState); +} + +/*********************************************************************** + * COMBO_PrepareColors + * + * This method will sent the appropriate WM_CTLCOLOR message to + * prepare and setup the colors for the combo's DC. + * + * It also returns the brush to use for the background. + */ +static HBRUSH COMBO_PrepareColors( + LPHEADCOMBO lphc, + HDC hDC) +{ + HBRUSH hBkgBrush; + + /* + * Get the background brush for this control. + */ + if (CB_DISABLED(lphc)) + { + hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLORSTATIC, + (WPARAM)hDC, (LPARAM)lphc->self ); + + /* + * We have to change the text color since WM_CTLCOLORSTATIC will + * set it to the "enabled" color. This is the same behavior as the + * edit control + */ + SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT)); + } + else + { + /* FIXME: In which cases WM_CTLCOLORLISTBOX should be sent? */ + hBkgBrush = (HBRUSH)SendMessageW(lphc->owner, WM_CTLCOLOREDIT, + (WPARAM)hDC, (LPARAM)lphc->self ); + } + + /* + * Catch errors. + */ + if( !hBkgBrush ) + hBkgBrush = GetSysColorBrush(COLOR_WINDOW); + + return hBkgBrush; +} + +/*********************************************************************** + * CBPaintText + * + * Paint CBS_DROPDOWNLIST text field / update edit control contents. + */ +static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint) +{ + RECT rectEdit = lphc->textRect; + INT id, size = 0; + LPWSTR pText = NULL; + + TRACE("\n"); + + /* follow Windows combobox that sends a bunch of text + * inquiries to its listbox while processing WM_PAINT. */ + + if( (id = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0) ) != LB_ERR ) + { + size = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, id, 0); + if (size == LB_ERR) + FIXME("LB_ERR probably not handled yet\n"); + if ((pText = heap_alloc((size + 1) * sizeof(WCHAR)))) + { + /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */ + size=SendMessageW(lphc->hWndLBox, LB_GETTEXT, id, (LPARAM)pText); + pText[size] = '\0'; /* just in case */ + } else return; + } + + if( lphc->wState & CBF_EDIT ) + { + static const WCHAR empty_stringW[] = { 0 }; + if( CB_HASSTRINGS(lphc) ) SetWindowTextW( lphc->hWndEdit, pText ? pText : empty_stringW ); + if( lphc->wState & CBF_FOCUSED ) + SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, MAXLONG); + } + else if(!(lphc->wState & CBF_NOREDRAW) && IsWindowVisible( lphc->self )) + { + /* paint text field ourselves */ + HDC hdc = hdc_paint ? hdc_paint : GetDC(lphc->self); + UINT itemState = ODS_COMBOBOXEDIT; + HFONT hPrevFont = (lphc->hFont) ? SelectObject(hdc, lphc->hFont) : 0; + HBRUSH hPrevBrush, hBkgBrush; + + /* + * Give ourselves some space. + */ + InflateRect( &rectEdit, -1, -1 ); + + hBkgBrush = COMBO_PrepareColors( lphc, hdc ); + hPrevBrush = SelectObject( hdc, hBkgBrush ); + FillRect( hdc, &rectEdit, hBkgBrush ); + + if( CB_OWNERDRAWN(lphc) ) + { + DRAWITEMSTRUCT dis; + HRGN clipRegion; + UINT ctlid = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID ); + + /* setup state for DRAWITEM message. Owner will highlight */ + if ( (lphc->wState & CBF_FOCUSED) && + !(lphc->wState & CBF_DROPPED) ) + itemState |= ODS_SELECTED | ODS_FOCUS; + + if (!IsWindowEnabled(lphc->self)) itemState |= ODS_DISABLED; + + dis.CtlType = ODT_COMBOBOX; + dis.CtlID = ctlid; + dis.hwndItem = lphc->self; + dis.itemAction = ODA_DRAWENTIRE; + dis.itemID = id; + dis.itemState = itemState; + dis.hDC = hdc; + dis.rcItem = rectEdit; + dis.itemData = SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, id, 0); + + /* + * Clip the DC and have the parent draw the item. + */ + clipRegion = set_control_clipping( hdc, &rectEdit ); + + SendMessageW(lphc->owner, WM_DRAWITEM, ctlid, (LPARAM)&dis ); + + SelectClipRgn( hdc, clipRegion ); + if (clipRegion) DeleteObject( clipRegion ); + } + else + { + static const WCHAR empty_stringW[] = { 0 }; + + if ( (lphc->wState & CBF_FOCUSED) && + !(lphc->wState & CBF_DROPPED) ) { + + /* highlight */ + FillRect( hdc, &rectEdit, GetSysColorBrush(COLOR_HIGHLIGHT) ); + SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); + SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) ); + } + + ExtTextOutW( hdc, + rectEdit.left + 1, + rectEdit.top + 1, + ETO_OPAQUE | ETO_CLIPPED, + &rectEdit, + pText ? pText : empty_stringW , size, NULL ); + + if(lphc->wState & CBF_FOCUSED && !(lphc->wState & CBF_DROPPED)) + DrawFocusRect( hdc, &rectEdit ); + } + + if( hPrevFont ) + SelectObject(hdc, hPrevFont ); + + if( hPrevBrush ) + SelectObject( hdc, hPrevBrush ); + + if( !hdc_paint ) + ReleaseDC( lphc->self, hdc ); + } + + heap_free(pText); +} + +/*********************************************************************** + * CBPaintBorder + */ +static void CBPaintBorder( + HWND hwnd, + const HEADCOMBO *lphc, + HDC hdc) +{ + RECT clientRect; + + if (CB_GETTYPE(lphc) != CBS_SIMPLE) + { + GetClientRect(hwnd, &clientRect); + } + else + { + clientRect = lphc->textRect; + + InflateRect(&clientRect, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING()); + InflateRect(&clientRect, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE()); + } + + DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_RECT); +} + +static LRESULT COMBO_ThemedPaint(HTHEME theme, HEADCOMBO *lphc, HDC hdc) +{ + int button_state; + RECT frame; + + /* paint border */ + if (CB_GETTYPE(lphc) != CBS_SIMPLE) + GetClientRect(lphc->self, &frame); + else + { + frame = lphc->textRect; + InflateRect(&frame, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING()); + InflateRect(&frame, COMBO_XBORDERSIZE(), COMBO_YBORDERSIZE()); + } + + DrawThemeBackground(theme, hdc, 0, IsWindowEnabled(lphc->self) ? CBXS_NORMAL : CBXS_DISABLED, &frame, NULL); + + /* Paint button */ + if (!IsRectEmpty(&lphc->buttonRect)) + { + if (!IsWindowEnabled(lphc->self)) + button_state = CBXS_DISABLED; + else if (lphc->wState & CBF_BUTTONDOWN) + button_state = CBXS_PRESSED; + else if (lphc->wState & CBF_HOT) + button_state = CBXS_HOT; + else + button_state = CBXS_NORMAL; + DrawThemeBackground(theme, hdc, CP_DROPDOWNBUTTON, button_state, &lphc->buttonRect, NULL); + } + + if ((lphc->dwStyle & CBS_DROPDOWNLIST) == CBS_DROPDOWNLIST) + CBPaintText(lphc, hdc); + + return 0; +} + +/*********************************************************************** + * COMBO_Paint + */ +static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc) +{ + HBRUSH hPrevBrush, hBkgBrush; + + TRACE("hdc=%p\n", hdc); + + /* + * Retrieve the background brush and select it in the + * DC. + */ + hBkgBrush = COMBO_PrepareColors(lphc, hdc); + hPrevBrush = SelectObject(hdc, hBkgBrush); + if (!(lphc->wState & CBF_EDIT)) + FillRect(hdc, &lphc->textRect, hBkgBrush); + + /* + * In non 3.1 look, there is a sunken border on the combobox + */ + CBPaintBorder(lphc->self, lphc, hdc); + + if (!IsRectEmpty(&lphc->buttonRect)) + CBPaintButton(lphc, hdc, lphc->buttonRect); + + /* paint the edit control padding area */ + if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST) + { + RECT rPadEdit = lphc->textRect; + + InflateRect(&rPadEdit, EDIT_CONTROL_PADDING(), EDIT_CONTROL_PADDING()); + + FrameRect(hdc, &rPadEdit, GetSysColorBrush(COLOR_WINDOW)); + } + + if (!(lphc->wState & CBF_EDIT)) + CBPaintText( lphc, hdc ); + + if (hPrevBrush) + SelectObject( hdc, hPrevBrush ); + + return 0; +} + +/*********************************************************************** + * CBUpdateLBox + * + * Select listbox entry according to the contents of the edit control. + */ +static INT CBUpdateLBox( LPHEADCOMBO lphc, BOOL bSelect ) +{ + INT length, idx; + LPWSTR pText = NULL; + + idx = LB_ERR; + length = SendMessageW( lphc->hWndEdit, WM_GETTEXTLENGTH, 0, 0 ); + + if (length > 0) + pText = heap_alloc((length + 1) * sizeof(WCHAR)); + + TRACE("\t edit text length %i\n", length ); + + if( pText ) + { + GetWindowTextW( lphc->hWndEdit, pText, length + 1); + idx = SendMessageW(lphc->hWndLBox, LB_FINDSTRING, -1, (LPARAM)pText); + heap_free( pText ); + } + + SendMessageW(lphc->hWndLBox, LB_SETCURSEL, bSelect ? idx : -1, 0); + + /* probably superfluous but Windows sends this too */ + SendMessageW(lphc->hWndLBox, LB_SETCARETINDEX, idx < 0 ? 0 : idx, 0); + SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, idx < 0 ? 0 : idx, 0); + + return idx; +} + +/*********************************************************************** + * CBUpdateEdit + * + * Copy a listbox entry to the edit control. + */ +static void CBUpdateEdit( LPHEADCOMBO lphc , INT index ) +{ + INT length; + LPWSTR pText = NULL; + static const WCHAR empty_stringW[] = { 0 }; + + TRACE("\t %i\n", index ); + + if( index >= 0 ) /* got an entry */ + { + length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, index, 0); + if( length != LB_ERR) + { + if ((pText = heap_alloc((length + 1) * sizeof(WCHAR)))) + SendMessageW(lphc->hWndLBox, LB_GETTEXT, index, (LPARAM)pText); + } + } + + if( CB_HASSTRINGS(lphc) ) + { + lphc->wState |= (CBF_NOEDITNOTIFY | CBF_NOLBSELECT); + SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, pText ? (LPARAM)pText : (LPARAM)empty_stringW); + lphc->wState &= ~(CBF_NOEDITNOTIFY | CBF_NOLBSELECT); + } + + if( lphc->wState & CBF_FOCUSED ) + SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1); + + heap_free( pText ); +} + +/*********************************************************************** + * CBDropDown + * + * Show listbox popup. + */ +static void CBDropDown( LPHEADCOMBO lphc ) +{ + HMONITOR monitor; + MONITORINFO mon_info; + RECT rect,r; + int nItems; + int nDroppedHeight; + + TRACE("[%p]: drop down\n", lphc->self); + + CB_NOTIFY( lphc, CBN_DROPDOWN ); + + /* set selection */ + + lphc->wState |= CBF_DROPPED; + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + { + lphc->droppedIndex = CBUpdateLBox( lphc, TRUE ); + + /* Update edit only if item is in the list */ + if( !(lphc->wState & CBF_CAPTURE) && lphc->droppedIndex >= 0) + CBUpdateEdit( lphc, lphc->droppedIndex ); + } + else + { + lphc->droppedIndex = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); + + SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, + lphc->droppedIndex == LB_ERR ? 0 : lphc->droppedIndex, 0); + SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0); + } + + /* now set popup position */ + GetWindowRect( lphc->self, &rect ); + + /* + * If it's a dropdown, the listbox is offset + */ + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + rect.left += COMBO_EDITBUTTONSPACE(); + + /* if the dropped height is greater than the total height of the dropped + items list, then force the drop down list height to be the total height + of the items in the dropped list */ + + /* And Remove any extra space (Best Fit) */ + nDroppedHeight = lphc->droppedRect.bottom - lphc->droppedRect.top; + /* if listbox length has been set directly by its handle */ + GetWindowRect(lphc->hWndLBox, &r); + if (nDroppedHeight < r.bottom - r.top) + nDroppedHeight = r.bottom - r.top; + nItems = (int)SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0); + + if (nItems > 0) + { + int nHeight; + int nIHeight; + + nIHeight = (int)SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, 0, 0); + + nHeight = nIHeight*nItems; + + if (nHeight < nDroppedHeight - COMBO_YBORDERSIZE()) + nDroppedHeight = nHeight + COMBO_YBORDERSIZE(); + + if (nDroppedHeight < nHeight) + { + if (nItems < 5) + nDroppedHeight = (nItems+1)*nIHeight; + else if (nDroppedHeight < 6*nIHeight) + nDroppedHeight = 6*nIHeight; + } + } + + r.left = rect.left; + r.top = rect.bottom; + r.right = r.left + lphc->droppedRect.right - lphc->droppedRect.left; + r.bottom = r.top + nDroppedHeight; + + /*If height of dropped rectangle gets beyond a screen size it should go up, otherwise down.*/ + monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY ); + mon_info.cbSize = sizeof(mon_info); + GetMonitorInfoW( monitor, &mon_info ); + + if (r.bottom > mon_info.rcWork.bottom) + { + r.top = max( rect.top - nDroppedHeight, mon_info.rcWork.top ); + r.bottom = min( r.top + nDroppedHeight, mon_info.rcWork.bottom ); + } + + SetWindowPos( lphc->hWndLBox, HWND_TOPMOST, r.left, r.top, r.right - r.left, r.bottom - r.top, + SWP_NOACTIVATE | SWP_SHOWWINDOW ); + + + if( !(lphc->wState & CBF_NOREDRAW) ) + RedrawWindow( lphc->self, NULL, 0, RDW_INVALIDATE | + RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); + + EnableWindow( lphc->hWndLBox, TRUE ); + if (GetCapture() != lphc->self) + SetCapture(lphc->hWndLBox); +} + +/*********************************************************************** + * CBRollUp + * + * Hide listbox popup. + */ +static void CBRollUp( LPHEADCOMBO lphc, BOOL ok, BOOL bButton ) +{ + HWND hWnd = lphc->self; + + TRACE("[%p]: sel ok? [%i] dropped? [%i]\n", + lphc->self, ok, (INT)(lphc->wState & CBF_DROPPED)); + + CB_NOTIFY( lphc, (ok) ? CBN_SELENDOK : CBN_SELENDCANCEL ); + + if( IsWindow( hWnd ) && CB_GETTYPE(lphc) != CBS_SIMPLE ) + { + + if( lphc->wState & CBF_DROPPED ) + { + RECT rect; + + lphc->wState &= ~CBF_DROPPED; + ShowWindow( lphc->hWndLBox, SW_HIDE ); + + if(GetCapture() == lphc->hWndLBox) + { + ReleaseCapture(); + } + + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + { + rect = lphc->buttonRect; + } + else + { + if( bButton ) + { + UnionRect( &rect, + &lphc->buttonRect, + &lphc->textRect); + } + else + rect = lphc->textRect; + + bButton = TRUE; + } + + if( bButton && !(lphc->wState & CBF_NOREDRAW) ) + RedrawWindow( hWnd, &rect, 0, RDW_INVALIDATE | + RDW_ERASE | RDW_UPDATENOW | RDW_NOCHILDREN ); + CB_NOTIFY( lphc, CBN_CLOSEUP ); + } + } +} + +/*********************************************************************** + * COMBO_FlipListbox + * + * Used by the ComboLBox to show/hide itself in response to VK_F4, etc... + */ +BOOL COMBO_FlipListbox( LPHEADCOMBO lphc, BOOL ok, BOOL bRedrawButton ) +{ + if( lphc->wState & CBF_DROPPED ) + { + CBRollUp( lphc, ok, bRedrawButton ); + return FALSE; + } + + CBDropDown( lphc ); + return TRUE; +} + +/*********************************************************************** + * CBRepaintButton + */ +static void CBRepaintButton( LPHEADCOMBO lphc ) + { + InvalidateRect(lphc->self, &lphc->buttonRect, TRUE); + UpdateWindow(lphc->self); +} + +/*********************************************************************** + * COMBO_SetFocus + */ +static void COMBO_SetFocus( LPHEADCOMBO lphc ) +{ + if( !(lphc->wState & CBF_FOCUSED) ) + { + if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) + SendMessageW(lphc->hWndLBox, LB_CARETON, 0, 0); + + /* This is wrong. Message sequences seem to indicate that this + is set *after* the notify. */ + /* lphc->wState |= CBF_FOCUSED; */ + + if( !(lphc->wState & CBF_EDIT) ) + InvalidateRect(lphc->self, &lphc->textRect, TRUE); + + CB_NOTIFY( lphc, CBN_SETFOCUS ); + lphc->wState |= CBF_FOCUSED; + } +} + +/*********************************************************************** + * COMBO_KillFocus + */ +static void COMBO_KillFocus( LPHEADCOMBO lphc ) +{ + HWND hWnd = lphc->self; + + if( lphc->wState & CBF_FOCUSED ) + { + CBRollUp( lphc, FALSE, TRUE ); + if( IsWindow( hWnd ) ) + { + if( CB_GETTYPE(lphc) == CBS_DROPDOWNLIST ) + SendMessageW(lphc->hWndLBox, LB_CARETOFF, 0, 0); + + lphc->wState &= ~CBF_FOCUSED; + + /* redraw text */ + if( !(lphc->wState & CBF_EDIT) ) + InvalidateRect(lphc->self, &lphc->textRect, TRUE); + + CB_NOTIFY( lphc, CBN_KILLFOCUS ); + } + } +} + +/*********************************************************************** + * COMBO_Command + */ +static LRESULT COMBO_Command( LPHEADCOMBO lphc, WPARAM wParam, HWND hWnd ) +{ + if ( lphc->wState & CBF_EDIT && lphc->hWndEdit == hWnd ) + { + /* ">> 8" makes gcc generate jump-table instead of cmp ladder */ + + switch( HIWORD(wParam) >> 8 ) + { + case (EN_SETFOCUS >> 8): + + TRACE("[%p]: edit [%p] got focus\n", lphc->self, lphc->hWndEdit ); + + COMBO_SetFocus( lphc ); + break; + + case (EN_KILLFOCUS >> 8): + + TRACE("[%p]: edit [%p] lost focus\n", lphc->self, lphc->hWndEdit ); + + /* NOTE: it seems that Windows' edit control sends an + * undocumented message WM_USER + 0x1B instead of this + * notification (only when it happens to be a part of + * the combo). ?? - AK. + */ + + COMBO_KillFocus( lphc ); + break; + + + case (EN_CHANGE >> 8): + /* + * In some circumstances (when the selection of the combobox + * is changed for example) we don't want the EN_CHANGE notification + * to be forwarded to the parent of the combobox. This code + * checks a flag that is set in these occasions and ignores the + * notification. + */ + if (lphc->wState & CBF_NOLBSELECT) + { + lphc->wState &= ~CBF_NOLBSELECT; + } + else + { + CBUpdateLBox( lphc, lphc->wState & CBF_DROPPED ); + } + + if (!(lphc->wState & CBF_NOEDITNOTIFY)) + CB_NOTIFY( lphc, CBN_EDITCHANGE ); + break; + + case (EN_UPDATE >> 8): + if (!(lphc->wState & CBF_NOEDITNOTIFY)) + CB_NOTIFY( lphc, CBN_EDITUPDATE ); + break; + + case (EN_ERRSPACE >> 8): + CB_NOTIFY( lphc, CBN_ERRSPACE ); + } + } + else if( lphc->hWndLBox == hWnd ) + { + switch( (short)HIWORD(wParam) ) + { + case LBN_ERRSPACE: + CB_NOTIFY( lphc, CBN_ERRSPACE ); + break; + + case LBN_DBLCLK: + CB_NOTIFY( lphc, CBN_DBLCLK ); + break; + + case LBN_SELCHANGE: + case LBN_SELCANCEL: + + TRACE("[%p]: lbox selection change [%x]\n", lphc->self, lphc->wState ); + + /* do not roll up if selection is being tracked + * by arrow keys in the dropdown listbox */ + if (!(lphc->wState & CBF_NOROLLUP)) + { + CBRollUp( lphc, (HIWORD(wParam) == LBN_SELCHANGE), TRUE ); + } + else lphc->wState &= ~CBF_NOROLLUP; + + CB_NOTIFY( lphc, CBN_SELCHANGE ); + + if( HIWORD(wParam) == LBN_SELCHANGE) + { + if( lphc->wState & CBF_EDIT ) + lphc->wState |= CBF_NOLBSELECT; + CBPaintText( lphc, NULL ); + } + break; + + case LBN_SETFOCUS: + case LBN_KILLFOCUS: + /* nothing to do here since ComboLBox always resets the focus to its + * combo/edit counterpart */ + break; + } + } + return 0; +} + +/*********************************************************************** + * COMBO_ItemOp + * + * Fixup an ownerdrawn item operation and pass it up to the combobox owner. + */ +static LRESULT COMBO_ItemOp( LPHEADCOMBO lphc, UINT msg, LPARAM lParam ) +{ + HWND hWnd = lphc->self; + UINT id = (UINT)GetWindowLongPtrW( hWnd, GWLP_ID ); + + TRACE("[%p]: ownerdraw op %04x\n", lphc->self, msg ); + + switch( msg ) + { + case WM_DELETEITEM: + { + DELETEITEMSTRUCT *lpIS = (DELETEITEMSTRUCT *)lParam; + lpIS->CtlType = ODT_COMBOBOX; + lpIS->CtlID = id; + lpIS->hwndItem = hWnd; + break; + } + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *lpIS = (DRAWITEMSTRUCT *)lParam; + lpIS->CtlType = ODT_COMBOBOX; + lpIS->CtlID = id; + lpIS->hwndItem = hWnd; + break; + } + case WM_COMPAREITEM: + { + COMPAREITEMSTRUCT *lpIS = (COMPAREITEMSTRUCT *)lParam; + lpIS->CtlType = ODT_COMBOBOX; + lpIS->CtlID = id; + lpIS->hwndItem = hWnd; + break; + } + case WM_MEASUREITEM: + { + MEASUREITEMSTRUCT *lpIS = (MEASUREITEMSTRUCT *)lParam; + lpIS->CtlType = ODT_COMBOBOX; + lpIS->CtlID = id; + break; + } + } + return SendMessageW(lphc->owner, msg, id, lParam); +} + + +/*********************************************************************** + * COMBO_GetTextW + */ +static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf ) +{ + INT length; + + if( lphc->wState & CBF_EDIT ) + return SendMessageW( lphc->hWndEdit, WM_GETTEXT, count, (LPARAM)buf ); + + /* get it from the listbox */ + + if (!count || !buf) return 0; + if( lphc->hWndLBox ) + { + INT idx = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); + if (idx == LB_ERR) goto error; + length = SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, idx, 0 ); + if (length == LB_ERR) goto error; + + /* 'length' is without the terminating character */ + if (length >= count) + { + WCHAR *lpBuffer = heap_alloc((length + 1) * sizeof(WCHAR)); + if (!lpBuffer) goto error; + length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)lpBuffer); + + /* truncate if buffer is too short */ + if (length != LB_ERR) + { + lstrcpynW( buf, lpBuffer, count ); + length = count; + } + heap_free( lpBuffer ); + } + else length = SendMessageW(lphc->hWndLBox, LB_GETTEXT, idx, (LPARAM)buf); + + if (length == LB_ERR) return 0; + return length; + } + + error: /* error - truncate string, return zero */ + buf[0] = 0; + return 0; +} + +/*********************************************************************** + * CBResetPos + * + * This function sets window positions according to the updated + * component placement struct. + */ +static void CBResetPos( + LPHEADCOMBO lphc, + const RECT *rectEdit, + const RECT *rectLB, + BOOL bRedraw) +{ + BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE); + + /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of + * sizing messages */ + + if( lphc->wState & CBF_EDIT ) + SetWindowPos( lphc->hWndEdit, 0, + rectEdit->left, rectEdit->top, + rectEdit->right - rectEdit->left, + rectEdit->bottom - rectEdit->top, + SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) ); + + SetWindowPos( lphc->hWndLBox, 0, + rectLB->left, rectLB->top, + rectLB->right - rectLB->left, + rectLB->bottom - rectLB->top, + SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) ); + + if( bDrop ) + { + if( lphc->wState & CBF_DROPPED ) + { + lphc->wState &= ~CBF_DROPPED; + ShowWindow( lphc->hWndLBox, SW_HIDE ); + } + + if( bRedraw && !(lphc->wState & CBF_NOREDRAW) ) + RedrawWindow( lphc->self, NULL, 0, + RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW ); + } +} + + +/*********************************************************************** + * COMBO_Size + */ +static void COMBO_Size( LPHEADCOMBO lphc ) +{ + /* + * Those controls are always the same height. So we have to make sure + * they are not resized to another value. + */ + if( CB_GETTYPE(lphc) != CBS_SIMPLE ) + { + int newComboHeight, curComboHeight, curComboWidth; + RECT rc; + + GetWindowRect(lphc->self, &rc); + curComboHeight = rc.bottom - rc.top; + curComboWidth = rc.right - rc.left; + newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE(); + + /* + * Resizing a combobox has another side effect, it resizes the dropped + * rectangle as well. However, it does it only if the new height for the + * combobox is more than the height it should have. In other words, + * if the application resizing the combobox only had the intention to resize + * the actual control, for example, to do the layout of a dialog that is + * resized, the height of the dropdown is not changed. + */ + if( curComboHeight > newComboHeight ) + { + TRACE("oldComboHeight=%d, newComboHeight=%d, oldDropBottom=%d, oldDropTop=%d\n", + curComboHeight, newComboHeight, lphc->droppedRect.bottom, + lphc->droppedRect.top); + lphc->droppedRect.bottom = lphc->droppedRect.top + curComboHeight - newComboHeight; + } + /* + * Restore original height + */ + if( curComboHeight != newComboHeight ) + SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight, + SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW); + } + + CBCalcPlacement(lphc->self, + lphc, + &lphc->textRect, + &lphc->buttonRect, + &lphc->droppedRect); + + CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); +} + + +/*********************************************************************** + * COMBO_Font + */ +static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw ) +{ + /* + * Set the font + */ + lphc->hFont = hFont; + + /* + * Propagate to owned windows. + */ + if( lphc->wState & CBF_EDIT ) + SendMessageW(lphc->hWndEdit, WM_SETFONT, (WPARAM)hFont, bRedraw); + SendMessageW(lphc->hWndLBox, WM_SETFONT, (WPARAM)hFont, bRedraw); + + /* + * Redo the layout of the control. + */ + if ( CB_GETTYPE(lphc) == CBS_SIMPLE) + { + CBCalcPlacement(lphc->self, + lphc, + &lphc->textRect, + &lphc->buttonRect, + &lphc->droppedRect); + + CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); + } + else + { + CBForceDummyResize(lphc); + } +} + + +/*********************************************************************** + * COMBO_SetItemHeight + */ +static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height ) +{ + LRESULT lRet = CB_ERR; + + if( index == -1 ) /* set text field height */ + { + if( height < 32768 ) + { + lphc->editHeight = height + 2; /* Is the 2 for 2*EDIT_CONTROL_PADDING? */ + + /* + * Redo the layout of the control. + */ + if ( CB_GETTYPE(lphc) == CBS_SIMPLE) + { + CBCalcPlacement(lphc->self, + lphc, + &lphc->textRect, + &lphc->buttonRect, + &lphc->droppedRect); + + CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE ); + } + else + { + CBForceDummyResize(lphc); + } + + lRet = height; + } + } + else if ( CB_OWNERDRAWN(lphc) ) /* set listbox item height */ + lRet = SendMessageW(lphc->hWndLBox, LB_SETITEMHEIGHT, index, height); + return lRet; +} + +/*********************************************************************** + * COMBO_SelectString + */ +static LRESULT COMBO_SelectString( LPHEADCOMBO lphc, INT start, LPARAM pText) +{ + INT index = SendMessageW(lphc->hWndLBox, LB_SELECTSTRING, start, pText); + if( index >= 0 ) + { + if( lphc->wState & CBF_EDIT ) + CBUpdateEdit( lphc, index ); + else + { + InvalidateRect(lphc->self, &lphc->textRect, TRUE); + } + } + return (LRESULT)index; +} + +/*********************************************************************** + * COMBO_LButtonDown + */ +static void COMBO_LButtonDown( LPHEADCOMBO lphc, LPARAM lParam ) +{ + POINT pt; + BOOL bButton; + HWND hWnd = lphc->self; + + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + bButton = PtInRect(&lphc->buttonRect, pt); + + if( (CB_GETTYPE(lphc) == CBS_DROPDOWNLIST) || + (bButton && (CB_GETTYPE(lphc) == CBS_DROPDOWN)) ) + { + lphc->wState |= CBF_BUTTONDOWN; + if( lphc->wState & CBF_DROPPED ) + { + /* got a click to cancel selection */ + + lphc->wState &= ~CBF_BUTTONDOWN; + CBRollUp( lphc, TRUE, FALSE ); + if( !IsWindow( hWnd ) ) return; + + if( lphc->wState & CBF_CAPTURE ) + { + lphc->wState &= ~CBF_CAPTURE; + ReleaseCapture(); + } + } + else + { + /* drop down the listbox and start tracking */ + + lphc->wState |= CBF_CAPTURE; + SetCapture( hWnd ); + CBDropDown( lphc ); + } + if( bButton ) CBRepaintButton( lphc ); + } +} + +/*********************************************************************** + * COMBO_LButtonUp + * + * Release capture and stop tracking if needed. + */ +static void COMBO_LButtonUp( LPHEADCOMBO lphc ) +{ + if( lphc->wState & CBF_CAPTURE ) + { + lphc->wState &= ~CBF_CAPTURE; + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) + { + INT index = CBUpdateLBox( lphc, TRUE ); + /* Update edit only if item is in the list */ + if(index >= 0) + { + lphc->wState |= CBF_NOLBSELECT; + CBUpdateEdit( lphc, index ); + lphc->wState &= ~CBF_NOLBSELECT; + } + } + ReleaseCapture(); + SetCapture(lphc->hWndLBox); + } + + if( lphc->wState & CBF_BUTTONDOWN ) + { + lphc->wState &= ~CBF_BUTTONDOWN; + CBRepaintButton( lphc ); + } +} + +/*********************************************************************** + * COMBO_MouseMove + * + * Two things to do - track combo button and release capture when + * pointer goes into the listbox. + */ +static void COMBO_MouseMove( LPHEADCOMBO lphc, WPARAM wParam, LPARAM lParam ) +{ + POINT pt; + RECT lbRect; + + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + + if( lphc->wState & CBF_BUTTONDOWN ) + { + BOOL bButton; + + bButton = PtInRect(&lphc->buttonRect, pt); + + if( !bButton ) + { + lphc->wState &= ~CBF_BUTTONDOWN; + CBRepaintButton( lphc ); + } + } + + GetClientRect( lphc->hWndLBox, &lbRect ); + MapWindowPoints( lphc->self, lphc->hWndLBox, &pt, 1 ); + if( PtInRect(&lbRect, pt) ) + { + lphc->wState &= ~CBF_CAPTURE; + ReleaseCapture(); + if( CB_GETTYPE(lphc) == CBS_DROPDOWN ) CBUpdateLBox( lphc, TRUE ); + + /* hand over pointer tracking */ + SendMessageW(lphc->hWndLBox, WM_LBUTTONDOWN, wParam, lParam); + } +} + +static LRESULT COMBO_GetComboBoxInfo(const HEADCOMBO *lphc, COMBOBOXINFO *pcbi) +{ + if (!pcbi || (pcbi->cbSize < sizeof(COMBOBOXINFO))) + return FALSE; + + pcbi->rcItem = lphc->textRect; + pcbi->rcButton = lphc->buttonRect; + pcbi->stateButton = 0; + if (lphc->wState & CBF_BUTTONDOWN) + pcbi->stateButton |= STATE_SYSTEM_PRESSED; + if (IsRectEmpty(&lphc->buttonRect)) + pcbi->stateButton |= STATE_SYSTEM_INVISIBLE; + pcbi->hwndCombo = lphc->self; + pcbi->hwndItem = lphc->hWndEdit; + pcbi->hwndList = lphc->hWndLBox; + return TRUE; +} + +static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + HEADCOMBO *lphc = (HEADCOMBO *)GetWindowLongPtrW( hwnd, 0 ); + HTHEME theme; + + TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", hwnd, message, wParam, lParam ); + + if (!IsWindow(hwnd)) return 0; + + if (lphc || message == WM_NCCREATE) + switch(message) + { + case WM_NCCREATE: + { + LONG style = ((CREATESTRUCTW *)lParam)->style; + return COMBO_NCCreate(hwnd, style); + } + + case WM_NCDESTROY: + COMBO_NCDestroy(lphc); + break;/* -> DefWindowProc */ + + case WM_CREATE: + { + HWND hwndParent; + LONG style; + + hwndParent = ((CREATESTRUCTW *)lParam)->hwndParent; + style = ((CREATESTRUCTW *)lParam)->style; + return COMBO_Create(hwnd, lphc, hwndParent, style); + } + + case WM_DESTROY: + theme = GetWindowTheme( hwnd ); + CloseThemeData( theme ); + break; + + case WM_THEMECHANGED: + theme = GetWindowTheme( hwnd ); + CloseThemeData( theme ); + OpenThemeData( hwnd, WC_COMBOBOXW ); + break; + + case WM_PRINTCLIENT: + case WM_PAINT: + { + LRESULT ret = 0; + PAINTSTRUCT ps; + HDC hdc; + + hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps); + + if (hdc && !(lphc->wState & CBF_NOREDRAW)) + { + HTHEME theme = GetWindowTheme(hwnd); + + if (theme) + ret = COMBO_ThemedPaint(theme, lphc, hdc); + else + ret = COMBO_Paint(lphc, hdc); + } + + if (!wParam) + EndPaint(hwnd, &ps); + + return ret; + } + case WM_ERASEBKGND: + /* do all painting in WM_PAINT like Windows does */ + return 1; + + case WM_GETDLGCODE: + { + LRESULT result = DLGC_WANTARROWS | DLGC_WANTCHARS; + if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN)) + { + int vk = (int)((LPMSG)lParam)->wParam; + + if ((vk == VK_RETURN || vk == VK_ESCAPE) && (lphc->wState & CBF_DROPPED)) + result |= DLGC_WANTMESSAGE; + } + return result; + } + + case WM_SIZE: + if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE)) + COMBO_Size( lphc ); + return TRUE; + + case WM_SETFONT: + COMBO_Font( lphc, (HFONT)wParam, (BOOL)lParam ); + return TRUE; + + case WM_GETFONT: + return (LRESULT)lphc->hFont; + + case WM_SETFOCUS: + if (lphc->wState & CBF_EDIT) + { + SetFocus( lphc->hWndEdit ); + /* The first time focus is received, select all the text */ + if (!(lphc->wState & CBF_BEENFOCUSED)) + { + SendMessageW(lphc->hWndEdit, EM_SETSEL, 0, -1); + lphc->wState |= CBF_BEENFOCUSED; + } + } + else + COMBO_SetFocus( lphc ); + return TRUE; + + case WM_KILLFOCUS: + { + HWND hwndFocus = (HWND)wParam; + if (!hwndFocus || (hwndFocus != lphc->hWndEdit && hwndFocus != lphc->hWndLBox)) + COMBO_KillFocus( lphc ); + return TRUE; + } + + case WM_COMMAND: + return COMBO_Command( lphc, wParam, (HWND)lParam ); + + case WM_GETTEXT: + return COMBO_GetText( lphc, wParam, (LPWSTR)lParam ); + + case WM_SETTEXT: + case WM_GETTEXTLENGTH: + case WM_CLEAR: + if ((message == WM_GETTEXTLENGTH) && !ISWIN31 && !(lphc->wState & CBF_EDIT)) + { + int j = SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); + if (j == -1) return 0; + return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, j, 0); + } + else if ( lphc->wState & CBF_EDIT ) + { + LRESULT ret; + lphc->wState |= CBF_NOEDITNOTIFY; + ret = SendMessageW(lphc->hWndEdit, message, wParam, lParam); + lphc->wState &= ~CBF_NOEDITNOTIFY; + return ret; + } + else + return CB_ERR; + + case WM_CUT: + case WM_PASTE: + case WM_COPY: + if (lphc->wState & CBF_EDIT) + return SendMessageW(lphc->hWndEdit, message, wParam, lParam); + else return CB_ERR; + + case WM_DRAWITEM: + case WM_DELETEITEM: + case WM_COMPAREITEM: + case WM_MEASUREITEM: + return COMBO_ItemOp(lphc, message, lParam); + + case WM_ENABLE: + if (lphc->wState & CBF_EDIT) + EnableWindow( lphc->hWndEdit, (BOOL)wParam ); + EnableWindow( lphc->hWndLBox, (BOOL)wParam ); + + /* Force the control to repaint when the enabled state changes. */ + InvalidateRect(lphc->self, NULL, TRUE); + return TRUE; + + case WM_SETREDRAW: + if (wParam) + lphc->wState &= ~CBF_NOREDRAW; + else + lphc->wState |= CBF_NOREDRAW; + + if ( lphc->wState & CBF_EDIT ) + SendMessageW(lphc->hWndEdit, message, wParam, lParam); + SendMessageW(lphc->hWndLBox, message, wParam, lParam); + return 0; + + case WM_SYSKEYDOWN: + if ( KEYDATA_ALT & HIWORD(lParam) ) + if( wParam == VK_UP || wParam == VK_DOWN ) + COMBO_FlipListbox( lphc, FALSE, FALSE ); + return 0; + + case WM_KEYDOWN: + if ((wParam == VK_RETURN || wParam == VK_ESCAPE) && + (lphc->wState & CBF_DROPPED)) + { + CBRollUp( lphc, wParam == VK_RETURN, FALSE ); + return TRUE; + } + else if ((wParam == VK_F4) && !(lphc->wState & CBF_EUI)) + { + COMBO_FlipListbox( lphc, FALSE, FALSE ); + return TRUE; + } + /* fall through */ + case WM_CHAR: + case WM_IME_CHAR: + { + HWND hwndTarget; + + if ( lphc->wState & CBF_EDIT ) + hwndTarget = lphc->hWndEdit; + else + hwndTarget = lphc->hWndLBox; + + return SendMessageW(hwndTarget, message, wParam, lParam); + } + + case WM_LBUTTONDOWN: + if ( !(lphc->wState & CBF_FOCUSED) ) SetFocus( lphc->self ); + if ( lphc->wState & CBF_FOCUSED ) COMBO_LButtonDown( lphc, lParam ); + return TRUE; + + case WM_LBUTTONUP: + COMBO_LButtonUp( lphc ); + return TRUE; + + case WM_MOUSEMOVE: + if (!IsRectEmpty(&lphc->buttonRect)) + { + POINT pt; + + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + + if (PtInRect(&lphc->buttonRect, pt)) + { + if (!(lphc->wState & CBF_HOT)) + { + lphc->wState |= CBF_HOT; + RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW); + } + } + else if (lphc->wState & CBF_HOT) + { + lphc->wState &= ~CBF_HOT; + RedrawWindow(hwnd, &lphc->buttonRect, 0, RDW_INVALIDATE | RDW_UPDATENOW); + } + } + + if ( lphc->wState & CBF_CAPTURE ) + COMBO_MouseMove( lphc, wParam, lParam ); + return TRUE; + + case WM_MOUSEWHEEL: + if (wParam & (MK_SHIFT | MK_CONTROL)) + return DefWindowProcW(hwnd, message, wParam, lParam); + + if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_UP, 0); + if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) return SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0); + return TRUE; + + /* Combo messages */ + case CB_ADDSTRING: + if (lphc->dwStyle & CBS_LOWERCASE) + CharLowerW((LPWSTR)lParam); + else if (lphc->dwStyle & CBS_UPPERCASE) + CharUpperW((LPWSTR)lParam); + return SendMessageW(lphc->hWndLBox, LB_ADDSTRING, 0, lParam); + + case CB_INSERTSTRING: + if (lphc->dwStyle & CBS_LOWERCASE) + CharLowerW((LPWSTR)lParam); + else if (lphc->dwStyle & CBS_UPPERCASE) + CharUpperW((LPWSTR)lParam); + return SendMessageW(lphc->hWndLBox, LB_INSERTSTRING, wParam, lParam); + + case CB_DELETESTRING: + return SendMessageW(lphc->hWndLBox, LB_DELETESTRING, wParam, 0); + + case CB_SELECTSTRING: + return COMBO_SelectString(lphc, (INT)wParam, lParam); + + case CB_FINDSTRING: + return SendMessageW(lphc->hWndLBox, LB_FINDSTRING, wParam, lParam); + + case CB_FINDSTRINGEXACT: + return SendMessageW(lphc->hWndLBox, LB_FINDSTRINGEXACT, wParam, lParam); + + case CB_SETITEMHEIGHT: + return COMBO_SetItemHeight( lphc, (INT)wParam, (INT)lParam); + + case CB_GETITEMHEIGHT: + if ((INT)wParam >= 0) /* listbox item */ + return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0); + return CBGetTextAreaHeight(hwnd, lphc); + + case CB_RESETCONTENT: + SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0); + + if ((lphc->wState & CBF_EDIT) && CB_HASSTRINGS(lphc)) + { + static const WCHAR empty_stringW[] = { 0 }; + SendMessageW(lphc->hWndEdit, WM_SETTEXT, 0, (LPARAM)empty_stringW); + } + else + InvalidateRect(lphc->self, NULL, TRUE); + return TRUE; + + case CB_INITSTORAGE: + return SendMessageW(lphc->hWndLBox, LB_INITSTORAGE, wParam, lParam); + + case CB_GETHORIZONTALEXTENT: + return SendMessageW(lphc->hWndLBox, LB_GETHORIZONTALEXTENT, 0, 0); + + case CB_SETHORIZONTALEXTENT: + return SendMessageW(lphc->hWndLBox, LB_SETHORIZONTALEXTENT, wParam, 0); + + case CB_GETTOPINDEX: + return SendMessageW(lphc->hWndLBox, LB_GETTOPINDEX, 0, 0); + + case CB_GETLOCALE: + return SendMessageW(lphc->hWndLBox, LB_GETLOCALE, 0, 0); + + case CB_SETLOCALE: + return SendMessageW(lphc->hWndLBox, LB_SETLOCALE, wParam, 0); + + case CB_SETDROPPEDWIDTH: + if ((CB_GETTYPE(lphc) == CBS_SIMPLE) || (INT)wParam >= 32768) + return CB_ERR; + + /* new value must be higher than combobox width */ + if ((INT)wParam >= lphc->droppedRect.right - lphc->droppedRect.left) + lphc->droppedWidth = wParam; + else if (wParam) + lphc->droppedWidth = 0; + + /* recalculate the combobox area */ + CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect ); + + /* fall through */ + case CB_GETDROPPEDWIDTH: + if (lphc->droppedWidth) + return lphc->droppedWidth; + return lphc->droppedRect.right - lphc->droppedRect.left; + + case CB_GETDROPPEDCONTROLRECT: + if (lParam) + CBGetDroppedControlRect(lphc, (LPRECT)lParam ); + return CB_OKAY; + + case CB_GETDROPPEDSTATE: + return (lphc->wState & CBF_DROPPED) != 0; + + case CB_DIR: + return SendMessageW(lphc->hWndLBox, LB_DIR, wParam, lParam); + + case CB_SHOWDROPDOWN: + if (CB_GETTYPE(lphc) != CBS_SIMPLE) + { + if (wParam) + { + if (!(lphc->wState & CBF_DROPPED)) + CBDropDown( lphc ); + } + else if (lphc->wState & CBF_DROPPED) + CBRollUp( lphc, FALSE, TRUE ); + } + return TRUE; + + case CB_GETCOUNT: + return SendMessageW(lphc->hWndLBox, LB_GETCOUNT, 0, 0); + + case CB_GETCURSEL: + return SendMessageW(lphc->hWndLBox, LB_GETCURSEL, 0, 0); + + case CB_SETCURSEL: + lParam = SendMessageW(lphc->hWndLBox, LB_SETCURSEL, wParam, 0); + if (lParam >= 0) + SendMessageW(lphc->hWndLBox, LB_SETTOPINDEX, wParam, 0); + + /* no LBN_SELCHANGE in this case, update manually */ + CBPaintText(lphc, NULL); + lphc->wState &= ~CBF_SELCHANGE; + return lParam; + + case CB_GETLBTEXT: + return SendMessageW(lphc->hWndLBox, LB_GETTEXT, wParam, lParam); + + case CB_GETLBTEXTLEN: + return SendMessageW(lphc->hWndLBox, LB_GETTEXTLEN, wParam, 0); + + case CB_GETITEMDATA: + return SendMessageW(lphc->hWndLBox, LB_GETITEMDATA, wParam, 0); + + case CB_SETITEMDATA: + return SendMessageW(lphc->hWndLBox, LB_SETITEMDATA, wParam, lParam); + + case CB_GETEDITSEL: + /* Edit checks passed parameters itself */ + if (lphc->wState & CBF_EDIT) + return SendMessageW(lphc->hWndEdit, EM_GETSEL, wParam, lParam); + return CB_ERR; + + case CB_SETEDITSEL: + if (lphc->wState & CBF_EDIT) + return SendMessageW(lphc->hWndEdit, EM_SETSEL, (INT)(SHORT)LOWORD(lParam), (INT)(SHORT)HIWORD(lParam) ); + return CB_ERR; + + case CB_SETEXTENDEDUI: + if (CB_GETTYPE(lphc) == CBS_SIMPLE ) + return CB_ERR; + if (wParam) + lphc->wState |= CBF_EUI; + else + lphc->wState &= ~CBF_EUI; + return CB_OKAY; + + case CB_GETEXTENDEDUI: + return (lphc->wState & CBF_EUI) != 0; + + case CB_GETCOMBOBOXINFO: + return COMBO_GetComboBoxInfo(lphc, (COMBOBOXINFO *)lParam); + + case CB_LIMITTEXT: + if (lphc->wState & CBF_EDIT) + return SendMessageW(lphc->hWndEdit, EM_LIMITTEXT, wParam, lParam); + return TRUE; + + default: + if (message >= WM_USER) + WARN("unknown msg WM_USER+%04x wp=%04lx lp=%08lx\n", message - WM_USER, wParam, lParam ); + break; + } + + return DefWindowProcW(hwnd, message, wParam, lParam); +} + +void COMBO_Register(void) +{ + WNDCLASSW wndClass; + + memset(&wndClass, 0, sizeof(wndClass)); + wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + wndClass.lpfnWndProc = COMBO_WindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = sizeof(HEADCOMBO *); + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = WC_COMBOBOXW; + RegisterClassW(&wndClass); +} diff --git a/dll/win32/comctl32/comboex.c b/dll/win32/comctl32/comboex.c index 68709f018ed..3c3714ac569 100644 --- a/dll/win32/comctl32/comboex.c +++ b/dll/win32/comctl32/comboex.c @@ -18,19 +18,19 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/debug.h" +#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(comboex); @@ -1502,10 +1502,10 @@ static void COMBOEX_ResetContent (COMBOEX_INFO *infoPtr) static LRESULT COMBOEX_Destroy (COMBOEX_INFO *infoPtr) { if (infoPtr->hwndCombo) - RemoveWindowSubclass(infoPtr->hwndCombo, COMBOEX_ComboWndProc, COMBO_SUBCLASSID); + SetWindowSubclass(infoPtr->hwndCombo, COMBOEX_ComboWndProc, COMBO_SUBCLASSID, 0); if (infoPtr->hwndEdit) - RemoveWindowSubclass(infoPtr->hwndEdit, COMBOEX_EditWndProc, EDIT_SUBCLASSID); + SetWindowSubclass(infoPtr->hwndEdit, COMBOEX_EditWndProc, EDIT_SUBCLASSID, 0); COMBOEX_FreeText (&infoPtr->edit); COMBOEX_ResetContent (infoPtr); @@ -1668,7 +1668,11 @@ COMBOEX_EditWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx, info_ptr=%p\n", hwnd, uMsg, wParam, lParam, infoPtr); - if (!infoPtr) return 0; + if (uMsg == WM_NCDESTROY) + RemoveWindowSubclass(hwnd, COMBOEX_EditWndProc, EDIT_SUBCLASSID); + + if (!infoPtr) + return DefSubclassProc(hwnd, uMsg, wParam, lParam); switch (uMsg) { @@ -1823,11 +1827,14 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx, info_ptr=%p\n", hwnd, uMsg, wParam, lParam, infoPtr); - if (!infoPtr) return 0; + if (uMsg == WM_NCDESTROY) + RemoveWindowSubclass(hwnd, COMBOEX_ComboWndProc, COMBO_SUBCLASSID); + + if (!infoPtr) + return DefSubclassProc(hwnd, uMsg, wParam, lParam); switch (uMsg) { - case WM_DRAWITEM: /* * The only way this message should come is from the @@ -1835,7 +1842,7 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, * that ComboEx knows this is listbox. */ ((DRAWITEMSTRUCT *)lParam)->itemState |= ODS_COMBOEXLBOX; - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + break; case WM_ERASEBKGND: hDC = (HDC) wParam; @@ -1844,7 +1851,7 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, TRACE("erasing (%s)\n", wine_dbgstr_rect(&rect)); ExtTextOutW (hDC, 0, 0, ETO_OPAQUE, &rect, 0, 0, 0); SetBkColor (hDC, obkc); - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + break; case WM_SETCURSOR: /* @@ -1858,7 +1865,7 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, nmmse.pt.y = 0; nmmse.dwHitInfo = lParam; COMBOEX_Notify (infoPtr, NM_SETCURSOR, (NMHDR *)&nmmse); - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + break; case WM_LBUTTONDOWN: GetClientRect (hwnd, &rect); @@ -1868,15 +1875,15 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, pt.x = (short)LOWORD(lParam); pt.y = (short)HIWORD(lParam); if (PtInRect(&rect, pt)) - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + break; infoPtr->flags |= WCBE_MOUSECAPTURED; SetCapture(hwnd); - break; + return 0; case WM_LBUTTONUP: if (!(infoPtr->flags & WCBE_MOUSECAPTURED)) - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + break; ReleaseCapture(); infoPtr->flags &= ~WCBE_MOUSECAPTURED; @@ -1885,7 +1892,7 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, } else { SendMessageW(hwnd, CB_SHOWDROPDOWN, TRUE, 0); } - break; + return 0; case WM_MOUSEMOVE: if ( (infoPtr->flags & WCBE_MOUSECAPTURED) && @@ -1894,7 +1901,7 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, COMBOEX_NotifyDragBegin(infoPtr, edit_text); infoPtr->flags |= WCBE_MOUSEDRAGGED; } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + break; case WM_COMMAND: switch (HIWORD(wParam)) { @@ -1975,9 +1982,10 @@ COMBOEX_ComboWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, break; }/* fall through */ default: - return DefSubclassProc(hwnd, uMsg, wParam, lParam); + ; } - return 0; + + return DefSubclassProc(hwnd, uMsg, wParam, lParam); } diff --git a/dll/win32/comctl32/comctl32.h b/dll/win32/comctl32/comctl32.h index 17b6767bfe8..2ced440b75b 100644 --- a/dll/win32/comctl32/comctl32.h +++ b/dll/win32/comctl32/comctl32.h @@ -24,36 +24,135 @@ #ifndef __WINE_COMCTL32_H #define __WINE_COMCTL32_H -#include - +#ifndef RC_INVOKED #include -#include +#endif -#define _INC_WINDOWS -#define COM_NO_WINDOWS_H - -#define COBJMACROS -#define NONAMELESSUNION -#define NONAMELESSSTRUCT - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "resource.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" extern HMODULE COMCTL32_hModule DECLSPEC_HIDDEN; extern HBRUSH COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN; +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + +/* Property sheet / Wizard */ +#define IDD_PROPSHEET 1006 +#define IDD_WIZARD 1020 + +#define IDC_TABCONTROL 12320 +#define IDC_APPLY_BUTTON 12321 +#define IDC_BACK_BUTTON 12323 +#define IDC_NEXT_BUTTON 12324 +#define IDC_FINISH_BUTTON 12325 +#define IDC_SUNKEN_LINE 12326 +#define IDC_SUNKEN_LINEHEADER 12327 + +#define IDS_CLOSE 4160 + +/* Toolbar customization dialog */ +#define IDD_TBCUSTOMIZE 200 + +#define IDC_AVAILBTN_LBOX 201 +#define IDC_RESET_BTN 202 +#define IDC_TOOLBARBTN_LBOX 203 +#define IDC_REMOVE_BTN 204 +#define IDC_HELP_BTN 205 +#define IDC_MOVEUP_BTN 206 +#define IDC_MOVEDN_BTN 207 + +#define IDS_SEPARATOR 1024 + +/* Toolbar imagelist bitmaps */ +#define IDB_STD_SMALL 120 +#define IDB_STD_LARGE 121 +#define IDB_VIEW_SMALL 124 +#define IDB_VIEW_LARGE 125 +#define IDB_HIST_SMALL 130 +#define IDB_HIST_LARGE 131 + +#define IDM_TODAY 4163 +#define IDM_GOTODAY 4164 + +/* Treeview Checkboxes */ + +#define IDT_CHECK 401 + + +/* Cursors */ +#define IDC_MOVEBUTTON 102 +#define IDC_COPY 104 +#define IDC_DIVIDER 106 +#define IDC_DIVIDEROPEN 107 + + +/* DragList resources */ +#define IDI_DRAGARROW 501 + +/* HOTKEY internal strings */ +#define HKY_NONE 2048 + +/* Tooltip icons */ +#define IDI_TT_INFO_SM 22 +#define IDI_TT_WARN_SM 25 +#define IDI_TT_ERROR_SM 28 + +/* Taskdialog strings */ +#define IDS_BUTTON_YES 3000 +#define IDS_BUTTON_NO 3001 +#define IDS_BUTTON_RETRY 3002 +#define IDS_BUTTON_OK 3003 +#define IDS_BUTTON_CANCEL 3004 +#define IDS_BUTTON_CLOSE 3005 + +#ifndef __REACTOS__ +#define WM_SYSTIMER 0x0118 +#endif + +enum combobox_state_flags +{ + CBF_DROPPED = 0x0001, + CBF_BUTTONDOWN = 0x0002, + CBF_NOROLLUP = 0x0004, + CBF_MEASUREITEM = 0x0008, + CBF_FOCUSED = 0x0010, + CBF_CAPTURE = 0x0020, + CBF_EDIT = 0x0040, + CBF_NORESIZE = 0x0080, + CBF_NOTIFY = 0x0100, + CBF_NOREDRAW = 0x0200, + CBF_SELCHANGE = 0x0400, + CBF_HOT = 0x0800, + CBF_NOEDITNOTIFY = 0x1000, + CBF_NOLBSELECT = 0x2000, /* do not change current selection */ + CBF_BEENFOCUSED = 0x4000, /* has it ever had focus */ + CBF_EUI = 0x8000, +}; + +typedef struct +{ + HWND self; + HWND owner; + UINT dwStyle; + HWND hWndEdit; + HWND hWndLBox; + UINT wState; + HFONT hFont; + RECT textRect; + RECT buttonRect; + RECT droppedRect; + INT droppedIndex; + INT fixedOwnerDrawHeight; + INT droppedWidth; /* last two are not used unless set */ + INT editHeight; /* explicitly */ +} HEADCOMBO, *LPHEADCOMBO; + +extern BOOL COMBO_FlipListbox(HEADCOMBO *lphc, BOOL ok, BOOL bRedrawButton) DECLSPEC_HIDDEN; + typedef struct { COLORREF clrBtnHighlight; /* COLOR_BTNHIGHLIGHT */ @@ -77,25 +176,6 @@ typedef struct extern COMCTL32_SysColor comctl32_color DECLSPEC_HIDDEN; -#ifdef __REACTOS__ - -typedef struct _BUTTON_DATA { - LONG state; - HFONT font; - LONG_PTR image; - DWORD ui_state; - - RECT rcTextMargin; - BUTTON_IMAGELIST imlData; -} BUTTON_DATA, *PBUTTON_DATA; - -static inline PBUTTON_DATA _GetButtonData(HWND hwnd) -{ - return (PBUTTON_DATA)GetWindowLongPtrW( hwnd, 0 ); -} - -#endif /* __REACTOS__ */ - /* Internal function */ HWND COMCTL32_CreateToolTip (HWND) DECLSPEC_HIDDEN; VOID COMCTL32_RefreshSysColors(void) DECLSPEC_HIDDEN; @@ -108,9 +188,7 @@ INT Str_GetPtrAtoW (LPCSTR lpSrc, LPWSTR lpDest, INT nMaxLen) DECLSPEC_HIDDEN; BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc) DECLSPEC_HIDDEN; BOOL Str_SetPtrWtoA (LPSTR *lppDest, LPCWSTR lpSrc) DECLSPEC_HIDDEN; -#ifndef __REACTOS__ #define COMCTL32_VERSION_MINOR 81 -#endif /* Our internal stack structure of the window procedures to subclass */ typedef struct _SUBCLASSPROCS { @@ -141,12 +219,18 @@ INT WINAPI Str_GetPtrW (LPCWSTR, LPWSTR, INT); LRESULT WINAPI SetPathWordBreakProc(HWND hwnd, BOOL bSet); BOOL WINAPI MirrorIcon(HICON *phicon1, HICON *phicon2); +HRGN set_control_clipping(HDC hdc, const RECT *rect) DECLSPEC_HIDDEN; + extern void ANIMATE_Register(void) DECLSPEC_HIDDEN; extern void ANIMATE_Unregister(void) DECLSPEC_HIDDEN; +extern void BUTTON_Register(void) DECLSPEC_HIDDEN; +extern void COMBO_Register(void) DECLSPEC_HIDDEN; extern void COMBOEX_Register(void) DECLSPEC_HIDDEN; extern void COMBOEX_Unregister(void) DECLSPEC_HIDDEN; +extern void COMBOLBOX_Register(void) DECLSPEC_HIDDEN; extern void DATETIME_Register(void) DECLSPEC_HIDDEN; extern void DATETIME_Unregister(void) DECLSPEC_HIDDEN; +extern void EDIT_Register(void) DECLSPEC_HIDDEN; extern void FLATSB_Register(void) DECLSPEC_HIDDEN; extern void FLATSB_Unregister(void) DECLSPEC_HIDDEN; extern void HEADER_Register(void) DECLSPEC_HIDDEN; @@ -155,6 +239,7 @@ extern void HOTKEY_Register(void) DECLSPEC_HIDDEN; extern void HOTKEY_Unregister(void) DECLSPEC_HIDDEN; extern void IPADDRESS_Register(void) DECLSPEC_HIDDEN; extern void IPADDRESS_Unregister(void) DECLSPEC_HIDDEN; +extern void LISTBOX_Register(void) DECLSPEC_HIDDEN; extern void LISTVIEW_Register(void) DECLSPEC_HIDDEN; extern void LISTVIEW_Unregister(void) DECLSPEC_HIDDEN; extern void MONTHCAL_Register(void) DECLSPEC_HIDDEN; @@ -167,6 +252,7 @@ extern void PROGRESS_Register(void) DECLSPEC_HIDDEN; extern void PROGRESS_Unregister(void) DECLSPEC_HIDDEN; extern void REBAR_Register(void) DECLSPEC_HIDDEN; extern void REBAR_Unregister(void) DECLSPEC_HIDDEN; +extern void STATIC_Register(void) DECLSPEC_HIDDEN; extern void STATUS_Register(void) DECLSPEC_HIDDEN; extern void STATUS_Unregister(void) DECLSPEC_HIDDEN; extern void SYSLINK_Register(void) DECLSPEC_HIDDEN; @@ -184,7 +270,6 @@ extern void TREEVIEW_Unregister(void) DECLSPEC_HIDDEN; extern void UPDOWN_Register(void) DECLSPEC_HIDDEN; extern void UPDOWN_Unregister(void) DECLSPEC_HIDDEN; #ifdef __REACTOS__ -extern void BUTTON_Register(); extern void BUTTON_Unregister(); extern void TOOLBARv6_Register(void) DECLSPEC_HIDDEN; extern void TOOLBARv6_Unregister(void) DECLSPEC_HIDDEN; diff --git a/dll/win32/comctl32/comctl32undoc.c b/dll/win32/comctl32/comctl32undoc.c index b12f6144124..639db05d1bd 100644 --- a/dll/win32/comctl32/comctl32undoc.c +++ b/dll/win32/comctl32/comctl32undoc.c @@ -26,9 +26,32 @@ * COMCTL32.DLL (internally). * */ +#include "config.h" +#include "wine/port.h" +#include +#include +#include +#include + +#define COBJMACROS +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "commctrl.h" +#include "objbase.h" +#include "winerror.h" + +#include "wine/unicode.h" #include "comctl32.h" +#include "wine/debug.h" + WINE_DEFAULT_DEBUG_CHANNEL(commctrl); static const WCHAR strMRUList[] = { 'M','R','U','L','i','s','t',0 }; diff --git a/dll/win32/comctl32/commctrl.c b/dll/win32/comctl32/commctrl.c index 1bdc71d6ad4..6eedf01810b 100644 --- a/dll/win32/comctl32/commctrl.c +++ b/dll/win32/comctl32/commctrl.c @@ -53,10 +53,22 @@ * -- ICC_WIN95_CLASSES */ -#include "comctl32.h" +#include +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "winerror.h" +#include "winreg.h" #define NO_SHLWAPI_STREAM -#include +#include "shlwapi.h" +#include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(commctrl); @@ -178,7 +190,13 @@ static void RegisterControls(BOOL bV6) } else { - BUTTON_Register(); + BUTTON_Register (); + COMBO_Register (); + COMBOLBOX_Register (); + EDIT_Register (); + LISTBOX_Register (); + STATIC_Register (); + TOOLBARv6_Register(); } } @@ -277,6 +295,28 @@ BOOLEAN WINAPI RegisterClassNameW(LPCWSTR className) #endif /* __REACTOS__ */ +#ifndef __REACTOS__ +static void unregister_versioned_classes(void) +{ +#define VERSION "6.0.2600.2982!" + static const char *classes[] = + { + VERSION WC_BUTTONA, + VERSION WC_COMBOBOXA, + VERSION "ComboLBox", + VERSION WC_EDITA, + VERSION WC_LISTBOXA, + VERSION WC_STATICA, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(classes); i++) + UnregisterClassA(classes[i], NULL); + +#undef VERSION +} +#endif + /*********************************************************************** * DllMain [Internal] * @@ -337,6 +377,13 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) TREEVIEW_Register (); UPDOWN_Register (); + BUTTON_Register (); + COMBO_Register (); + COMBOLBOX_Register (); + EDIT_Register (); + LISTBOX_Register (); + STATIC_Register (); + /* subclass user32 controls */ THEMING_Initialize (); #else @@ -373,6 +420,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) TRACKBAR_Unregister (); TREEVIEW_Unregister (); UPDOWN_Unregister (); + + unregister_versioned_classes (); + #else UninitializeClasses(); #endif @@ -450,7 +500,7 @@ MenuHelp (UINT uMsg, WPARAM wParam, LPARAM lParam, HMENU hMainMenu, if (uMenuID) { WCHAR szText[256]; - if (!LoadStringW (hInst, uMenuID, szText, sizeof(szText)/sizeof(szText[0]))) + if (!LoadStringW (hInst, uMenuID, szText, ARRAY_SIZE(szText))) szText[0] = '\0'; SendMessageW (hwndStatus, SB_SETTEXTW, @@ -1593,7 +1643,7 @@ void COMCTL32_DrawInsertMark(HDC hDC, const RECT *lpRect, COLORREF clrInsertMark {lCentre + 1, l2 - 3}, }; hOldPen = SelectObject(hDC, hPen); - PolyPolyline(hDC, aptInsertMark, adwPolyPoints, sizeof(adwPolyPoints)/sizeof(adwPolyPoints[0])); + PolyPolyline(hDC, aptInsertMark, adwPolyPoints, ARRAY_SIZE(adwPolyPoints)); SelectObject(hDC, hOldPen); DeleteObject(hPen); } diff --git a/dll/win32/comctl32/datetime.c b/dll/win32/comctl32/datetime.c index eb26ad107be..97f82260592 100644 --- a/dll/win32/comctl32/datetime.c +++ b/dll/win32/comctl32/datetime.c @@ -20,15 +20,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Oct. 20, 2004, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * -- DTS_APPCANPARSE * -- DTS_SHORTDATECENTURYFORMAT @@ -39,9 +30,21 @@ * -- FORMATCALLBACK */ -#include "comctl32.h" - #include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "comctl32.h" +#include "wine/debug.h" +#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(datetime); @@ -325,7 +328,7 @@ DATETIME_SetFormatW (DATETIME_INFO *infoPtr, LPCWSTR format) format_item = LOCALE_STIMEFORMAT; else /* DTS_SHORTDATEFORMAT */ format_item = LOCALE_SSHORTDATE; - GetLocaleInfoW(LOCALE_USER_DEFAULT, format_item, format_buf, sizeof(format_buf)/sizeof(format_buf[0])); + GetLocaleInfoW(LOCALE_USER_DEFAULT, format_item, format_buf, ARRAY_SIZE(format_buf)); format = format_buf; } @@ -446,8 +449,7 @@ DATETIME_ReturnTxt (const DATETIME_INFO *infoPtr, int count, LPWSTR result, int wsprintfW (result, fmt__2dW, date.wMonth); break; case THREECHARMONTH: - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1, - buffer, sizeof(buffer)/sizeof(buffer[0])); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+date.wMonth -1, buffer, ARRAY_SIZE(buffer)); wsprintfW (result, fmt__3sW, buffer); break; case FULLMONTH: @@ -739,14 +741,14 @@ DATETIME_Refresh (DATETIME_INFO *infoPtr, HDC hdc) INT oldBkMode = SetBkMode (hdc, TRANSPARENT); WCHAR txt[80]; - DATETIME_ReturnTxt (infoPtr, 0, txt, sizeof(txt)/sizeof(txt[0])); + DATETIME_ReturnTxt (infoPtr, 0, txt, ARRAY_SIZE(txt)); GetTextExtentPoint32W (hdc, txt, strlenW(txt), &size); rcDraw->bottom = size.cy + 2; prevright = infoPtr->checkbox.right = ((infoPtr->dwStyle & DTS_SHOWNONE) ? 18 : 2); for (i = 0; i < infoPtr->nrFields; i++) { - DATETIME_ReturnTxt (infoPtr, i, txt, sizeof(txt)/sizeof(txt[0])); + DATETIME_ReturnTxt (infoPtr, i, txt, ARRAY_SIZE(txt)); GetTextExtentPoint32W (hdc, txt, strlenW(txt), &size); DATETIME_ReturnFieldWidth (infoPtr, hdc, i, &fieldWidth); field = &infoPtr->fieldRect[i]; @@ -1527,7 +1529,7 @@ DATETIME_GetText (const DATETIME_INFO *infoPtr, INT count, LPWSTR dst) dst[0] = 0; for (i = 0; i < infoPtr->nrFields; i++) { - DATETIME_ReturnTxt(infoPtr, i, buf, sizeof(buf)/sizeof(buf[0])); + DATETIME_ReturnTxt(infoPtr, i, buf, ARRAY_SIZE(buf)); if ((strlenW(dst) + strlenW(buf)) < count) strcatW(dst, buf); else break; diff --git a/dll/win32/comctl32/dpa.c b/dll/win32/comctl32/dpa.c index dd73338b1af..36b3a422e8c 100644 --- a/dll/win32/comctl32/dpa.c +++ b/dll/win32/comctl32/dpa.c @@ -29,7 +29,19 @@ * http://members.ozemail.com.au/~geoffch/samples/win32/shell/comctl32 */ +#define COBJMACROS + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "commctrl.h" +#include "objbase.h" + #include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(dpa); diff --git a/dll/win32/comctl32/draglist.c b/dll/win32/comctl32/draglist.c index 21a8ead97fd..ed52e5e9068 100644 --- a/dll/win32/comctl32/draglist.c +++ b/dll/win32/comctl32/draglist.c @@ -29,7 +29,16 @@ * */ +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(commctrl); diff --git a/dll/win32/comctl32/dsa.c b/dll/win32/comctl32/dsa.c index 27a926886f2..d5e82343fbd 100644 --- a/dll/win32/comctl32/dsa.c +++ b/dll/win32/comctl32/dsa.c @@ -29,7 +29,15 @@ * http://members.ozemail.com.au/~geoffch/samples/win32/shell/comctl32 */ +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "commctrl.h" + #include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(dsa); diff --git a/dll/win32/comctl32/edit.c b/dll/win32/comctl32/edit.c new file mode 100644 index 00000000000..dde7d3e4c5b --- /dev/null +++ b/dll/win32/comctl32/edit.c @@ -0,0 +1,5041 @@ +/* + * Edit control + * + * Copyright David W. Metcalfe, 1994 + * Copyright William Magro, 1995, 1996 + * Copyright Frans van Dorsselaer, 1996, 1997 + * Copyright Frank Richter, 2005 + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * TODO: + * - EDITBALLOONTIP structure + * - EM_GETCUEBANNER/Edit_GetCueBannerText + * - EM_HIDEBALLOONTIP/Edit_HideBalloonTip + * - EM_SETCUEBANNER/Edit_SetCueBannerText + * - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip + * - EM_GETIMESTATUS, EM_SETIMESTATUS + * - EN_ALIGN_LTR_EC + * - EN_ALIGN_RTL_EC + * - ES_OEMCONVERT + * + */ + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "imm.h" +#include "usp10.h" +#include "commctrl.h" +#include "uxtheme.h" +#include "vsstyle.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(edit); + +#define BUFLIMIT_INITIAL 30000 /* initial buffer size */ +#define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */ +#define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1)) +#define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */ + +/* + * extra flags for EDITSTATE.flags field + */ +#define EF_MODIFIED 0x0001 /* text has been modified */ +#define EF_FOCUSED 0x0002 /* we have input focus */ +#define EF_UPDATE 0x0004 /* notify parent of changed state */ +#define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */ +#define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */ +#define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a + wrapped line, instead of in front of the next character */ +#define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */ +#define EF_DIALOGMODE 0x0200 /* Indicates that we are inside a dialog window */ + +#define ID_CB_LISTBOX 1000 + +typedef enum +{ + END_0 = 0, /* line ends with terminating '\0' character */ + END_WRAP, /* line is wrapped */ + END_HARD, /* line ends with a hard return '\r\n' */ + END_SOFT, /* line ends with a soft return '\r\r\n' */ + END_RICH /* line ends with a single '\n' */ +} LINE_END; + +typedef struct tagLINEDEF { + INT length; /* bruto length of a line in bytes */ + INT net_length; /* netto length of a line in visible characters */ + LINE_END ending; + INT width; /* width of the line in pixels */ + INT index; /* line index into the buffer */ + SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data */ + struct tagLINEDEF *next; +} LINEDEF; + +typedef struct +{ + LPWSTR text; /* the actual contents of the control */ + UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */ + UINT buffer_size; /* the size of the buffer in characters */ + UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */ + HFONT font; /* NULL means standard system font */ + INT x_offset; /* scroll offset for multi lines this is in pixels + for single lines it's in characters */ + INT line_height; /* height of a screen line in pixels */ + INT char_width; /* average character width in pixels */ + DWORD style; /* sane version of wnd->dwStyle */ + WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */ + INT undo_insert_count; /* number of characters inserted in sequence */ + UINT undo_position; /* character index of the insertion and deletion */ + LPWSTR undo_text; /* deleted text */ + UINT undo_buffer_size; /* size of the deleted text buffer */ + INT selection_start; /* == selection_end if no selection */ + INT selection_end; /* == current caret position */ + WCHAR password_char; /* == 0 if no password char, and for multi line controls */ + INT left_margin; /* in pixels */ + INT right_margin; /* in pixels */ + RECT format_rect; + INT text_width; /* width of the widest line in pixels for multi line controls + and just line width for single line controls */ + INT region_posx; /* Position of cursor relative to region: */ + INT region_posy; /* -1: to left, 0: within, 1: to right */ + EDITWORDBREAKPROCW word_break_proc; + INT line_count; /* number of lines */ + INT y_offset; /* scroll offset in number of lines */ + BOOL bCaptureState; /* flag indicating whether mouse was captured */ + BOOL bEnableState; /* flag keeping the enable state */ + HWND hwndSelf; /* the our window handle */ + HWND hwndParent; /* Handle of parent for sending EN_* messages. + Even if parent will change, EN_* messages + should be sent to the first parent. */ + HWND hwndListBox; /* handle of ComboBox's listbox or NULL */ + INT wheelDeltaRemainder; /* scroll wheel delta left over after scrolling whole lines */ + /* + * only for multi line controls + */ + INT lock_count; /* amount of re-entries in the EditWndProc */ + INT tabs_count; + LPINT tabs; + LINEDEF *first_line_def; /* linked list of (soft) linebreaks */ + HLOCAL hloc32W; /* our unicode local memory block */ + HLOCAL hlocapp; /* The text buffer handle belongs to the app */ + /* + * IME Data + */ + UINT composition_len; /* length of composition, 0 == no composition */ + int composition_start; /* the character position for the composition */ + /* + * Uniscribe Data + */ + SCRIPT_LOGATTR *logAttr; + SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data for single line controls */ +} EDITSTATE; + + +#define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0) +#define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0) + +/* used for disabled or read-only edit control */ +#define EDIT_NOTIFY_PARENT(es, wNotifyCode) \ + do \ + { /* Notify parent which has created this edit control */ \ + TRACE("notification " #wNotifyCode " sent to hwnd=%p\n", es->hwndParent); \ + SendMessageW(es->hwndParent, WM_COMMAND, \ + MAKEWPARAM(GetWindowLongPtrW((es->hwndSelf),GWLP_ID), wNotifyCode), \ + (LPARAM)(es->hwndSelf)); \ + } while(0) + +static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap); + +/********************************************************************* + * + * EM_CANUNDO + * + */ +static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es) +{ + return (es->undo_insert_count || strlenW(es->undo_text)); +} + + +/********************************************************************* + * + * EM_EMPTYUNDOBUFFER + * + */ +static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es) +{ + es->undo_insert_count = 0; + *es->undo_text = '\0'; +} + +static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc) +{ + HBRUSH hbrush; + UINT msg; + + if ((!es->bEnableState || (es->style & ES_READONLY))) + msg = WM_CTLCOLORSTATIC; + else + msg = WM_CTLCOLOREDIT; + + /* Why do we notify to es->hwndParent, and we send this one to GetParent()? */ + hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf); + if (!hbrush) + hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf); + return hbrush; +} + + +static inline UINT get_text_length(EDITSTATE *es) +{ + if(es->text_length == (UINT)-1) + es->text_length = strlenW(es->text); + return es->text_length; +} + + +/********************************************************************* + * + * EDIT_WordBreakProc + * + * Find the beginning of words. + * Note: unlike the specs for a WordBreakProc, this function can + * only be called without linebreaks between s[0] up to + * s[count - 1]. Remember it is only called + * internally, so we can decide this for ourselves. + * Additionally we will always be breaking the full string. + * + */ +static INT EDIT_WordBreakProc(EDITSTATE *es, LPWSTR s, INT index, INT count, INT action) +{ + INT ret = 0; + + TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action); + + if(!s) return 0; + + if (!es->logAttr) + { + SCRIPT_ANALYSIS psa; + + memset(&psa,0,sizeof(SCRIPT_ANALYSIS)); + psa.eScript = SCRIPT_UNDEFINED; + + es->logAttr = heap_alloc(sizeof(SCRIPT_LOGATTR) * get_text_length(es)); + ScriptBreak(es->text, get_text_length(es), &psa, es->logAttr); + } + + switch (action) { + case WB_LEFT: + if (index) + index--; + while (index && !es->logAttr[index].fSoftBreak) + index--; + ret = index; + break; + case WB_RIGHT: + if (!count) + break; + while (index < count && s[index] && !es->logAttr[index].fSoftBreak) + index++; + ret = index; + break; + case WB_ISDELIMITER: + ret = es->logAttr[index].fWhiteSpace; + break; + default: + ERR("unknown action code, please report !\n"); + break; + } + return ret; +} + + +/********************************************************************* + * + * EDIT_CallWordBreakProc + * + * Call appropriate WordBreakProc (internal or external). + * + * Note: The "start" argument should always be an index referring + * to es->text. The actual wordbreak proc might be + * 16 bit, so we can't always pass any 32 bit LPSTR. + * Hence we assume that es->text is the buffer that holds + * the string under examination (we can decide this for ourselves). + * + */ +static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action) +{ + INT ret; + + if (es->word_break_proc) + ret = es->word_break_proc(es->text + start, index, count, action); + else + ret = EDIT_WordBreakProc(es, es->text, index + start, count + start, action) - start; + + return ret; +} + +static inline void EDIT_InvalidateUniscribeData_linedef(LINEDEF *line_def) +{ + if (line_def->ssa) + { + ScriptStringFree(&line_def->ssa); + line_def->ssa = NULL; + } +} + +static inline void EDIT_InvalidateUniscribeData(EDITSTATE *es) +{ + LINEDEF *line_def = es->first_line_def; + while (line_def) + { + EDIT_InvalidateUniscribeData_linedef(line_def); + line_def = line_def->next; + } + if (es->ssa) + { + ScriptStringFree(&es->ssa); + es->ssa = NULL; + } +} + +static SCRIPT_STRING_ANALYSIS EDIT_UpdateUniscribeData_linedef(EDITSTATE *es, HDC dc, LINEDEF *line_def) +{ + if (!line_def) + return NULL; + + if (line_def->net_length && !line_def->ssa) + { + int index = line_def->index; + HFONT old_font = NULL; + HDC udc = dc; + SCRIPT_TABDEF tabdef; + HRESULT hr; + + if (!udc) + udc = GetDC(es->hwndSelf); + if (es->font) + old_font = SelectObject(udc, es->font); + + tabdef.cTabStops = es->tabs_count; + tabdef.iScale = GdiGetCharDimensions(udc, NULL, NULL); + tabdef.pTabStops = es->tabs; + tabdef.iTabOrigin = 0; + + hr = ScriptStringAnalyse(udc, &es->text[index], line_def->net_length, + (1.5*line_def->net_length+16), -1, + SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_TAB, -1, + NULL, NULL, NULL, &tabdef, NULL, &line_def->ssa); + if (FAILED(hr)) + { + WARN("ScriptStringAnalyse failed (%x)\n",hr); + line_def->ssa = NULL; + } + + if (es->font) + SelectObject(udc, old_font); + if (udc != dc) + ReleaseDC(es->hwndSelf, udc); + } + + return line_def->ssa; +} + +static SCRIPT_STRING_ANALYSIS EDIT_UpdateUniscribeData(EDITSTATE *es, HDC dc, INT line) +{ + LINEDEF *line_def; + + if (!(es->style & ES_MULTILINE)) + { + if (!es->ssa) + { + INT length = get_text_length(es); + HFONT old_font = NULL; + HDC udc = dc; + + if (!udc) + udc = GetDC(es->hwndSelf); + if (es->font) + old_font = SelectObject(udc, es->font); + + if (es->style & ES_PASSWORD) + ScriptStringAnalyse(udc, &es->password_char, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_PASSWORD, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa); + else + ScriptStringAnalyse(udc, es->text, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa); + + if (es->font) + SelectObject(udc, old_font); + if (udc != dc) + ReleaseDC(es->hwndSelf, udc); + } + return es->ssa; + } + else + { + line_def = es->first_line_def; + while (line_def && line) + { + line_def = line_def->next; + line--; + } + + return EDIT_UpdateUniscribeData_linedef(es,dc,line_def); + } +} + +static inline INT get_vertical_line_count(EDITSTATE *es) +{ + INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height; + return max(1,vlc); +} + +/********************************************************************* + * + * EDIT_BuildLineDefs_ML + * + * Build linked list of text lines. + * Lines can end with '\0' (last line), a character (if it is wrapped), + * a soft return '\r\r\n' or a hard return '\r\n' + * + */ +static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn) +{ + LPWSTR current_position, cp; + INT fw; + LINEDEF *current_line; + LINEDEF *previous_line; + LINEDEF *start_line; + INT line_index = 0, nstart_line, nstart_index; + INT line_count = es->line_count; + INT orig_net_length; + RECT rc; + INT vlc; + + if (istart == iend && delta == 0) + return; + + previous_line = NULL; + current_line = es->first_line_def; + + /* Find starting line. istart must lie inside an existing line or + * at the end of buffer */ + do { + if (istart < current_line->index + current_line->length || + current_line->ending == END_0) + break; + + previous_line = current_line; + current_line = current_line->next; + line_index++; + } while (current_line); + + if (!current_line) /* Error occurred start is not inside previous buffer */ + { + FIXME(" modification occurred outside buffer\n"); + return; + } + + /* Remember start of modifications in order to calculate update region */ + nstart_line = line_index; + nstart_index = current_line->index; + + /* We must start to reformat from the previous line since the modifications + * may have caused the line to wrap upwards. */ + if (!(es->style & ES_AUTOHSCROLL) && line_index > 0) + { + line_index--; + current_line = previous_line; + } + start_line = current_line; + + fw = es->format_rect.right - es->format_rect.left; + current_position = es->text + current_line->index; + vlc = get_vertical_line_count(es); + do { + if (current_line != start_line) + { + if (!current_line || current_line->index + delta > current_position - es->text) + { + /* The buffer has been expanded, create a new line and + insert it into the link list */ + LINEDEF *new_line = heap_alloc_zero(sizeof(*new_line)); + new_line->next = previous_line->next; + previous_line->next = new_line; + current_line = new_line; + es->line_count++; + } + else if (current_line->index + delta < current_position - es->text) + { + /* The previous line merged with this line so we delete this extra entry */ + previous_line->next = current_line->next; + heap_free(current_line); + current_line = previous_line->next; + es->line_count--; + continue; + } + else /* current_line->index + delta == current_position */ + { + if (current_position - es->text > iend) + break; /* We reached end of line modifications */ + /* else recalculate this line */ + } + } + + current_line->index = current_position - es->text; + orig_net_length = current_line->net_length; + + /* Find end of line */ + cp = current_position; + while (*cp) { + if (*cp == '\n') break; + if ((*cp == '\r') && (*(cp + 1) == '\n')) + break; + cp++; + } + + /* Mark type of line termination */ + if (!(*cp)) { + current_line->ending = END_0; + current_line->net_length = strlenW(current_position); + } else if ((cp > current_position) && (*(cp - 1) == '\r')) { + current_line->ending = END_SOFT; + current_line->net_length = cp - current_position - 1; + } else if (*cp == '\n') { + current_line->ending = END_RICH; + current_line->net_length = cp - current_position; + } else { + current_line->ending = END_HARD; + current_line->net_length = cp - current_position; + } + + if (current_line->net_length) + { + const SIZE *sz; + EDIT_InvalidateUniscribeData_linedef(current_line); + EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); + if (current_line->ssa) + { + sz = ScriptString_pSize(current_line->ssa); + /* Calculate line width */ + current_line->width = sz->cx; + } + else current_line->width = es->char_width * current_line->net_length; + } + else current_line->width = 0; + + /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */ + +/* Line breaks just look back from the end and find the next break and try that. */ + + if (!(es->style & ES_AUTOHSCROLL)) { + if (current_line->width > fw && fw > es->char_width) { + + INT prev, next; + int w; + const SIZE *sz; + float d; + + prev = current_line->net_length - 1; + w = current_line->net_length; + d = (float)current_line->width/(float)fw; + if (d > 1.2f) d -= 0.2f; + next = prev/d; + if (next >= prev) next = prev-1; + do { + prev = EDIT_CallWordBreakProc(es, current_position - es->text, + next, current_line->net_length, WB_LEFT); + current_line->net_length = prev; + EDIT_InvalidateUniscribeData_linedef(current_line); + EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); + if (current_line->ssa) + sz = ScriptString_pSize(current_line->ssa); + else sz = 0; + if (sz) + current_line->width = sz->cx; + else + prev = 0; + next = prev - 1; + } while (prev && current_line->width > fw); + current_line->net_length = w; + + if (prev == 0) { /* Didn't find a line break so force a break */ + INT *piDx; + const INT *count; + + EDIT_InvalidateUniscribeData_linedef(current_line); + EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); + + if (current_line->ssa) + { + count = ScriptString_pcOutChars(current_line->ssa); + piDx = heap_alloc(sizeof(INT) * (*count)); + ScriptStringGetLogicalWidths(current_line->ssa,piDx); + + prev = current_line->net_length-1; + do { + current_line->width -= piDx[prev]; + prev--; + } while ( prev > 0 && current_line->width > fw); + if (prev<=0) + prev = 1; + heap_free(piDx); + } + else + prev = (fw / es->char_width); + } + + /* If the first line we are calculating, wrapped before istart, we must + * adjust istart in order for this to be reflected in the update region. */ + if (current_line->index == nstart_index && istart > current_line->index + prev) + istart = current_line->index + prev; + /* else if we are updating the previous line before the first line we + * are re-calculating and it expanded */ + else if (current_line == start_line && + current_line->index != nstart_index && orig_net_length < prev) + { + /* Line expanded due to an upwards line wrap so we must partially include + * previous line in update region */ + nstart_line = line_index; + nstart_index = current_line->index; + istart = current_line->index + orig_net_length; + } + + current_line->net_length = prev; + current_line->ending = END_WRAP; + + if (current_line->net_length > 0) + { + EDIT_UpdateUniscribeData_linedef(es, NULL, current_line); + if (current_line->ssa) + { + sz = ScriptString_pSize(current_line->ssa); + current_line->width = sz->cx; + } + else + current_line->width = 0; + } + else current_line->width = 0; + } + else if (current_line == start_line && + current_line->index != nstart_index && + orig_net_length < current_line->net_length) { + /* The previous line expanded but it's still not as wide as the client rect */ + /* The expansion is due to an upwards line wrap so we must partially include + it in the update region */ + nstart_line = line_index; + nstart_index = current_line->index; + istart = current_line->index + orig_net_length; + } + } + + + /* Adjust length to include line termination */ + switch (current_line->ending) { + case END_SOFT: + current_line->length = current_line->net_length + 3; + break; + case END_RICH: + current_line->length = current_line->net_length + 1; + break; + case END_HARD: + current_line->length = current_line->net_length + 2; + break; + case END_WRAP: + case END_0: + current_line->length = current_line->net_length; + break; + } + es->text_width = max(es->text_width, current_line->width); + current_position += current_line->length; + previous_line = current_line; + + /* Discard data for non-visible lines. It will be calculated as needed */ + if ((line_index < es->y_offset) || (line_index > es->y_offset + vlc)) + EDIT_InvalidateUniscribeData_linedef(current_line); + + current_line = current_line->next; + line_index++; + } while (previous_line->ending != END_0); + + /* Finish adjusting line indexes by delta or remove hanging lines */ + if (previous_line->ending == END_0) + { + LINEDEF *pnext = NULL; + + previous_line->next = NULL; + while (current_line) + { + pnext = current_line->next; + EDIT_InvalidateUniscribeData_linedef(current_line); + heap_free(current_line); + current_line = pnext; + es->line_count--; + } + } + else if (delta != 0) + { + while (current_line) + { + current_line->index += delta; + current_line = current_line->next; + } + } + + /* Calculate rest of modification rectangle */ + if (hrgn) + { + HRGN tmphrgn; + /* + * We calculate two rectangles. One for the first line which may have + * an indent with respect to the format rect. The other is a format-width + * rectangle that spans the rest of the lines that changed or moved. + */ + rc.top = es->format_rect.top + nstart_line * es->line_height - + (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */ + rc.bottom = rc.top + es->line_height; + if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) + rc.left = es->format_rect.left; + else + rc.left = LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE)); + rc.right = es->format_rect.right; + SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom); + + rc.top = rc.bottom; + rc.left = es->format_rect.left; + rc.right = es->format_rect.right; + /* + * If lines were added or removed we must re-paint the remainder of the + * lines since the remaining lines were either shifted up or down. + */ + if (line_count < es->line_count) /* We added lines */ + rc.bottom = es->line_count * es->line_height; + else if (line_count > es->line_count) /* We removed lines */ + rc.bottom = line_count * es->line_height; + else + rc.bottom = line_index * es->line_height; + rc.bottom += es->format_rect.top; + rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */ + tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); + CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR); + DeleteObject(tmphrgn); + } +} + +/********************************************************************* + * + * EDIT_CalcLineWidth_SL + * + */ +static void EDIT_CalcLineWidth_SL(EDITSTATE *es) +{ + EDIT_UpdateUniscribeData(es, NULL, 0); + if (es->ssa) + { + const SIZE *size; + size = ScriptString_pSize(es->ssa); + es->text_width = size->cx; + } + else + es->text_width = 0; +} + +/********************************************************************* + * + * EDIT_CharFromPos + * + * Beware: This is not the function called on EM_CHARFROMPOS + * The position _can_ be outside the formatting / client + * rectangle + * The return value is only the character index + * + */ +static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap) +{ + INT index; + + if (es->style & ES_MULTILINE) { + int trailing; + INT line = (y - es->format_rect.top) / es->line_height + es->y_offset; + INT line_index = 0; + LINEDEF *line_def = es->first_line_def; + EDIT_UpdateUniscribeData(es, NULL, line); + while ((line > 0) && line_def->next) { + line_index += line_def->length; + line_def = line_def->next; + line--; + } + + x += es->x_offset - es->format_rect.left; + if (es->style & ES_RIGHT) + x -= (es->format_rect.right - es->format_rect.left) - line_def->width; + else if (es->style & ES_CENTER) + x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2; + if (x >= line_def->width) { + if (after_wrap) + *after_wrap = (line_def->ending == END_WRAP); + return line_index + line_def->net_length; + } + if (x <= 0 || !line_def->ssa) { + if (after_wrap) + *after_wrap = FALSE; + return line_index; + } + + ScriptStringXtoCP(line_def->ssa, x , &index, &trailing); + if (trailing) index++; + index += line_index; + if (after_wrap) + *after_wrap = ((index == line_index + line_def->net_length) && + (line_def->ending == END_WRAP)); + } else { + INT xoff = 0; + INT trailing; + if (after_wrap) + *after_wrap = FALSE; + x -= es->format_rect.left; + if (!x) + return es->x_offset; + + if (!es->x_offset) + { + INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width; + if (es->style & ES_RIGHT) + x -= indent; + else if (es->style & ES_CENTER) + x -= indent / 2; + } + + EDIT_UpdateUniscribeData(es, NULL, 0); + if (es->x_offset) + { + if (es->ssa) + { + if (es->x_offset>= get_text_length(es)) + { + const SIZE *size; + size = ScriptString_pSize(es->ssa); + xoff = size->cx; + } + ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff); + } + else + xoff = 0; + } + if (x < 0) + { + if (x + xoff > 0 || !es->ssa) + { + ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing); + if (trailing) index++; + } + else + index = 0; + } + else + { + if (x) + { + const SIZE *size = NULL; + if (es->ssa) + size = ScriptString_pSize(es->ssa); + if (!size) + index = 0; + else if (x > size->cx) + index = get_text_length(es); + else if (es->ssa) + { + ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing); + if (trailing) index++; + } + else + index = 0; + } + else + index = es->x_offset; + } + } + return index; +} + + +/********************************************************************* + * + * EDIT_ConfinePoint + * + * adjusts the point to be within the formatting rectangle + * (so CharFromPos returns the nearest _visible_ character) + * + */ +static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y) +{ + *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1); + *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1); +} + + +/********************************************************************* + * + * EM_LINEFROMCHAR + * + */ +static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index) +{ + INT line; + LINEDEF *line_def; + + if (!(es->style & ES_MULTILINE)) + return 0; + if (index > (INT)get_text_length(es)) + return es->line_count - 1; + if (index == -1) + index = min(es->selection_start, es->selection_end); + + line = 0; + line_def = es->first_line_def; + index -= line_def->length; + while ((index >= 0) && line_def->next) { + line++; + line_def = line_def->next; + index -= line_def->length; + } + return line; +} + + +/********************************************************************* + * + * EM_LINEINDEX + * + */ +static INT EDIT_EM_LineIndex(const EDITSTATE *es, INT line) +{ + INT line_index; + const LINEDEF *line_def; + + if (!(es->style & ES_MULTILINE)) + return 0; + if (line >= es->line_count) + return -1; + + line_index = 0; + line_def = es->first_line_def; + if (line == -1) { + INT index = es->selection_end - line_def->length; + while ((index >= 0) && line_def->next) { + line_index += line_def->length; + line_def = line_def->next; + index -= line_def->length; + } + } else { + while (line > 0) { + line_index += line_def->length; + line_def = line_def->next; + line--; + } + } + return line_index; +} + + +/********************************************************************* + * + * EM_LINELENGTH + * + */ +static INT EDIT_EM_LineLength(EDITSTATE *es, INT index) +{ + LINEDEF *line_def; + + if (!(es->style & ES_MULTILINE)) + return get_text_length(es); + + if (index == -1) { + /* get the number of remaining non-selected chars of selected lines */ + INT32 l; /* line number */ + INT32 li; /* index of first char in line */ + INT32 count; + l = EDIT_EM_LineFromChar(es, es->selection_start); + /* # chars before start of selection area */ + count = es->selection_start - EDIT_EM_LineIndex(es, l); + l = EDIT_EM_LineFromChar(es, es->selection_end); + /* # chars after end of selection */ + li = EDIT_EM_LineIndex(es, l); + count += li + EDIT_EM_LineLength(es, li) - es->selection_end; + return count; + } + line_def = es->first_line_def; + index -= line_def->length; + while ((index >= 0) && line_def->next) { + line_def = line_def->next; + index -= line_def->length; + } + return line_def->net_length; +} + + +/********************************************************************* + * + * EM_POSFROMCHAR + * + */ +static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap) +{ + INT len = get_text_length(es); + INT l; + INT li; + INT x = 0; + INT y = 0; + INT w; + INT lw; + LINEDEF *line_def; + + index = min(index, len); + if (es->style & ES_MULTILINE) { + l = EDIT_EM_LineFromChar(es, index); + EDIT_UpdateUniscribeData(es, NULL, l); + + y = (l - es->y_offset) * es->line_height; + li = EDIT_EM_LineIndex(es, l); + if (after_wrap && (li == index) && l) { + INT l2 = l - 1; + line_def = es->first_line_def; + while (l2) { + line_def = line_def->next; + l2--; + } + if (line_def->ending == END_WRAP) { + l--; + y -= es->line_height; + li = EDIT_EM_LineIndex(es, l); + } + } + + line_def = es->first_line_def; + while (line_def->index != li) + line_def = line_def->next; + + lw = line_def->width; + w = es->format_rect.right - es->format_rect.left; + if (line_def->ssa) + { + ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x); + x -= es->x_offset; + } + else + x = es->x_offset; + + if (es->style & ES_RIGHT) + x = w - (lw - x); + else if (es->style & ES_CENTER) + x += (w - lw) / 2; + } else { + INT xoff = 0; + INT xi = 0; + EDIT_UpdateUniscribeData(es, NULL, 0); + if (es->x_offset) + { + if (es->ssa) + { + if (es->x_offset >= get_text_length(es)) + { + int leftover = es->x_offset - get_text_length(es); + if (es->ssa) + { + const SIZE *size; + size = ScriptString_pSize(es->ssa); + xoff = size->cx; + } + else + xoff = 0; + xoff += es->char_width * leftover; + } + else + ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff); + } + else + xoff = 0; + } + if (index) + { + if (index >= get_text_length(es)) + { + if (es->ssa) + { + const SIZE *size; + size = ScriptString_pSize(es->ssa); + xi = size->cx; + } + else + xi = 0; + } + else if (es->ssa) + ScriptStringCPtoX(es->ssa, index, FALSE, &xi); + else + xi = 0; + } + x = xi - xoff; + + if (index >= es->x_offset) { + if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER))) + { + w = es->format_rect.right - es->format_rect.left; + if (w > es->text_width) + { + if (es->style & ES_RIGHT) + x += w - es->text_width; + else if (es->style & ES_CENTER) + x += (w - es->text_width) / 2; + } + } + } + y = 0; + } + x += es->format_rect.left; + y += es->format_rect.top; + return MAKELONG((INT16)x, (INT16)y); +} + + +/********************************************************************* + * + * EDIT_GetLineRect + * + * Calculates the bounding rectangle for a line from a starting + * column to an ending column. + * + */ +static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc) +{ + SCRIPT_STRING_ANALYSIS ssa; + INT line_index = 0; + INT pt1, pt2, pt3; + + if (es->style & ES_MULTILINE) + { + const LINEDEF *line_def = NULL; + rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height; + if (line >= es->line_count) + return; + + line_def = es->first_line_def; + if (line == -1) { + INT index = es->selection_end - line_def->length; + while ((index >= 0) && line_def->next) { + line_index += line_def->length; + line_def = line_def->next; + index -= line_def->length; + } + } else { + while (line > 0) { + line_index += line_def->length; + line_def = line_def->next; + line--; + } + } + ssa = line_def->ssa; + } + else + { + line_index = 0; + rc->top = es->format_rect.top; + ssa = es->ssa; + } + + rc->bottom = rc->top + es->line_height; + pt1 = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE)); + pt2 = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE)); + if (ssa) + { + ScriptStringCPtoX(ssa, scol, FALSE, &pt3); + pt3+=es->format_rect.left; + } + else pt3 = pt1; + rc->right = max(max(pt1 , pt2),pt3); + rc->left = min(min(pt1, pt2),pt3); +} + + +static inline void text_buffer_changed(EDITSTATE *es) +{ + es->text_length = (UINT)-1; + + heap_free( es->logAttr ); + es->logAttr = NULL; + EDIT_InvalidateUniscribeData(es); +} + +/********************************************************************* + * EDIT_LockBuffer + * + */ +static void EDIT_LockBuffer(EDITSTATE *es) +{ + if (!es->text) + { + if (!es->hloc32W) + return; + + es->text = LocalLock(es->hloc32W); + } + + es->lock_count++; +} + + +/********************************************************************* + * + * EDIT_UnlockBuffer + * + */ +static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force) +{ + /* Edit window might be already destroyed */ + if (!IsWindow(es->hwndSelf)) + { + WARN("edit hwnd %p already destroyed\n", es->hwndSelf); + return; + } + + if (!es->lock_count) + { + ERR("lock_count == 0 ... please report\n"); + return; + } + + if (!es->text) + { + ERR("es->text == 0 ... please report\n"); + return; + } + + if (force || (es->lock_count == 1)) + { + if (es->hloc32W) + { + LocalUnlock(es->hloc32W); + es->text = NULL; + } + else + { + ERR("no buffer ... please report\n"); + return; + } + + } + + es->lock_count--; +} + + +/********************************************************************* + * + * EDIT_MakeFit + * + * Try to fit size + 1 characters in the buffer. + */ +static BOOL EDIT_MakeFit(EDITSTATE *es, UINT size) +{ + HLOCAL hNew32W; + + if (size <= es->buffer_size) + return TRUE; + + TRACE("trying to ReAlloc to %d+1 characters\n", size); + + /* Force edit to unlock its buffer. es->text now NULL */ + EDIT_UnlockBuffer(es, TRUE); + + if (es->hloc32W) { + UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR)); + if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) { + TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W); + es->hloc32W = hNew32W; + es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1; + } + } + + EDIT_LockBuffer(es); + + if (es->buffer_size < size) { + WARN("FAILED ! We now have %d+1\n", es->buffer_size); + EDIT_NOTIFY_PARENT(es, EN_ERRSPACE); + return FALSE; + } else { + TRACE("We now have %d+1\n", es->buffer_size); + return TRUE; + } +} + + +/********************************************************************* + * + * EDIT_MakeUndoFit + * + * Try to fit size + 1 bytes in the undo buffer. + * + */ +static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size) +{ + UINT alloc_size; + + if (size <= es->undo_buffer_size) + return TRUE; + + TRACE("trying to ReAlloc to %d+1\n", size); + + alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR)); + if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) { + es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1; + return TRUE; + } + else + { + WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size); + return FALSE; + } +} + + +/********************************************************************* + * + * EDIT_UpdateTextRegion + * + */ +static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase) +{ + if (es->flags & EF_UPDATE) { + es->flags &= ~EF_UPDATE; + EDIT_NOTIFY_PARENT(es, EN_UPDATE); + } + InvalidateRgn(es->hwndSelf, hrgn, bErase); +} + + +/********************************************************************* + * + * EDIT_UpdateText + * + */ +static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase) +{ + if (es->flags & EF_UPDATE) { + es->flags &= ~EF_UPDATE; + EDIT_NOTIFY_PARENT(es, EN_UPDATE); + } + InvalidateRect(es->hwndSelf, rc, bErase); +} + +/********************************************************************* + * + * EDIT_SL_InvalidateText + * + * Called from EDIT_InvalidateText(). + * Does the job for single-line controls only. + * + */ +static void EDIT_SL_InvalidateText(EDITSTATE *es, INT start, INT end) +{ + RECT line_rect; + RECT rc; + + EDIT_GetLineRect(es, 0, start, end, &line_rect); + if (IntersectRect(&rc, &line_rect, &es->format_rect)) + EDIT_UpdateText(es, &rc, TRUE); +} + +/********************************************************************* + * + * EDIT_ML_InvalidateText + * + * Called from EDIT_InvalidateText(). + * Does the job for multi-line controls only. + * + */ +static void EDIT_ML_InvalidateText(EDITSTATE *es, INT start, INT end) +{ + INT vlc = get_vertical_line_count(es); + INT sl = EDIT_EM_LineFromChar(es, start); + INT el = EDIT_EM_LineFromChar(es, end); + INT sc; + INT ec; + RECT rc1; + RECT rcWnd; + RECT rcLine; + RECT rcUpdate; + INT l; + + if ((el < es->y_offset) || (sl > es->y_offset + vlc)) + return; + + sc = start - EDIT_EM_LineIndex(es, sl); + ec = end - EDIT_EM_LineIndex(es, el); + if (sl < es->y_offset) { + sl = es->y_offset; + sc = 0; + } + if (el > es->y_offset + vlc) { + el = es->y_offset + vlc; + ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el)); + } + GetClientRect(es->hwndSelf, &rc1); + IntersectRect(&rcWnd, &rc1, &es->format_rect); + if (sl == el) { + EDIT_GetLineRect(es, sl, sc, ec, &rcLine); + if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) + EDIT_UpdateText(es, &rcUpdate, TRUE); + } else { + EDIT_GetLineRect(es, sl, sc, + EDIT_EM_LineLength(es, + EDIT_EM_LineIndex(es, sl)), + &rcLine); + if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) + EDIT_UpdateText(es, &rcUpdate, TRUE); + for (l = sl + 1 ; l < el ; l++) { + EDIT_GetLineRect(es, l, 0, + EDIT_EM_LineLength(es, + EDIT_EM_LineIndex(es, l)), + &rcLine); + if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) + EDIT_UpdateText(es, &rcUpdate, TRUE); + } + EDIT_GetLineRect(es, el, 0, ec, &rcLine); + if (IntersectRect(&rcUpdate, &rcWnd, &rcLine)) + EDIT_UpdateText(es, &rcUpdate, TRUE); + } +} + + +/********************************************************************* + * + * EDIT_InvalidateText + * + * Invalidate the text from offset start up to, but not including, + * offset end. Useful for (re)painting the selection. + * Regions outside the linewidth are not invalidated. + * end == -1 means end == TextLength. + * start and end need not be ordered. + * + */ +static void EDIT_InvalidateText(EDITSTATE *es, INT start, INT end) +{ + if (end == start) + return; + + if (end == -1) + end = get_text_length(es); + + if (end < start) { + INT tmp = start; + start = end; + end = tmp; + } + + if (es->style & ES_MULTILINE) + EDIT_ML_InvalidateText(es, start, end); + else + EDIT_SL_InvalidateText(es, start, end); +} + + +/********************************************************************* + * + * EDIT_EM_SetSel + * + * note: unlike the specs say: the order of start and end + * _is_ preserved in Windows. (i.e. start can be > end) + * In other words: this handler is OK + * + */ +static BOOL EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap) +{ + UINT old_start = es->selection_start; + UINT old_end = es->selection_end; + UINT len = get_text_length(es); + + if (start == old_start && end == old_end) + return FALSE; + + if (start == (UINT)-1) { + start = es->selection_end; + end = es->selection_end; + } else { + start = min(start, len); + end = min(end, len); + } + es->selection_start = start; + es->selection_end = end; + if (after_wrap) + es->flags |= EF_AFTER_WRAP; + else + es->flags &= ~EF_AFTER_WRAP; + /* Compute the necessary invalidation region. */ + /* Note that we don't need to invalidate regions which have + * "never" been selected, or those which are "still" selected. + * In fact, every time we hit a selection boundary, we can + * *toggle* whether we need to invalidate. Thus we can optimize by + * *sorting* the interval endpoints. Let's assume that we sort them + * in this order: + * start <= end <= old_start <= old_end + * Knuth 5.3.1 (p 183) assures us that this can be done optimally + * in 5 comparisons; i.e. it is impossible to do better than the + * following: */ + ORDER_UINT(end, old_end); + ORDER_UINT(start, old_start); + ORDER_UINT(old_start, old_end); + ORDER_UINT(start, end); + /* Note that at this point 'end' and 'old_start' are not in order, but + * start is definitely the min. and old_end is definitely the max. */ + if (end != old_start) + { +/* + * One can also do + * ORDER_UINT32(end, old_start); + * EDIT_InvalidateText(es, start, end); + * EDIT_InvalidateText(es, old_start, old_end); + * in place of the following if statement. + * (That would complete the optimal five-comparison four-element sort.) + */ + if (old_start > end ) + { + EDIT_InvalidateText(es, start, end); + EDIT_InvalidateText(es, old_start, old_end); + } + else + { + EDIT_InvalidateText(es, start, old_start); + EDIT_InvalidateText(es, end, old_end); + } + } + else EDIT_InvalidateText(es, start, old_end); + + return TRUE; +} + + +/********************************************************************* + * + * EDIT_UpdateScrollInfo + * + */ +static void EDIT_UpdateScrollInfo(EDITSTATE *es) +{ + if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) + { + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = es->line_count - 1; + si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height; + si.nPos = es->y_offset; + TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n", + si.nMin, si.nMax, si.nPage, si.nPos); + SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE); + } + + if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) + { + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = es->text_width - 1; + si.nPage = es->format_rect.right - es->format_rect.left; + si.nPos = es->x_offset; + TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n", + si.nMin, si.nMax, si.nPage, si.nPos); + SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE); + } +} + + +/********************************************************************* + * + * EDIT_EM_LineScroll_internal + * + * Version of EDIT_EM_LineScroll for internal use. + * It doesn't refuse if ES_MULTILINE is set and assumes that + * dx is in pixels, dy - in lines. + * + */ +static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy) +{ + INT nyoff; + INT x_offset_in_pixels; + INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) / + es->line_height; + + if (es->style & ES_MULTILINE) + { + x_offset_in_pixels = es->x_offset; + } + else + { + dy = 0; + x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE)); + } + + if (-dx > x_offset_in_pixels) + dx = -x_offset_in_pixels; + if (dx > es->text_width - x_offset_in_pixels) + dx = es->text_width - x_offset_in_pixels; + nyoff = max(0, es->y_offset + dy); + if (nyoff >= es->line_count - lines_per_page) + nyoff = max(0, es->line_count - lines_per_page); + dy = (es->y_offset - nyoff) * es->line_height; + if (dx || dy) { + RECT rc1; + RECT rc; + + es->y_offset = nyoff; + if(es->style & ES_MULTILINE) + es->x_offset += dx; + else + es->x_offset += dx / es->char_width; + + GetClientRect(es->hwndSelf, &rc1); + IntersectRect(&rc, &rc1, &es->format_rect); + ScrollWindowEx(es->hwndSelf, -dx, dy, + NULL, &rc, NULL, NULL, SW_INVALIDATE); + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); + } + if (dx && !(es->flags & EF_HSCROLL_TRACK)) + EDIT_NOTIFY_PARENT(es, EN_HSCROLL); + if (dy && !(es->flags & EF_VSCROLL_TRACK)) + EDIT_NOTIFY_PARENT(es, EN_VSCROLL); + return TRUE; +} + +/********************************************************************* + * + * EM_LINESCROLL + * + * NOTE: dx is in average character widths, dy - in lines; + * + */ +static BOOL EDIT_EM_LineScroll(EDITSTATE *es, INT dx, INT dy) +{ + if (!(es->style & ES_MULTILINE)) + return FALSE; + + dx *= es->char_width; + return EDIT_EM_LineScroll_internal(es, dx, dy); +} + + +/********************************************************************* + * + * EM_SCROLL + * + */ +static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action) +{ + INT dy; + + if (!(es->style & ES_MULTILINE)) + return (LRESULT)FALSE; + + dy = 0; + + switch (action) { + case SB_LINEUP: + if (es->y_offset) + dy = -1; + break; + case SB_LINEDOWN: + if (es->y_offset < es->line_count - 1) + dy = 1; + break; + case SB_PAGEUP: + if (es->y_offset) + dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height; + break; + case SB_PAGEDOWN: + if (es->y_offset < es->line_count - 1) + dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height; + break; + default: + return (LRESULT)FALSE; + } + if (dy) { + INT vlc = get_vertical_line_count(es); + /* check if we are going to move too far */ + if(es->y_offset + dy > es->line_count - vlc) + dy = max(es->line_count - vlc, 0) - es->y_offset; + + /* Notification is done in EDIT_EM_LineScroll */ + if(dy) { + EDIT_EM_LineScroll(es, 0, dy); + return MAKELONG(dy, TRUE); + } + + } + return (LRESULT)FALSE; +} + + +/********************************************************************* + * + * EDIT_SetCaretPos + * + */ +static void EDIT_SetCaretPos(EDITSTATE *es, INT pos, + BOOL after_wrap) +{ + LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap); + TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res)); + SetCaretPos((short)LOWORD(res), (short)HIWORD(res)); +} + + +/********************************************************************* + * + * EM_SCROLLCARET + * + */ +static void EDIT_EM_ScrollCaret(EDITSTATE *es) +{ + if (es->style & ES_MULTILINE) { + INT l; + INT vlc; + INT ww; + INT cw = es->char_width; + INT x; + INT dy = 0; + INT dx = 0; + + l = EDIT_EM_LineFromChar(es, es->selection_end); + x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)); + vlc = get_vertical_line_count(es); + if (l >= es->y_offset + vlc) + dy = l - vlc + 1 - es->y_offset; + if (l < es->y_offset) + dy = l - es->y_offset; + ww = es->format_rect.right - es->format_rect.left; + if (x < es->format_rect.left) + dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw; + if (x > es->format_rect.right) + dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw; + if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc))) + { + /* check if we are going to move too far */ + if(es->x_offset + dx + ww > es->text_width) + dx = es->text_width - ww - es->x_offset; + if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc))) + EDIT_EM_LineScroll_internal(es, dx, dy); + } + } else { + INT x; + INT goal; + INT format_width; + + x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE)); + format_width = es->format_rect.right - es->format_rect.left; + if (x < es->format_rect.left) { + goal = es->format_rect.left + format_width / HSCROLL_FRACTION; + do { + es->x_offset--; + x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE)); + } while ((x < goal) && es->x_offset); + /* FIXME: use ScrollWindow() somehow to improve performance */ + EDIT_UpdateText(es, NULL, TRUE); + } else if (x > es->format_rect.right) { + INT x_last; + INT len = get_text_length(es); + goal = es->format_rect.right - format_width / HSCROLL_FRACTION; + do { + es->x_offset++; + x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE)); + x_last = (short)LOWORD(EDIT_EM_PosFromChar(es, len, FALSE)); + } while ((x > goal) && (x_last > es->format_rect.right)); + /* FIXME: use ScrollWindow() somehow to improve performance */ + EDIT_UpdateText(es, NULL, TRUE); + } + } + + if(es->flags & EF_FOCUSED) + EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP); +} + + +/********************************************************************* + * + * EDIT_MoveBackward + * + */ +static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend) +{ + INT e = es->selection_end; + + if (e) { + e--; + if ((es->style & ES_MULTILINE) && e && + (es->text[e - 1] == '\r') && (es->text[e] == '\n')) { + e--; + if (e && (es->text[e - 1] == '\r')) + e--; + } + } + EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveDown_ML + * + * Only for multi line controls + * Move the caret one line down, on a column with the nearest + * x coordinate on the screen (might be a different column). + * + */ +static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend) +{ + INT s = es->selection_start; + INT e = es->selection_end; + BOOL after_wrap = (es->flags & EF_AFTER_WRAP); + LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); + INT x = (short)LOWORD(pos); + INT y = (short)HIWORD(pos); + + e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap); + if (!extend) + s = e; + EDIT_EM_SetSel(es, s, e, after_wrap); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveEnd + * + */ +static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl) +{ + BOOL after_wrap = FALSE; + INT e; + + /* Pass a high value in x to make sure of receiving the end of the line */ + if (!ctrl && (es->style & ES_MULTILINE)) + e = EDIT_CharFromPos(es, 0x3fffffff, + HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap); + else + e = get_text_length(es); + EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveForward + * + */ +static void EDIT_MoveForward(EDITSTATE *es, BOOL extend) +{ + INT e = es->selection_end; + + if (es->text[e]) { + e++; + if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) { + if (es->text[e] == '\n') + e++; + else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n')) + e += 2; + } + } + EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveHome + * + * Home key: move to beginning of line. + * + */ +static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl) +{ + INT e; + + /* Pass the x_offset in x to make sure of receiving the first position of the line */ + if (!ctrl && (es->style & ES_MULTILINE)) + e = EDIT_CharFromPos(es, -es->x_offset, + HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL); + else + e = 0; + EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MovePageDown_ML + * + * Only for multi line controls + * Move the caret one page down, on a column with the nearest + * x coordinate on the screen (might be a different column). + * + */ +static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend) +{ + INT s = es->selection_start; + INT e = es->selection_end; + BOOL after_wrap = (es->flags & EF_AFTER_WRAP); + LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); + INT x = (short)LOWORD(pos); + INT y = (short)HIWORD(pos); + + e = EDIT_CharFromPos(es, x, + y + (es->format_rect.bottom - es->format_rect.top), + &after_wrap); + if (!extend) + s = e; + EDIT_EM_SetSel(es, s, e, after_wrap); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MovePageUp_ML + * + * Only for multi line controls + * Move the caret one page up, on a column with the nearest + * x coordinate on the screen (might be a different column). + * + */ +static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend) +{ + INT s = es->selection_start; + INT e = es->selection_end; + BOOL after_wrap = (es->flags & EF_AFTER_WRAP); + LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); + INT x = (short)LOWORD(pos); + INT y = (short)HIWORD(pos); + + e = EDIT_CharFromPos(es, x, + y - (es->format_rect.bottom - es->format_rect.top), + &after_wrap); + if (!extend) + s = e; + EDIT_EM_SetSel(es, s, e, after_wrap); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveUp_ML + * + * Only for multi line controls + * Move the caret one line up, on a column with the nearest + * x coordinate on the screen (might be a different column). + * + */ +static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend) +{ + INT s = es->selection_start; + INT e = es->selection_end; + BOOL after_wrap = (es->flags & EF_AFTER_WRAP); + LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap); + INT x = (short)LOWORD(pos); + INT y = (short)HIWORD(pos); + + e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap); + if (!extend) + s = e; + EDIT_EM_SetSel(es, s, e, after_wrap); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveWordBackward + * + */ +static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend) +{ + INT s = es->selection_start; + INT e = es->selection_end; + INT l; + INT ll; + INT li; + + l = EDIT_EM_LineFromChar(es, e); + ll = EDIT_EM_LineLength(es, e); + li = EDIT_EM_LineIndex(es, l); + if (e - li == 0) { + if (l) { + li = EDIT_EM_LineIndex(es, l - 1); + e = li + EDIT_EM_LineLength(es, li); + } + } else { + e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT); + } + if (!extend) + s = e; + EDIT_EM_SetSel(es, s, e, FALSE); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_MoveWordForward + * + */ +static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend) +{ + INT s = es->selection_start; + INT e = es->selection_end; + INT l; + INT ll; + INT li; + + l = EDIT_EM_LineFromChar(es, e); + ll = EDIT_EM_LineLength(es, e); + li = EDIT_EM_LineIndex(es, l); + if (e - li == ll) { + if ((es->style & ES_MULTILINE) && (l != es->line_count - 1)) + e = EDIT_EM_LineIndex(es, l + 1); + } else { + e = li + EDIT_CallWordBreakProc(es, + li, e - li + 1, ll, WB_RIGHT); + } + if (!extend) + s = e; + EDIT_EM_SetSel(es, s, e, FALSE); + EDIT_EM_ScrollCaret(es); +} + + +/********************************************************************* + * + * EDIT_PaintText + * + */ +static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev) +{ + COLORREF BkColor; + COLORREF TextColor; + LOGFONTW underline_font; + HFONT hUnderline = 0; + HFONT old_font = 0; + INT ret; + INT li; + INT BkMode; + SIZE size; + + if (!count) + return 0; + BkMode = GetBkMode(dc); + BkColor = GetBkColor(dc); + TextColor = GetTextColor(dc); + if (rev) { + if (es->composition_len == 0) + { + SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT)); + SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkMode( dc, OPAQUE); + } + else + { + HFONT current = GetCurrentObject(dc,OBJ_FONT); + GetObjectW(current,sizeof(LOGFONTW),&underline_font); + underline_font.lfUnderline = TRUE; + hUnderline = CreateFontIndirectW(&underline_font); + old_font = SelectObject(dc,hUnderline); + } + } + li = EDIT_EM_LineIndex(es, line); + if (es->style & ES_MULTILINE) { + ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count, + es->tabs_count, es->tabs, es->format_rect.left - es->x_offset)); + } else { + TextOutW(dc, x, y, es->text + li + col, count); + GetTextExtentPoint32W(dc, es->text + li + col, count, &size); + ret = size.cx; + } + if (rev) { + if (es->composition_len == 0) + { + SetBkColor(dc, BkColor); + SetTextColor(dc, TextColor); + SetBkMode( dc, BkMode); + } + else + { + if (old_font) + SelectObject(dc,old_font); + if (hUnderline) + DeleteObject(hUnderline); + } + } + return ret; +} + + +/********************************************************************* + * + * EDIT_PaintLine + * + */ +static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev) +{ + INT s = 0; + INT e = 0; + INT li = 0; + INT ll = 0; + INT x; + INT y; + LRESULT pos; + SCRIPT_STRING_ANALYSIS ssa; + + if (es->style & ES_MULTILINE) { + INT vlc = get_vertical_line_count(es); + + if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count)) + return; + } else if (line) + return; + + TRACE("line=%d\n", line); + + ssa = EDIT_UpdateUniscribeData(es, dc, line); + pos = EDIT_EM_PosFromChar(es, EDIT_EM_LineIndex(es, line), FALSE); + x = (short)LOWORD(pos); + y = (short)HIWORD(pos); + + if (es->style & ES_MULTILINE) + { + int line_idx = line; + x = -es->x_offset; + if (es->style & ES_RIGHT || es->style & ES_CENTER) + { + LINEDEF *line_def = es->first_line_def; + int w, lw; + + while (line_def && line_idx) + { + line_def = line_def->next; + line_idx--; + } + w = es->format_rect.right - es->format_rect.left; + lw = line_def->width; + + if (es->style & ES_RIGHT) + x = w - (lw - x); + else if (es->style & ES_CENTER) + x += (w - lw) / 2; + } + x += es->format_rect.left; + } + + if (rev) + { + li = EDIT_EM_LineIndex(es, line); + ll = EDIT_EM_LineLength(es, li); + s = min(es->selection_start, es->selection_end); + e = max(es->selection_start, es->selection_end); + s = min(li + ll, max(li, s)); + e = min(li + ll, max(li, e)); + } + + if (ssa) + ScriptStringOut(ssa, x, y, 0, &es->format_rect, s - li, e - li, FALSE); + else if (rev && (s != e) && + ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) { + x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE); + x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE); + x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE); + } else + x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE); +} + + +/********************************************************************* + * + * EDIT_AdjustFormatRect + * + * Adjusts the format rectangle for the current font and the + * current client rectangle. + * + */ +static void EDIT_AdjustFormatRect(EDITSTATE *es) +{ + RECT ClientRect; + + es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width); + if (es->style & ES_MULTILINE) + { + INT fw, vlc, max_x_offset, max_y_offset; + + vlc = get_vertical_line_count(es); + es->format_rect.bottom = es->format_rect.top + vlc * es->line_height; + + /* correct es->x_offset */ + fw = es->format_rect.right - es->format_rect.left; + max_x_offset = es->text_width - fw; + if(max_x_offset < 0) max_x_offset = 0; + if(es->x_offset > max_x_offset) + es->x_offset = max_x_offset; + + /* correct es->y_offset */ + max_y_offset = es->line_count - vlc; + if(max_y_offset < 0) max_y_offset = 0; + if(es->y_offset > max_y_offset) + es->y_offset = max_y_offset; + + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); + } + else + /* Windows doesn't care to fix text placement for SL controls */ + es->format_rect.bottom = es->format_rect.top + es->line_height; + + /* Always stay within the client area */ + GetClientRect(es->hwndSelf, &ClientRect); + es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom); + + if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) + EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); + + EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP); +} + + +/********************************************************************* + * + * EDIT_SetRectNP + * + * note: this is not (exactly) the handler called on EM_SETRECTNP + * it is also used to set the rect of a single line control + * + */ +static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc) +{ + LONG_PTR ExStyle; + INT bw, bh; + ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE); + + CopyRect(&es->format_rect, rc); + + if (ExStyle & WS_EX_CLIENTEDGE) { + es->format_rect.left++; + es->format_rect.right--; + + if (es->format_rect.bottom - es->format_rect.top + >= es->line_height + 2) + { + es->format_rect.top++; + es->format_rect.bottom--; + } + } + else if (es->style & WS_BORDER) { + bw = GetSystemMetrics(SM_CXBORDER) + 1; + bh = GetSystemMetrics(SM_CYBORDER) + 1; + InflateRect(&es->format_rect, -bw, 0); + if (es->format_rect.bottom - es->format_rect.top >= es->line_height + 2 * bh) + InflateRect(&es->format_rect, 0, -bh); + } + + es->format_rect.left += es->left_margin; + es->format_rect.right -= es->right_margin; + EDIT_AdjustFormatRect(es); +} + + +/********************************************************************* + * + * EM_CHARFROMPOS + * + * returns line number (not index) in high-order word of result. + * NB : Q137805 is unclear about this. POINT * pointer in lParam apply + * to Richedit, not to the edit control. Original documentation is valid. + * FIXME: do the specs mean to return -1 if outside client area or + * if outside formatting rectangle ??? + * + */ +static LRESULT EDIT_EM_CharFromPos(EDITSTATE *es, INT x, INT y) +{ + POINT pt; + RECT rc; + INT index; + + pt.x = x; + pt.y = y; + GetClientRect(es->hwndSelf, &rc); + if (!PtInRect(&rc, pt)) + return -1; + + index = EDIT_CharFromPos(es, x, y, NULL); + return MAKELONG(index, EDIT_EM_LineFromChar(es, index)); +} + + +/********************************************************************* + * + * EM_FMTLINES + * + * Enable or disable soft breaks. + * + * This means: insert or remove the soft linebreak character (\r\r\n). + * Take care to check if the text still fits the buffer after insertion. + * If not, notify with EN_ERRSPACE. + * + */ +static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol) +{ + es->flags &= ~EF_USE_SOFTBRK; + if (add_eol) { + es->flags |= EF_USE_SOFTBRK; + FIXME("soft break enabled, not implemented\n"); + } + return add_eol; +} + + +/********************************************************************* + * + * EM_GETHANDLE + * + * Hopefully this won't fire back at us. + * We always start with a fixed buffer in the local heap. + * Despite of the documentation says that the local heap is used + * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate + * buffer on the local heap. + * + */ +static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es) +{ + if (!(es->style & ES_MULTILINE)) + return 0; + + EDIT_UnlockBuffer(es, TRUE); + + /* The text buffer handle belongs to the app */ + es->hlocapp = es->hloc32W; + + TRACE("Returning %p, LocalSize() = %ld\n", es->hlocapp, LocalSize(es->hlocapp)); + return es->hlocapp; +} + + +/********************************************************************* + * + * EM_GETLINE + * + */ +static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPWSTR dst) +{ + INT line_len, dst_len; + LPWSTR src; + INT i; + + if (es->style & ES_MULTILINE) + { + if (line >= es->line_count) + return 0; + } + else + line = 0; + + i = EDIT_EM_LineIndex(es, line); + src = es->text + i; + line_len = EDIT_EM_LineLength(es, i); + dst_len = *(WORD *)dst; + + if (dst_len <= line_len) + { + memcpy(dst, src, dst_len * sizeof(WCHAR)); + return dst_len; + } + else /* Append 0 if enough space */ + { + memcpy(dst, src, line_len * sizeof(WCHAR)); + dst[line_len] = 0; + return line_len; + } +} + + +/********************************************************************* + * + * EM_GETSEL + * + */ +static LRESULT EDIT_EM_GetSel(const EDITSTATE *es, PUINT start, PUINT end) +{ + UINT s = es->selection_start; + UINT e = es->selection_end; + + ORDER_UINT(s, e); + if (start) + *start = s; + if (end) + *end = e; + return MAKELONG(s, e); +} + + +/********************************************************************* + * + * EM_REPLACESEL + * + * FIXME: handle ES_NUMBER and ES_OEMCONVERT here + * + */ +static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_replace, UINT strl, + BOOL send_update, BOOL honor_limit) +{ + UINT tl = get_text_length(es); + UINT utl; + UINT s; + UINT e; + UINT i; + UINT size; + LPWSTR p; + HRGN hrgn = 0; + LPWSTR buf = NULL; + UINT bufl; + + TRACE("%s, can_undo %d, send_update %d\n", + debugstr_wn(lpsz_replace, strl), can_undo, send_update); + + s = es->selection_start; + e = es->selection_end; + + EDIT_InvalidateUniscribeData(es); + if ((s == e) && !strl) + return; + + ORDER_UINT(s, e); + + size = tl - (e - s) + strl; + if (!size) + es->text_width = 0; + + /* Issue the EN_MAXTEXT notification and continue with replacing text + * so that buffer limit is honored. */ + if ((honor_limit) && (size > es->buffer_limit)) { + EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); + /* Buffer limit can be smaller than the actual length of text in combobox */ + if (es->buffer_limit < (tl - (e-s))) + strl = 0; + else + strl = min(strl, es->buffer_limit - (tl - (e-s))); + } + + if (!EDIT_MakeFit(es, tl - (e - s) + strl)) + return; + + if (e != s) { + /* there is something to be deleted */ + TRACE("deleting stuff.\n"); + bufl = e - s; + buf = heap_alloc((bufl + 1) * sizeof(WCHAR)); + if (!buf) return; + memcpy(buf, es->text + s, bufl * sizeof(WCHAR)); + buf[bufl] = 0; /* ensure 0 termination */ + /* now delete */ + strcpyW(es->text + s, es->text + e); + text_buffer_changed(es); + } + if (strl) { + /* there is an insertion */ + tl = get_text_length(es); + TRACE("inserting stuff (tl %d, strl %d, selstart %d (%s), text %s)\n", tl, strl, s, debugstr_w(es->text + s), debugstr_w(es->text)); + for (p = es->text + tl ; p >= es->text + s ; p--) + p[strl] = p[0]; + for (i = 0 , p = es->text + s ; i < strl ; i++) + p[i] = lpsz_replace[i]; + if(es->style & ES_UPPERCASE) + CharUpperBuffW(p, strl); + else if(es->style & ES_LOWERCASE) + CharLowerBuffW(p, strl); + text_buffer_changed(es); + } + if (es->style & ES_MULTILINE) + { + INT st = min(es->selection_start, es->selection_end); + INT vlc = get_vertical_line_count(es); + + hrgn = CreateRectRgn(0, 0, 0, 0); + EDIT_BuildLineDefs_ML(es, st, st + strl, + strl - abs(es->selection_end - es->selection_start), hrgn); + /* if text is too long undo all changes */ + if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) { + if (strl) + strcpyW(es->text + e, es->text + e + strl); + if (e != s) + for (i = 0 , p = es->text ; i < e - s ; i++) + p[i + s] = buf[i]; + text_buffer_changed(es); + EDIT_BuildLineDefs_ML(es, s, e, + abs(es->selection_end - es->selection_start) - strl, hrgn); + strl = 0; + e = s; + hrgn = CreateRectRgn(0, 0, 0, 0); + EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); + } + } + else { + INT fw = es->format_rect.right - es->format_rect.left; + EDIT_InvalidateUniscribeData(es); + EDIT_CalcLineWidth_SL(es); + /* remove chars that don't fit */ + if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) { + while ((es->text_width > fw) && s + strl >= s) { + strcpyW(es->text + s + strl - 1, es->text + s + strl); + strl--; + es->text_length = -1; + EDIT_InvalidateUniscribeData(es); + EDIT_CalcLineWidth_SL(es); + } + text_buffer_changed(es); + EDIT_NOTIFY_PARENT(es, EN_MAXTEXT); + } + } + + if (e != s) { + if (can_undo) { + utl = strlenW(es->undo_text); + if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) { + /* undo-buffer is extended to the right */ + EDIT_MakeUndoFit(es, utl + e - s); + memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR)); + (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */ + } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) { + /* undo-buffer is extended to the left */ + EDIT_MakeUndoFit(es, utl + e - s); + for (p = es->undo_text + utl ; p >= es->undo_text ; p--) + p[e - s] = p[0]; + for (i = 0 , p = es->undo_text ; i < e - s ; i++) + p[i] = buf[i]; + es->undo_position = s; + } else { + /* new undo-buffer */ + EDIT_MakeUndoFit(es, e - s); + memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR)); + es->undo_text[e - s] = 0; /* ensure 0 termination */ + es->undo_position = s; + } + /* any deletion makes the old insertion-undo invalid */ + es->undo_insert_count = 0; + } else + EDIT_EM_EmptyUndoBuffer(es); + } + if (strl) { + if (can_undo) { + if ((s == es->undo_position) || + ((es->undo_insert_count) && + (s == es->undo_position + es->undo_insert_count))) + /* + * insertion is new and at delete position or + * an extension to either left or right + */ + es->undo_insert_count += strl; + else { + /* new insertion undo */ + es->undo_position = s; + es->undo_insert_count = strl; + /* new insertion makes old delete-buffer invalid */ + *es->undo_text = '\0'; + } + } else + EDIT_EM_EmptyUndoBuffer(es); + } + + heap_free(buf); + + s += strl; + + /* If text has been deleted and we're right or center aligned then scroll rightward */ + if (es->style & (ES_RIGHT | ES_CENTER)) + { + INT delta = strl - abs(es->selection_end - es->selection_start); + + if (delta < 0 && es->x_offset) + { + if (abs(delta) > es->x_offset) + es->x_offset = 0; + else + es->x_offset += delta; + } + } + + EDIT_EM_SetSel(es, s, s, FALSE); + es->flags |= EF_MODIFIED; + if (send_update) es->flags |= EF_UPDATE; + if (hrgn) + { + EDIT_UpdateTextRegion(es, hrgn, TRUE); + DeleteObject(hrgn); + } + else + EDIT_UpdateText(es, NULL, TRUE); + + EDIT_EM_ScrollCaret(es); + + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); + + + if(send_update || (es->flags & EF_UPDATE)) + { + es->flags &= ~EF_UPDATE; + EDIT_NOTIFY_PARENT(es, EN_CHANGE); + } + EDIT_InvalidateUniscribeData(es); +} + + +/********************************************************************* + * + * EM_SETHANDLE + * + * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ??? + * + */ +static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc) +{ + if (!(es->style & ES_MULTILINE)) + return; + + if (!hloc) + return; + + EDIT_UnlockBuffer(es, TRUE); + + es->hloc32W = hloc; + es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1; + + /* The text buffer handle belongs to the control */ + es->hlocapp = NULL; + + EDIT_LockBuffer(es); + text_buffer_changed(es); + + es->x_offset = es->y_offset = 0; + es->selection_start = es->selection_end = 0; + EDIT_EM_EmptyUndoBuffer(es); + es->flags &= ~EF_MODIFIED; + es->flags &= ~EF_UPDATE; + EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); + EDIT_UpdateText(es, NULL, TRUE); + EDIT_EM_ScrollCaret(es); + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); +} + + +/********************************************************************* + * + * EM_SETLIMITTEXT + * + * NOTE: this version currently implements WinNT limits + * + */ +static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit) +{ + if (!limit) limit = ~0u; + if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe); + es->buffer_limit = limit; +} + + +/********************************************************************* + * + * EM_SETMARGINS + * + * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an + * action wParam despite what the docs say. EC_USEFONTINFO calculates the + * margin according to the textmetrics of the current font. + * + * When EC_USEFONTINFO is used in the non_cjk case the margins only + * change if the edit control is equal to or larger than a certain + * size. Though there is an exception for the empty client rect case + * with small font sizes. + */ +static BOOL is_cjk(UINT charset) +{ + switch(charset) + { + case SHIFTJIS_CHARSET: + case HANGUL_CHARSET: + case GB2312_CHARSET: + case CHINESEBIG5_CHARSET: + return TRUE; + } + /* HANGUL_CHARSET is strange, though treated as CJK by Win 8, it is + * not by other versions including Win 10. */ + return FALSE; +} + +static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, + WORD left, WORD right, BOOL repaint) +{ + TEXTMETRICW tm; + INT default_left_margin = 0; /* in pixels */ + INT default_right_margin = 0; /* in pixels */ + + /* Set the default margins depending on the font */ + if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) { + HDC dc = GetDC(es->hwndSelf); + HFONT old_font = SelectObject(dc, es->font); + LONG width = GdiGetCharDimensions(dc, &tm, NULL); + RECT rc; + + /* The default margins are only non zero for TrueType or Vector fonts */ + if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) { + if (!is_cjk(tm.tmCharSet)) { + default_left_margin = width / 2; + default_right_margin = width / 2; + + GetClientRect(es->hwndSelf, &rc); + if (rc.right - rc.left < (width / 2 + width) * 2 && + (width >= 28 || !IsRectEmpty(&rc)) ) { + default_left_margin = es->left_margin; + default_right_margin = es->right_margin; + } + } else { + /* FIXME: figure out the CJK values. They are not affected by the client rect. */ + default_left_margin = width / 2; + default_right_margin = width / 2; + } + } + SelectObject(dc, old_font); + ReleaseDC(es->hwndSelf, dc); + } + + if (action & EC_LEFTMARGIN) { + es->format_rect.left -= es->left_margin; + if (left != EC_USEFONTINFO) + es->left_margin = left; + else + es->left_margin = default_left_margin; + es->format_rect.left += es->left_margin; + } + + if (action & EC_RIGHTMARGIN) { + es->format_rect.right += es->right_margin; + if (right != EC_USEFONTINFO) + es->right_margin = right; + else + es->right_margin = default_right_margin; + es->format_rect.right -= es->right_margin; + } + + if (action & (EC_LEFTMARGIN | EC_RIGHTMARGIN)) { + EDIT_AdjustFormatRect(es); + if (repaint) EDIT_UpdateText(es, NULL, TRUE); + } + + TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin); +} + + +/********************************************************************* + * + * EM_SETPASSWORDCHAR + * + */ +static void EDIT_EM_SetPasswordChar(EDITSTATE *es, WCHAR c) +{ + LONG style; + + if (es->style & ES_MULTILINE) + return; + + if (es->password_char == c) + return; + + style = GetWindowLongW( es->hwndSelf, GWL_STYLE ); + es->password_char = c; + if (c) { + SetWindowLongW( es->hwndSelf, GWL_STYLE, style | ES_PASSWORD ); + es->style |= ES_PASSWORD; + } else { + SetWindowLongW( es->hwndSelf, GWL_STYLE, style & ~ES_PASSWORD ); + es->style &= ~ES_PASSWORD; + } + EDIT_InvalidateUniscribeData(es); + EDIT_UpdateText(es, NULL, TRUE); +} + + +/********************************************************************* + * + * EM_SETTABSTOPS + * + */ +static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, const INT *tabs) +{ + if (!(es->style & ES_MULTILINE)) + return FALSE; + heap_free(es->tabs); + es->tabs_count = count; + if (!count) + es->tabs = NULL; + else { + es->tabs = heap_alloc(count * sizeof(INT)); + memcpy(es->tabs, tabs, count * sizeof(INT)); + } + EDIT_InvalidateUniscribeData(es); + return TRUE; +} + + +/********************************************************************* + * + * EM_SETWORDBREAKPROC + * + */ +static void EDIT_EM_SetWordBreakProc(EDITSTATE *es, EDITWORDBREAKPROCW wbp) +{ + if (es->word_break_proc == wbp) + return; + + es->word_break_proc = wbp; + + if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) { + EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); + EDIT_UpdateText(es, NULL, TRUE); + } +} + + +/********************************************************************* + * + * EM_UNDO / WM_UNDO + * + */ +static BOOL EDIT_EM_Undo(EDITSTATE *es) +{ + INT ulength; + LPWSTR utext; + + /* As per MSDN spec, for a single-line edit control, + the return value is always TRUE */ + if( es->style & ES_READONLY ) + return !(es->style & ES_MULTILINE); + + ulength = strlenW(es->undo_text); + + utext = heap_alloc((ulength + 1) * sizeof(WCHAR)); + + strcpyW(utext, es->undo_text); + + TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n", + es->undo_insert_count, debugstr_w(utext)); + + EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE); + EDIT_EM_EmptyUndoBuffer(es); + EDIT_EM_ReplaceSel(es, TRUE, utext, ulength, TRUE, TRUE); + EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE); + /* send the notification after the selection start and end are set */ + EDIT_NOTIFY_PARENT(es, EN_CHANGE); + EDIT_EM_ScrollCaret(es); + heap_free(utext); + + TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n", + es->undo_insert_count, debugstr_w(es->undo_text)); + return TRUE; +} + + +/* Helper function for WM_CHAR + * + * According to an MSDN blog article titled "Just because you're a control + * doesn't mean that you're necessarily inside a dialog box," multiline edit + * controls without ES_WANTRETURN would attempt to detect whether it is inside + * a dialog box or not. + */ +static inline BOOL EDIT_IsInsideDialog(EDITSTATE *es) +{ + return (es->flags & EF_DIALOGMODE); +} + + +/********************************************************************* + * + * WM_PASTE + * + */ +static void EDIT_WM_Paste(EDITSTATE *es) +{ + HGLOBAL hsrc; + LPWSTR src, ptr; + int len; + + /* Protect read-only edit control from modification */ + if(es->style & ES_READONLY) + return; + + OpenClipboard(es->hwndSelf); + if ((hsrc = GetClipboardData(CF_UNICODETEXT))) { + src = GlobalLock(hsrc); + len = strlenW(src); + /* Protect single-line edit against pasting new line character */ + if (!(es->style & ES_MULTILINE) && ((ptr = strchrW(src, '\n')))) { + len = ptr - src; + if (len && src[len - 1] == '\r') + --len; + } + EDIT_EM_ReplaceSel(es, TRUE, src, len, TRUE, TRUE); + GlobalUnlock(hsrc); + } + else if (es->style & ES_PASSWORD) { + /* clear selected text in password edit box even with empty clipboard */ + EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); + } + CloseClipboard(); +} + + +/********************************************************************* + * + * WM_COPY + * + */ +static void EDIT_WM_Copy(EDITSTATE *es) +{ + INT s = min(es->selection_start, es->selection_end); + INT e = max(es->selection_start, es->selection_end); + HGLOBAL hdst; + LPWSTR dst; + DWORD len; + + if (e == s) return; + + len = e - s; + hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR)); + dst = GlobalLock(hdst); + memcpy(dst, es->text + s, len * sizeof(WCHAR)); + dst[len] = 0; /* ensure 0 termination */ + TRACE("%s\n", debugstr_w(dst)); + GlobalUnlock(hdst); + OpenClipboard(es->hwndSelf); + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, hdst); + CloseClipboard(); +} + + +/********************************************************************* + * + * WM_CLEAR + * + */ +static inline void EDIT_WM_Clear(EDITSTATE *es) +{ + /* Protect read-only edit control from modification */ + if(es->style & ES_READONLY) + return; + + EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); +} + + +/********************************************************************* + * + * WM_CUT + * + */ +static inline void EDIT_WM_Cut(EDITSTATE *es) +{ + EDIT_WM_Copy(es); + EDIT_WM_Clear(es); +} + + +/********************************************************************* + * + * WM_CHAR + * + */ +static LRESULT EDIT_WM_Char(EDITSTATE *es, WCHAR c) +{ + BOOL control; + + control = GetKeyState(VK_CONTROL) & 0x8000; + + switch (c) { + case '\r': + /* If it's not a multiline edit box, it would be ignored below. + * For multiline edit without ES_WANTRETURN, we have to make a + * special case. + */ + if ((es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN)) + if (EDIT_IsInsideDialog(es)) + break; + case '\n': + if (es->style & ES_MULTILINE) { + if (es->style & ES_READONLY) { + EDIT_MoveHome(es, FALSE, FALSE); + EDIT_MoveDown_ML(es, FALSE); + } else { + static const WCHAR cr_lfW[] = {'\r','\n'}; + EDIT_EM_ReplaceSel(es, TRUE, cr_lfW, 2, TRUE, TRUE); + } + } + break; + case '\t': + if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY)) + { + static const WCHAR tabW[] = {'\t'}; + if (EDIT_IsInsideDialog(es)) + break; + EDIT_EM_ReplaceSel(es, TRUE, tabW, 1, TRUE, TRUE); + } + break; + case VK_BACK: + if (!(es->style & ES_READONLY) && !control) { + if (es->selection_start != es->selection_end) + EDIT_WM_Clear(es); + else { + /* delete character left of caret */ + EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); + EDIT_MoveBackward(es, TRUE); + EDIT_WM_Clear(es); + } + } + break; + case 0x03: /* ^C */ + if (!(es->style & ES_PASSWORD)) + SendMessageW(es->hwndSelf, WM_COPY, 0, 0); + break; + case 0x16: /* ^V */ + if (!(es->style & ES_READONLY)) + SendMessageW(es->hwndSelf, WM_PASTE, 0, 0); + break; + case 0x18: /* ^X */ + if (!((es->style & ES_READONLY) || (es->style & ES_PASSWORD))) + SendMessageW(es->hwndSelf, WM_CUT, 0, 0); + break; + case 0x1A: /* ^Z */ + if (!(es->style & ES_READONLY)) + SendMessageW(es->hwndSelf, WM_UNDO, 0, 0); + break; + + default: + /*If Edit control style is ES_NUMBER allow users to key in only numeric values*/ + if( (es->style & ES_NUMBER) && !( c >= '0' && c <= '9') ) + break; + + if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) + EDIT_EM_ReplaceSel(es, TRUE, &c, 1, TRUE, TRUE); + break; + } + return 1; +} + + +/********************************************************************* + * + * EDIT_ContextMenuCommand + * + */ +static void EDIT_ContextMenuCommand(EDITSTATE *es, UINT id) +{ + switch (id) { + case EM_UNDO: + SendMessageW(es->hwndSelf, WM_UNDO, 0, 0); + break; + case WM_CUT: + SendMessageW(es->hwndSelf, WM_CUT, 0, 0); + break; + case WM_COPY: + SendMessageW(es->hwndSelf, WM_COPY, 0, 0); + break; + case WM_PASTE: + SendMessageW(es->hwndSelf, WM_PASTE, 0, 0); + break; + case WM_CLEAR: + SendMessageW(es->hwndSelf, WM_CLEAR, 0, 0); + break; + case EM_SETSEL: + SendMessageW(es->hwndSelf, EM_SETSEL, 0, -1); + break; + default: + ERR("unknown menu item, please report\n"); + break; + } +} + + +/********************************************************************* + * + * WM_CONTEXTMENU + * + * Note: the resource files resource/sysres_??.rc cannot define a + * single popup menu. Hence we use a (dummy) menubar + * containing the single popup menu as its first item. + * + * FIXME: the message identifiers have been chosen arbitrarily, + * hence we use MF_BYPOSITION. + * We might as well use the "real" values (anybody knows ?) + * The menu definition is in resources/sysres_??.rc. + * Once these are OK, we better use MF_BYCOMMAND here + * (as we do in EDIT_WM_Command()). + * + */ +static void EDIT_WM_ContextMenu(EDITSTATE *es, INT x, INT y) +{ + HMENU menu = LoadMenuA(GetModuleHandleA("user32.dll"), "EDITMENU"); + HMENU popup = GetSubMenu(menu, 0); + UINT start = es->selection_start; + UINT end = es->selection_end; + UINT cmd; + POINT pt; + + ORDER_UINT(start, end); + + /* undo */ + EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); + /* cut */ + EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); + /* copy */ + EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED)); + /* paste */ + EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); + /* delete */ + EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED)); + /* select all */ + EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != get_text_length(es)) ? MF_ENABLED : MF_GRAYED)); + + pt.x = x; + pt.y = y; + + if (pt.x == -1 && pt.y == -1) /* passed via VK_APPS press/release */ + { + RECT rc; + + /* Windows places the menu at the edit's center in this case */ + GetClientRect(es->hwndSelf, &rc); + pt.x = rc.left + (rc.right - rc.left) / 2; + pt.y = rc.top + (rc.bottom - rc.top) / 2; + ClientToScreen(es->hwndSelf, &pt); + } + + if (!(es->flags & EF_FOCUSED)) + SetFocus(es->hwndSelf); + + cmd = TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, + pt.x, pt.y, 0, es->hwndSelf, NULL); + + if (cmd) + EDIT_ContextMenuCommand(es, cmd); + + DestroyMenu(menu); +} + + +/********************************************************************* + * + * WM_GETTEXT + * + */ +static INT EDIT_WM_GetText(const EDITSTATE *es, INT count, LPWSTR dst) +{ + if (!count) + return 0; + + lstrcpynW(dst, es->text, count); + return strlenW(dst); +} + +/********************************************************************* + * + * EDIT_CheckCombo + * + */ +static BOOL EDIT_CheckCombo(EDITSTATE *es, UINT msg, INT key) +{ + HWND hLBox = es->hwndListBox; + HWND hCombo; + BOOL bDropped; + int nEUI; + + if (!hLBox) + return FALSE; + + hCombo = GetParent(es->hwndSelf); + bDropped = TRUE; + nEUI = 0; + + TRACE("[%p]: handling msg %x (%x)\n", es->hwndSelf, msg, key); + + if (key == VK_UP || key == VK_DOWN) + { + if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0)) + nEUI = 1; + + if (msg == WM_KEYDOWN || nEUI) + bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0); + } + + switch (msg) + { + case WM_KEYDOWN: + if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN)) + { + /* make sure ComboLBox pops up */ + SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0); + key = VK_F4; + nEUI = 2; + } + + SendMessageW(hLBox, WM_KEYDOWN, key, 0); + break; + + case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */ + if (nEUI) + SendMessageW(hCombo, CB_SHOWDROPDOWN, !bDropped, 0); + else + SendMessageW(hLBox, WM_KEYDOWN, VK_F4, 0); + break; + } + + if (nEUI == 2) + SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0); + + return TRUE; +} + + +/********************************************************************* + * + * WM_KEYDOWN + * + * Handling of special keys that don't produce a WM_CHAR + * (i.e. non-printable keys) & Backspace & Delete + * + */ +static LRESULT EDIT_WM_KeyDown(EDITSTATE *es, INT key) +{ + BOOL shift; + BOOL control; + + if (GetKeyState(VK_MENU) & 0x8000) + return 0; + + shift = GetKeyState(VK_SHIFT) & 0x8000; + control = GetKeyState(VK_CONTROL) & 0x8000; + + switch (key) { + case VK_F4: + case VK_UP: + if (EDIT_CheckCombo(es, WM_KEYDOWN, key) || key == VK_F4) + break; + + /* fall through */ + case VK_LEFT: + if ((es->style & ES_MULTILINE) && (key == VK_UP)) + EDIT_MoveUp_ML(es, shift); + else + if (control) + EDIT_MoveWordBackward(es, shift); + else + EDIT_MoveBackward(es, shift); + break; + case VK_DOWN: + if (EDIT_CheckCombo(es, WM_KEYDOWN, key)) + break; + /* fall through */ + case VK_RIGHT: + if ((es->style & ES_MULTILINE) && (key == VK_DOWN)) + EDIT_MoveDown_ML(es, shift); + else if (control) + EDIT_MoveWordForward(es, shift); + else + EDIT_MoveForward(es, shift); + break; + case VK_HOME: + EDIT_MoveHome(es, shift, control); + break; + case VK_END: + EDIT_MoveEnd(es, shift, control); + break; + case VK_PRIOR: + if (es->style & ES_MULTILINE) + EDIT_MovePageUp_ML(es, shift); + else + EDIT_CheckCombo(es, WM_KEYDOWN, key); + break; + case VK_NEXT: + if (es->style & ES_MULTILINE) + EDIT_MovePageDown_ML(es, shift); + else + EDIT_CheckCombo(es, WM_KEYDOWN, key); + break; + case VK_DELETE: + if (!(es->style & ES_READONLY) && !(shift && control)) { + if (es->selection_start != es->selection_end) { + if (shift) + EDIT_WM_Cut(es); + else + EDIT_WM_Clear(es); + } else { + if (shift) { + /* delete character left of caret */ + EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); + EDIT_MoveBackward(es, TRUE); + EDIT_WM_Clear(es); + } else if (control) { + /* delete to end of line */ + EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); + EDIT_MoveEnd(es, TRUE, FALSE); + EDIT_WM_Clear(es); + } else { + /* delete character right of caret */ + EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE); + EDIT_MoveForward(es, TRUE); + EDIT_WM_Clear(es); + } + } + } + break; + case VK_INSERT: + if (shift) { + if (!(es->style & ES_READONLY)) + EDIT_WM_Paste(es); + } else if (control) + EDIT_WM_Copy(es); + break; + case VK_RETURN: + /* If the edit doesn't want the return send a message to the default object */ + if(!(es->style & ES_MULTILINE) || !(es->style & ES_WANTRETURN)) + { + DWORD dw; + + if (!EDIT_IsInsideDialog(es)) break; + if (control) break; + dw = SendMessageW(es->hwndParent, DM_GETDEFID, 0, 0); + if (HIWORD(dw) == DC_HASDEFID) + { + HWND hwDefCtrl = GetDlgItem(es->hwndParent, LOWORD(dw)); + if (hwDefCtrl) + { + SendMessageW(es->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE); + PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0); + } + } + } + break; + case VK_ESCAPE: + if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es)) + PostMessageW(es->hwndParent, WM_CLOSE, 0, 0); + break; + case VK_TAB: + if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es)) + SendMessageW(es->hwndParent, WM_NEXTDLGCTL, shift, 0); + break; + case 'A': + if (control) + { + if (EDIT_EM_SetSel(es, 0, get_text_length(es), FALSE)) + { + EDIT_NOTIFY_PARENT(es, EN_UPDATE); + EDIT_NOTIFY_PARENT(es, EN_CHANGE); + } + } + break; + } + return TRUE; +} + + +/********************************************************************* + * + * WM_KILLFOCUS + * + */ +static LRESULT EDIT_WM_KillFocus(HTHEME theme, EDITSTATE *es) +{ + UINT flags = RDW_INVALIDATE | RDW_UPDATENOW; + + es->flags &= ~EF_FOCUSED; + DestroyCaret(); + if (!(es->style & ES_NOHIDESEL)) + EDIT_InvalidateText(es, es->selection_start, es->selection_end); + EDIT_NOTIFY_PARENT(es, EN_KILLFOCUS); + /* Throw away left over scroll when we lose focus */ + es->wheelDeltaRemainder = 0; + + if (theme) + flags |= RDW_FRAME; + + RedrawWindow(es->hwndSelf, NULL, NULL, flags); + return 0; +} + + +/********************************************************************* + * + * WM_LBUTTONDBLCLK + * + * The caret position has been set on the WM_LBUTTONDOWN message + * + */ +static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es) +{ + INT s; + INT e = es->selection_end; + INT l; + INT li; + INT ll; + + es->bCaptureState = TRUE; + SetCapture(es->hwndSelf); + + l = EDIT_EM_LineFromChar(es, e); + li = EDIT_EM_LineIndex(es, l); + ll = EDIT_EM_LineLength(es, e); + s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT); + e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT); + EDIT_EM_SetSel(es, s, e, FALSE); + EDIT_EM_ScrollCaret(es); + es->region_posx = es->region_posy = 0; + SetTimer(es->hwndSelf, 0, 100, NULL); + return 0; +} + + +/********************************************************************* + * + * WM_LBUTTONDOWN + * + */ +static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y) +{ + INT e; + BOOL after_wrap; + + es->bCaptureState = TRUE; + SetCapture(es->hwndSelf); + EDIT_ConfinePoint(es, &x, &y); + e = EDIT_CharFromPos(es, x, y, &after_wrap); + EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap); + EDIT_EM_ScrollCaret(es); + es->region_posx = es->region_posy = 0; + SetTimer(es->hwndSelf, 0, 100, NULL); + + if (!(es->flags & EF_FOCUSED)) + SetFocus(es->hwndSelf); + + return 0; +} + + +/********************************************************************* + * + * WM_LBUTTONUP + * + */ +static LRESULT EDIT_WM_LButtonUp(EDITSTATE *es) +{ + if (es->bCaptureState) { + KillTimer(es->hwndSelf, 0); + if (GetCapture() == es->hwndSelf) ReleaseCapture(); + } + es->bCaptureState = FALSE; + return 0; +} + + +/********************************************************************* + * + * WM_MBUTTONDOWN + * + */ +static LRESULT EDIT_WM_MButtonDown(EDITSTATE *es) +{ + SendMessageW(es->hwndSelf, WM_PASTE, 0, 0); + return 0; +} + + +/********************************************************************* + * + * WM_MOUSEMOVE + * + */ +static LRESULT EDIT_WM_MouseMove(EDITSTATE *es, INT x, INT y) +{ + INT e; + BOOL after_wrap; + INT prex, prey; + + /* If the mouse has been captured by process other than the edit control itself, + * the windows edit controls will not select the strings with mouse move. + */ + if (!es->bCaptureState || GetCapture() != es->hwndSelf) + return 0; + + /* + * FIXME: gotta do some scrolling if outside client + * area. Maybe reset the timer ? + */ + prex = x; prey = y; + EDIT_ConfinePoint(es, &x, &y); + es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0); + es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0); + e = EDIT_CharFromPos(es, x, y, &after_wrap); + EDIT_EM_SetSel(es, es->selection_start, e, after_wrap); + EDIT_SetCaretPos(es,es->selection_end,es->flags & EF_AFTER_WRAP); + return 0; +} + + +/********************************************************************* + * + * WM_PAINT + * + */ +static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc) +{ + PAINTSTRUCT ps; + INT i; + HDC dc; + HFONT old_font = 0; + RECT rc; + RECT rcClient; + RECT rcLine; + RECT rcRgn; + HBRUSH brush; + HBRUSH old_brush; + INT bw, bh; + BOOL rev = es->bEnableState && + ((es->flags & EF_FOCUSED) || + (es->style & ES_NOHIDESEL)); + dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps); + + /* The dc we use for calculating may not be the one we paint into. + This is the safest action. */ + EDIT_InvalidateUniscribeData(es); + GetClientRect(es->hwndSelf, &rcClient); + + /* get the background brush */ + brush = EDIT_NotifyCtlColor(es, dc); + + /* paint the border and the background */ + IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + + if(es->style & WS_BORDER) { + bw = GetSystemMetrics(SM_CXBORDER); + bh = GetSystemMetrics(SM_CYBORDER); + rc = rcClient; + if(es->style & ES_MULTILINE) { + if(es->style & WS_HSCROLL) rc.bottom+=bh; + if(es->style & WS_VSCROLL) rc.right+=bw; + } + + /* Draw the frame. Same code as in nonclient.c */ + old_brush = SelectObject(dc, GetSysColorBrush(COLOR_WINDOWFRAME)); + PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY); + PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY); + PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY); + PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY); + SelectObject(dc, old_brush); + + /* Keep the border clean */ + IntersectClipRect(dc, rc.left+bw, rc.top+bh, + max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh)); + } + + GetClipBox(dc, &rc); + FillRect(dc, &rc, brush); + + IntersectClipRect(dc, es->format_rect.left, + es->format_rect.top, + es->format_rect.right, + es->format_rect.bottom); + if (es->style & ES_MULTILINE) { + rc = rcClient; + IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom); + } + if (es->font) + old_font = SelectObject(dc, es->font); + + if (!es->bEnableState) + SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT)); + GetClipBox(dc, &rcRgn); + if (es->style & ES_MULTILINE) { + INT vlc = get_vertical_line_count(es); + for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) { + EDIT_UpdateUniscribeData(es, dc, i); + EDIT_GetLineRect(es, i, 0, -1, &rcLine); + if (IntersectRect(&rc, &rcRgn, &rcLine)) + EDIT_PaintLine(es, dc, i, rev); + } + } else { + EDIT_UpdateUniscribeData(es, dc, 0); + EDIT_GetLineRect(es, 0, 0, -1, &rcLine); + if (IntersectRect(&rc, &rcRgn, &rcLine)) + EDIT_PaintLine(es, dc, 0, rev); + } + if (es->font) + SelectObject(dc, old_font); + + if (!hdc) + EndPaint(es->hwndSelf, &ps); +} + +static void EDIT_WM_NCPaint(HWND hwnd, HRGN region) +{ + DWORD exStyle = GetWindowLongW(hwnd, GWL_EXSTYLE); + HTHEME theme = GetWindowTheme(hwnd); + HRGN cliprgn = region; + + if (theme && exStyle & WS_EX_CLIENTEDGE) + { + HDC dc; + RECT r; + int cxEdge = GetSystemMetrics(SM_CXEDGE), + cyEdge = GetSystemMetrics(SM_CYEDGE); + const int part = EP_EDITTEXT; + int state = ETS_NORMAL; + DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE); + + if (!IsWindowEnabled(hwnd)) + state = ETS_DISABLED; + else if (dwStyle & ES_READONLY) + state = ETS_READONLY; + else if (GetFocus() == hwnd) + state = ETS_FOCUSED; + + GetWindowRect(hwnd, &r); + + /* New clipping region passed to default proc to exclude border */ + cliprgn = CreateRectRgn(r.left + cxEdge, r.top + cyEdge, + r.right - cxEdge, r.bottom - cyEdge); + if (region != (HRGN)1) + CombineRgn(cliprgn, cliprgn, region, RGN_AND); + OffsetRect(&r, -r.left, -r.top); + +#ifdef __REACTOS__ /* r73789 */ + dc = GetWindowDC(hwnd); + /* Exclude client part */ + ExcludeClipRect(dc, + r.left + cxEdge, + r.top + cyEdge, + r.right - cxEdge, + r.bottom -cyEdge); +#else + dc = GetDCEx(hwnd, region, DCX_WINDOW|DCX_INTERSECTRGN); + OffsetRect(&r, -r.left, -r.top); +#endif + + if (IsThemeBackgroundPartiallyTransparent(theme, part, state)) + DrawThemeParentBackground(hwnd, dc, &r); + DrawThemeBackground(theme, dc, part, state, &r, 0); + ReleaseDC(hwnd, dc); + } + + /* Call default proc to get the scrollbars etc. also painted */ + DefWindowProcW (hwnd, WM_NCPAINT, (WPARAM)cliprgn, 0); +} + +/********************************************************************* + * + * WM_SETFOCUS + * + */ +static void EDIT_WM_SetFocus(HTHEME theme, EDITSTATE *es) +{ + UINT flags = RDW_INVALIDATE | RDW_UPDATENOW; + + es->flags |= EF_FOCUSED; + + if (!(es->style & ES_NOHIDESEL)) + EDIT_InvalidateText(es, es->selection_start, es->selection_end); + + CreateCaret(es->hwndSelf, 0, 1, es->line_height); + EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP); + ShowCaret(es->hwndSelf); + EDIT_NOTIFY_PARENT(es, EN_SETFOCUS); + + if (theme) + flags |= RDW_FRAME | RDW_ERASE; + + RedrawWindow(es->hwndSelf, NULL, NULL, flags); +} + + +/********************************************************************* + * + * WM_SETFONT + * + * With Win95 look the margins are set to default font value unless + * the system font (font == 0) is being set, in which case they are left + * unchanged. + * + */ +static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw) +{ + TEXTMETRICW tm; + HDC dc; + HFONT old_font = 0; + RECT clientRect; + + es->font = font; + EDIT_InvalidateUniscribeData(es); + dc = GetDC(es->hwndSelf); + if (font) + old_font = SelectObject(dc, font); + GetTextMetricsW(dc, &tm); + es->line_height = tm.tmHeight; + es->char_width = tm.tmAveCharWidth; + if (font) + SelectObject(dc, old_font); + ReleaseDC(es->hwndSelf, dc); + + /* Reset the format rect and the margins */ + GetClientRect(es->hwndSelf, &clientRect); + EDIT_SetRectNP(es, &clientRect); + EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN, + EC_USEFONTINFO, EC_USEFONTINFO, FALSE); + + if (es->style & ES_MULTILINE) + EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL); + else + EDIT_CalcLineWidth_SL(es); + + if (redraw) + EDIT_UpdateText(es, NULL, TRUE); + if (es->flags & EF_FOCUSED) { + DestroyCaret(); + CreateCaret(es->hwndSelf, 0, 1, es->line_height); + EDIT_SetCaretPos(es, es->selection_end, + es->flags & EF_AFTER_WRAP); + ShowCaret(es->hwndSelf); + } +} + + +/********************************************************************* + * + * WM_SETTEXT + * + * NOTES + * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers: + * The modified flag is reset. No notifications are sent. + * + * For single-line controls, reception of WM_SETTEXT triggers: + * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent. + * + */ +static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text) +{ + if (es->flags & EF_UPDATE) + /* fixed this bug once; complain if we see it about to happen again. */ + ERR("SetSel may generate UPDATE message whose handler may reset " + "selection.\n"); + + EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE); + if (text) + { + TRACE("%s\n", debugstr_w(text)); + EDIT_EM_ReplaceSel(es, FALSE, text, strlenW(text), FALSE, FALSE); + } + else + { + TRACE("\n"); + EDIT_EM_ReplaceSel(es, FALSE, NULL, 0, FALSE, FALSE); + } + es->x_offset = 0; + es->flags &= ~EF_MODIFIED; + EDIT_EM_SetSel(es, 0, 0, FALSE); + + /* Send the notification after the selection start and end have been set + * edit control doesn't send notification on WM_SETTEXT + * if it is multiline, or it is part of combobox + */ + if( !((es->style & ES_MULTILINE) || es->hwndListBox)) + { + EDIT_NOTIFY_PARENT(es, EN_UPDATE); + EDIT_NOTIFY_PARENT(es, EN_CHANGE); + } + EDIT_EM_ScrollCaret(es); + EDIT_UpdateScrollInfo(es); + EDIT_InvalidateUniscribeData(es); +} + + +/********************************************************************* + * + * WM_SIZE + * + */ +static void EDIT_WM_Size(EDITSTATE *es, UINT action) +{ + if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) { + RECT rc; + GetClientRect(es->hwndSelf, &rc); + EDIT_SetRectNP(es, &rc); + EDIT_UpdateText(es, NULL, TRUE); + } +} + + +/********************************************************************* + * + * WM_STYLECHANGED + * + * This message is sent by SetWindowLong on having changed either the Style + * or the extended style. + * + * We ensure that the window's version of the styles and the EDITSTATE's agree. + * + * See also EDIT_WM_NCCreate + * + * It appears that the Windows version of the edit control allows the style + * (as retrieved by GetWindowLong) to be any value and maintains an internal + * style variable which will generally be different. In this function we + * update the internal style based on what changed in the externally visible + * style. + * + * Much of this content as based upon the MSDN, especially: + * Platform SDK Documentation -> User Interface Services -> + * Windows User Interface -> Edit Controls -> Edit Control Reference -> + * Edit Control Styles + */ +static LRESULT EDIT_WM_StyleChanged ( EDITSTATE *es, WPARAM which, const STYLESTRUCT *style) +{ + if (GWL_STYLE == which) { + DWORD style_change_mask; + DWORD new_style; + /* Only a subset of changes can be applied after the control + * has been created. + */ + style_change_mask = ES_UPPERCASE | ES_LOWERCASE | + ES_NUMBER; + if (es->style & ES_MULTILINE) + style_change_mask |= ES_WANTRETURN; + + new_style = style->styleNew & style_change_mask; + + /* Number overrides lowercase overrides uppercase (at least it + * does in Win95). However I'll bet that ES_NUMBER would be + * invalid under Win 3.1. + */ + if (new_style & ES_NUMBER) { + ; /* do not override the ES_NUMBER */ + } else if (new_style & ES_LOWERCASE) { + new_style &= ~ES_UPPERCASE; + } + + es->style = (es->style & ~style_change_mask) | new_style; + } else if (GWL_EXSTYLE == which) { + ; /* FIXME - what is needed here */ + } else { + WARN ("Invalid style change %ld\n",which); + } + + return 0; +} + +/********************************************************************* + * + * WM_SYSKEYDOWN + * + */ +static LRESULT EDIT_WM_SysKeyDown(EDITSTATE *es, INT key, DWORD key_data) +{ + if ((key == VK_BACK) && (key_data & 0x2000)) { + if (EDIT_EM_CanUndo(es)) + EDIT_EM_Undo(es); + return 0; + } else if (key == VK_UP || key == VK_DOWN) { + if (EDIT_CheckCombo(es, WM_SYSKEYDOWN, key)) + return 0; + } + return DefWindowProcW(es->hwndSelf, WM_SYSKEYDOWN, key, key_data); +} + + +/********************************************************************* + * + * WM_TIMER + * + */ +static void EDIT_WM_Timer(EDITSTATE *es) +{ + if (es->region_posx < 0) { + EDIT_MoveBackward(es, TRUE); + } else if (es->region_posx > 0) { + EDIT_MoveForward(es, TRUE); + } +/* + * FIXME: gotta do some vertical scrolling here, like + * EDIT_EM_LineScroll(hwnd, 0, 1); + */ +} + +/********************************************************************* + * + * WM_HSCROLL + * + */ +static LRESULT EDIT_WM_HScroll(EDITSTATE *es, INT action, INT pos) +{ + INT dx; + INT fw; + + if (!(es->style & ES_MULTILINE)) + return 0; + + if (!(es->style & ES_AUTOHSCROLL)) + return 0; + + dx = 0; + fw = es->format_rect.right - es->format_rect.left; + switch (action) { + case SB_LINELEFT: + TRACE("SB_LINELEFT\n"); + if (es->x_offset) + dx = -es->char_width; + break; + case SB_LINERIGHT: + TRACE("SB_LINERIGHT\n"); + if (es->x_offset < es->text_width) + dx = es->char_width; + break; + case SB_PAGELEFT: + TRACE("SB_PAGELEFT\n"); + if (es->x_offset) + dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width; + break; + case SB_PAGERIGHT: + TRACE("SB_PAGERIGHT\n"); + if (es->x_offset < es->text_width) + dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width; + break; + case SB_LEFT: + TRACE("SB_LEFT\n"); + if (es->x_offset) + dx = -es->x_offset; + break; + case SB_RIGHT: + TRACE("SB_RIGHT\n"); + if (es->x_offset < es->text_width) + dx = es->text_width - es->x_offset; + break; + case SB_THUMBTRACK: + TRACE("SB_THUMBTRACK %d\n", pos); + es->flags |= EF_HSCROLL_TRACK; + if(es->style & WS_HSCROLL) + dx = pos - es->x_offset; + else + { + INT fw, new_x; + /* Sanity check */ + if(pos < 0 || pos > 100) return 0; + /* Assume default scroll range 0-100 */ + fw = es->format_rect.right - es->format_rect.left; + new_x = pos * (es->text_width - fw) / 100; + dx = es->text_width ? (new_x - es->x_offset) : 0; + } + break; + case SB_THUMBPOSITION: + TRACE("SB_THUMBPOSITION %d\n", pos); + es->flags &= ~EF_HSCROLL_TRACK; + if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL) + dx = pos - es->x_offset; + else + { + INT fw, new_x; + /* Sanity check */ + if(pos < 0 || pos > 100) return 0; + /* Assume default scroll range 0-100 */ + fw = es->format_rect.right - es->format_rect.left; + new_x = pos * (es->text_width - fw) / 100; + dx = es->text_width ? (new_x - es->x_offset) : 0; + } + if (!dx) { + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); + EDIT_NOTIFY_PARENT(es, EN_HSCROLL); + } + break; + case SB_ENDSCROLL: + TRACE("SB_ENDSCROLL\n"); + break; + /* + * FIXME : the next two are undocumented ! + * Are we doing the right thing ? + * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way, + * although it's also a regular control message. + */ + case EM_GETTHUMB: /* this one is used by NT notepad */ + { + LRESULT ret; + if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL) + ret = GetScrollPos(es->hwndSelf, SB_HORZ); + else + { + /* Assume default scroll range 0-100 */ + INT fw = es->format_rect.right - es->format_rect.left; + ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0; + } + TRACE("EM_GETTHUMB: returning %ld\n", ret); + return ret; + } + case EM_LINESCROLL: + TRACE("EM_LINESCROLL16\n"); + dx = pos; + break; + + default: + ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n", + action, action); + return 0; + } + if (dx) + { + INT fw = es->format_rect.right - es->format_rect.left; + /* check if we are going to move too far */ + if(es->x_offset + dx + fw > es->text_width) + dx = es->text_width - fw - es->x_offset; + if(dx) + EDIT_EM_LineScroll_internal(es, dx, 0); + } + return 0; +} + + +/********************************************************************* + * + * WM_VSCROLL + * + */ +static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos) +{ + INT dy; + + if (!(es->style & ES_MULTILINE)) + return 0; + + if (!(es->style & ES_AUTOVSCROLL)) + return 0; + + dy = 0; + switch (action) { + case SB_LINEUP: + case SB_LINEDOWN: + case SB_PAGEUP: + case SB_PAGEDOWN: + TRACE("action %d (%s)\n", action, (action == SB_LINEUP ? "SB_LINEUP" : + (action == SB_LINEDOWN ? "SB_LINEDOWN" : + (action == SB_PAGEUP ? "SB_PAGEUP" : + "SB_PAGEDOWN")))); + EDIT_EM_Scroll(es, action); + return 0; + case SB_TOP: + TRACE("SB_TOP\n"); + dy = -es->y_offset; + break; + case SB_BOTTOM: + TRACE("SB_BOTTOM\n"); + dy = es->line_count - 1 - es->y_offset; + break; + case SB_THUMBTRACK: + TRACE("SB_THUMBTRACK %d\n", pos); + es->flags |= EF_VSCROLL_TRACK; + if(es->style & WS_VSCROLL) + dy = pos - es->y_offset; + else + { + /* Assume default scroll range 0-100 */ + INT vlc, new_y; + /* Sanity check */ + if(pos < 0 || pos > 100) return 0; + vlc = get_vertical_line_count(es); + new_y = pos * (es->line_count - vlc) / 100; + dy = es->line_count ? (new_y - es->y_offset) : 0; + TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n", + es->line_count, es->y_offset, pos, dy); + } + break; + case SB_THUMBPOSITION: + TRACE("SB_THUMBPOSITION %d\n", pos); + es->flags &= ~EF_VSCROLL_TRACK; + if(es->style & WS_VSCROLL) + dy = pos - es->y_offset; + else + { + /* Assume default scroll range 0-100 */ + INT vlc, new_y; + /* Sanity check */ + if(pos < 0 || pos > 100) return 0; + vlc = get_vertical_line_count(es); + new_y = pos * (es->line_count - vlc) / 100; + dy = es->line_count ? (new_y - es->y_offset) : 0; + TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n", + es->line_count, es->y_offset, pos, dy); + } + if (!dy) + { + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); + EDIT_NOTIFY_PARENT(es, EN_VSCROLL); + } + break; + case SB_ENDSCROLL: + TRACE("SB_ENDSCROLL\n"); + break; + /* + * FIXME : the next two are undocumented ! + * Are we doing the right thing ? + * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way, + * although it's also a regular control message. + */ + case EM_GETTHUMB: /* this one is used by NT notepad */ + { + LRESULT ret; + if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_VSCROLL) + ret = GetScrollPos(es->hwndSelf, SB_VERT); + else + { + /* Assume default scroll range 0-100 */ + INT vlc = get_vertical_line_count(es); + ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0; + } + TRACE("EM_GETTHUMB: returning %ld\n", ret); + return ret; + } + case EM_LINESCROLL: + TRACE("EM_LINESCROLL %d\n", pos); + dy = pos; + break; + + default: + ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n", + action, action); + return 0; + } + if (dy) + EDIT_EM_LineScroll(es, 0, dy); + return 0; +} + +/********************************************************************* + * + * EM_GETTHUMB + * + * FIXME: is this right ? (or should it be only VSCROLL) + * (and maybe only for edit controls that really have their + * own scrollbars) (and maybe only for multiline controls ?) + * All in all: very poorly documented + * + */ +static LRESULT EDIT_EM_GetThumb(EDITSTATE *es) +{ + return MAKELONG(EDIT_WM_VScroll(es, EM_GETTHUMB, 0), + EDIT_WM_HScroll(es, EM_GETTHUMB, 0)); +} + + +/******************************************************************** + * + * The Following code is to handle inline editing from IMEs + */ + +static void EDIT_GetCompositionStr(HIMC hIMC, LPARAM CompFlag, EDITSTATE *es) +{ + LONG buflen; + LPWSTR lpCompStr; + LPSTR lpCompStrAttr = NULL; + DWORD dwBufLenAttr; + + buflen = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0); + + if (buflen < 0) + { + return; + } + + lpCompStr = heap_alloc(buflen); + if (!lpCompStr) + { + ERR("Unable to allocate IME CompositionString\n"); + return; + } + + if (buflen) + ImmGetCompositionStringW(hIMC, GCS_COMPSTR, lpCompStr, buflen); + + if (CompFlag & GCS_COMPATTR) + { + /* + * We do not use the attributes yet. it would tell us what characters + * are in transition and which are converted or decided upon + */ + dwBufLenAttr = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0); + if (dwBufLenAttr) + { + dwBufLenAttr ++; + lpCompStrAttr = heap_alloc(dwBufLenAttr + 1); + if (!lpCompStrAttr) + { + ERR("Unable to allocate IME Attribute String\n"); + heap_free(lpCompStr); + return; + } + ImmGetCompositionStringW(hIMC,GCS_COMPATTR, lpCompStrAttr, + dwBufLenAttr); + lpCompStrAttr[dwBufLenAttr] = 0; + } + } + + /* check for change in composition start */ + if (es->selection_end < es->composition_start) + es->composition_start = es->selection_end; + + /* replace existing selection string */ + es->selection_start = es->composition_start; + + if (es->composition_len > 0) + es->selection_end = es->composition_start + es->composition_len; + else + es->selection_end = es->selection_start; + + EDIT_EM_ReplaceSel(es, FALSE, lpCompStr, buflen / sizeof(WCHAR), TRUE, TRUE); + es->composition_len = abs(es->composition_start - es->selection_end); + + es->selection_start = es->composition_start; + es->selection_end = es->selection_start + es->composition_len; + + heap_free(lpCompStrAttr); + heap_free(lpCompStr); +} + +static void EDIT_GetResultStr(HIMC hIMC, EDITSTATE *es) +{ + LONG buflen; + LPWSTR lpResultStr; + + buflen = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); + if (buflen <= 0) + { + return; + } + + lpResultStr = heap_alloc(buflen); + if (!lpResultStr) + { + ERR("Unable to alloc buffer for IME string\n"); + return; + } + + ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, lpResultStr, buflen); + + /* check for change in composition start */ + if (es->selection_end < es->composition_start) + es->composition_start = es->selection_end; + + es->selection_start = es->composition_start; + es->selection_end = es->composition_start + es->composition_len; + EDIT_EM_ReplaceSel(es, TRUE, lpResultStr, buflen / sizeof(WCHAR), TRUE, TRUE); + es->composition_start = es->selection_end; + es->composition_len = 0; + + heap_free(lpResultStr); +} + +static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es) +{ + HIMC hIMC; + int cursor; + + if (es->composition_len == 0 && es->selection_start != es->selection_end) + { + EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); + es->composition_start = es->selection_end; + } + + hIMC = ImmGetContext(hwnd); + if (!hIMC) + return; + + if (CompFlag & GCS_RESULTSTR) + { + EDIT_GetResultStr(hIMC, es); + cursor = 0; + } + else + { + if (CompFlag & GCS_COMPSTR) + EDIT_GetCompositionStr(hIMC, CompFlag, es); + cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, 0, 0); + } + ImmReleaseContext(hwnd, hIMC); + EDIT_SetCaretPos(es, es->selection_start + cursor, es->flags & EF_AFTER_WRAP); +} + + +/********************************************************************* + * + * WM_NCCREATE + * + * See also EDIT_WM_StyleChanged + */ +static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs) +{ + EDITSTATE *es; + UINT alloc_size; + + TRACE("Creating edit control, style = %08x\n", lpcs->style); + + if (!(es = heap_alloc_zero(sizeof(*es)))) + return FALSE; + SetWindowLongPtrW( hwnd, 0, (LONG_PTR)es ); + + /* + * Note: since the EDITSTATE has not been fully initialized yet, + * we can't use any API calls that may send + * WM_XXX messages before WM_NCCREATE is completed. + */ + + es->style = lpcs->style; + + es->bEnableState = !(es->style & WS_DISABLED); + + es->hwndSelf = hwnd; + /* Save parent, which will be notified by EN_* messages */ + es->hwndParent = lpcs->hwndParent; + + if (es->style & ES_COMBO) + es->hwndListBox = GetDlgItem(es->hwndParent, ID_CB_LISTBOX); + + /* FIXME: should we handle changes to WS_EX_RIGHT style after creation? */ + if (lpcs->dwExStyle & WS_EX_RIGHT) es->style |= ES_RIGHT; + + /* Number overrides lowercase overrides uppercase (at least it + * does in Win95). However I'll bet that ES_NUMBER would be + * invalid under Win 3.1. + */ + if (es->style & ES_NUMBER) { + ; /* do not override the ES_NUMBER */ + } else if (es->style & ES_LOWERCASE) { + es->style &= ~ES_UPPERCASE; + } + if (es->style & ES_MULTILINE) { + es->buffer_limit = BUFLIMIT_INITIAL; + if (es->style & WS_VSCROLL) + es->style |= ES_AUTOVSCROLL; + if (es->style & WS_HSCROLL) + es->style |= ES_AUTOHSCROLL; + es->style &= ~ES_PASSWORD; + if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) { + /* Confirmed - RIGHT overrides CENTER */ + if (es->style & ES_RIGHT) + es->style &= ~ES_CENTER; + es->style &= ~WS_HSCROLL; + es->style &= ~ES_AUTOHSCROLL; + } + } else { + es->buffer_limit = BUFLIMIT_INITIAL; + if ((es->style & ES_RIGHT) && (es->style & ES_CENTER)) + es->style &= ~ES_CENTER; + es->style &= ~WS_HSCROLL; + es->style &= ~WS_VSCROLL; + if (es->style & ES_PASSWORD) + es->password_char = '*'; + } + + alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR)); + if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) + goto cleanup; + es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1; + + if (!(es->undo_text = heap_alloc_zero((es->buffer_size + 1) * sizeof(WCHAR)))) + goto cleanup; + es->undo_buffer_size = es->buffer_size; + + if (es->style & ES_MULTILINE) + if (!(es->first_line_def = heap_alloc_zero(sizeof(LINEDEF)))) + goto cleanup; + es->line_count = 1; + + /* + * In Win95 look and feel, the WS_BORDER style is replaced by the + * WS_EX_CLIENTEDGE style for the edit control. This gives the edit + * control a nonclient area so we don't need to draw the border. + * If WS_BORDER without WS_EX_CLIENTEDGE is specified we shouldn't have + * a nonclient area and we should handle painting the border ourselves. + * + * When making modifications please ensure that the code still works + * for edit controls created directly with style 0x50800000, exStyle 0 + * (which should have a single pixel border) + */ + if (lpcs->dwExStyle & WS_EX_CLIENTEDGE) + es->style &= ~WS_BORDER; + else if (es->style & WS_BORDER) + SetWindowLongW(hwnd, GWL_STYLE, es->style & ~WS_BORDER); + + return TRUE; + +cleanup: + SetWindowLongPtrW(es->hwndSelf, 0, 0); + EDIT_InvalidateUniscribeData(es); + heap_free(es->first_line_def); + heap_free(es->undo_text); + if (es->hloc32W) LocalFree(es->hloc32W); + heap_free(es->logAttr); + heap_free(es); + return FALSE; +} + + +/********************************************************************* + * + * WM_CREATE + * + */ +static LRESULT EDIT_WM_Create(EDITSTATE *es, const WCHAR *name) +{ + RECT clientRect; + + TRACE("%s\n", debugstr_w(name)); + + /* + * To initialize some final structure members, we call some helper + * functions. However, since the EDITSTATE is not consistent (i.e. + * not fully initialized), we should be very careful which + * functions can be called, and in what order. + */ + EDIT_WM_SetFont(es, 0, FALSE); + EDIT_EM_EmptyUndoBuffer(es); + + /* We need to calculate the format rect + (applications may send EM_SETMARGINS before the control gets visible) */ + GetClientRect(es->hwndSelf, &clientRect); + EDIT_SetRectNP(es, &clientRect); + + if (name && *name) + { + EDIT_EM_ReplaceSel(es, FALSE, name, strlenW(name), FALSE, FALSE); + /* if we insert text to the editline, the text scrolls out + * of the window, as the caret is placed after the insert + * pos normally; thus we reset es->selection... to 0 and + * update caret + */ + es->selection_start = es->selection_end = 0; + /* Adobe Photoshop does NOT like this. and MSDN says that EN_CHANGE + * Messages are only to be sent when the USER does something to + * change the contents. So I am removing this EN_CHANGE + * + * EDIT_NOTIFY_PARENT(es, EN_CHANGE); + */ + EDIT_EM_ScrollCaret(es); + } + + /* force scroll info update */ + EDIT_UpdateScrollInfo(es); + OpenThemeData(es->hwndSelf, WC_EDITW); + + /* The rule seems to return 1 here for success */ + /* Power Builder masked edit controls will crash */ + /* if not. */ + /* FIXME: is that in all cases so ? */ + return 1; +} + + +/********************************************************************* + * + * WM_NCDESTROY + * + */ +static LRESULT EDIT_WM_NCDestroy(EDITSTATE *es) +{ + LINEDEF *pc, *pp; + HTHEME theme; + + theme = GetWindowTheme(es->hwndSelf); + CloseThemeData(theme); + + /* The app can own the text buffer handle */ + if (es->hloc32W && (es->hloc32W != es->hlocapp)) + LocalFree(es->hloc32W); + + EDIT_InvalidateUniscribeData(es); + + pc = es->first_line_def; + while (pc) + { + pp = pc->next; + heap_free(pc); + pc = pp; + } + + SetWindowLongPtrW( es->hwndSelf, 0, 0 ); + heap_free(es->undo_text); + heap_free(es); + + return 0; +} + +static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW(hwnd, 0); + HTHEME theme = GetWindowTheme(hwnd); + LRESULT result = 0; + RECT *rect; + + TRACE("hwnd=%p msg=%#x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam); + + if (!es && msg != WM_NCCREATE) + return DefWindowProcW(hwnd, msg, wParam, lParam); + + if (es && (msg != WM_NCDESTROY)) + EDIT_LockBuffer(es); + + switch (msg) + { + case EM_GETSEL: + result = EDIT_EM_GetSel(es, (UINT *)wParam, (UINT *)lParam); + break; + + case EM_SETSEL: + EDIT_EM_SetSel(es, wParam, lParam, FALSE); + EDIT_EM_ScrollCaret(es); + result = 1; + break; + + case EM_GETRECT: + rect = (RECT *)lParam; + if (rect) + *rect = es->format_rect; + break; + + case EM_SETRECT: + if ((es->style & ES_MULTILINE) && lParam) + { + EDIT_SetRectNP(es, (RECT *)lParam); + EDIT_UpdateText(es, NULL, TRUE); + } + break; + + case EM_SETRECTNP: + if ((es->style & ES_MULTILINE) && lParam) + EDIT_SetRectNP(es, (LPRECT)lParam); + break; + + case EM_SCROLL: + result = EDIT_EM_Scroll(es, (INT)wParam); + break; + + case EM_LINESCROLL: + result = (LRESULT)EDIT_EM_LineScroll(es, (INT)wParam, (INT)lParam); + break; + + case EM_SCROLLCARET: + EDIT_EM_ScrollCaret(es); + result = 1; + break; + + case EM_GETMODIFY: + result = ((es->flags & EF_MODIFIED) != 0); + break; + + case EM_SETMODIFY: + if (wParam) + es->flags |= EF_MODIFIED; + else + es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */ + break; + + case EM_GETLINECOUNT: + result = (es->style & ES_MULTILINE) ? es->line_count : 1; + break; + + case EM_LINEINDEX: + result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam); + break; + + case EM_SETHANDLE: + EDIT_EM_SetHandle(es, (HLOCAL)wParam); + break; + + case EM_GETHANDLE: + result = (LRESULT)EDIT_EM_GetHandle(es); + break; + + case EM_GETTHUMB: + result = EDIT_EM_GetThumb(es); + break; + + /* these messages missing from specs */ + case 0x00bf: + case 0x00c0: + case 0x00c3: + case 0x00ca: + FIXME("undocumented message 0x%x, please report\n", msg); + result = DefWindowProcW(hwnd, msg, wParam, lParam); + break; + + case EM_LINELENGTH: + result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam); + break; + + case EM_REPLACESEL: + { + const WCHAR *textW = (const WCHAR *)lParam; + + EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, strlenW(textW), TRUE, TRUE); + result = 1; + break; + } + + case EM_GETLINE: + result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, (LPWSTR)lParam); + break; + + case EM_SETLIMITTEXT: + EDIT_EM_SetLimitText(es, wParam); + break; + + case EM_CANUNDO: + result = (LRESULT)EDIT_EM_CanUndo(es); + break; + + case EM_UNDO: + case WM_UNDO: + result = (LRESULT)EDIT_EM_Undo(es); + break; + + case EM_FMTLINES: + result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam); + break; + + case EM_LINEFROMCHAR: + result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam); + break; + + case EM_SETTABSTOPS: + result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam); + break; + + case EM_SETPASSWORDCHAR: + EDIT_EM_SetPasswordChar(es, wParam); + break; + + case EM_EMPTYUNDOBUFFER: + EDIT_EM_EmptyUndoBuffer(es); + break; + + case EM_GETFIRSTVISIBLELINE: + result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset; + break; + + case EM_SETREADONLY: + { + DWORD old_style = es->style; + + if (wParam) + { + SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | ES_READONLY); + es->style |= ES_READONLY; + } + else + { + SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~ES_READONLY); + es->style &= ~ES_READONLY; + } + + if (old_style ^ es->style) + InvalidateRect(es->hwndSelf, NULL, TRUE); + + result = 1; + break; + } + + case EM_SETWORDBREAKPROC: + EDIT_EM_SetWordBreakProc(es, (void *)lParam); + result = 1; + break; + + case EM_GETWORDBREAKPROC: + result = (LRESULT)es->word_break_proc; + break; + + case EM_GETPASSWORDCHAR: + result = es->password_char; + break; + + case EM_SETMARGINS: + EDIT_EM_SetMargins(es, (INT)wParam, LOWORD(lParam), HIWORD(lParam), TRUE); + break; + + case EM_GETMARGINS: + result = MAKELONG(es->left_margin, es->right_margin); + break; + + case EM_GETLIMITTEXT: + result = es->buffer_limit; + break; + + case EM_POSFROMCHAR: + if ((INT)wParam >= get_text_length(es)) result = -1; + else result = EDIT_EM_PosFromChar(es, (INT)wParam, FALSE); + break; + + case EM_CHARFROMPOS: + result = EDIT_EM_CharFromPos(es, (short)LOWORD(lParam), (short)HIWORD(lParam)); + break; + + /* End of the EM_ messages which were in numerical order; what order + * are these in? vaguely alphabetical? + */ + + case WM_NCCREATE: + result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTW)lParam); + break; + + case WM_NCDESTROY: + result = EDIT_WM_NCDestroy(es); + es = NULL; + break; + + case WM_GETDLGCODE: + result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS; + + if (es->style & ES_MULTILINE) + result |= DLGC_WANTALLKEYS; + + if (lParam) + { + MSG *msg = (MSG *)lParam; + es->flags |= EF_DIALOGMODE; + + if (msg->message == WM_KEYDOWN) + { + int vk = (int)msg->wParam; + + if (es->hwndListBox) + { + if (vk == VK_RETURN || vk == VK_ESCAPE) + if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0)) + result |= DLGC_WANTMESSAGE; + } + } + } + break; + + case WM_IME_CHAR: + case WM_CHAR: + { + WCHAR charW = wParam; + + if (es->hwndListBox) + { + if (charW == VK_RETURN || charW == VK_ESCAPE) + { + if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0)) + SendMessageW(GetParent(hwnd), WM_KEYDOWN, charW, 0); + break; + } + } + result = EDIT_WM_Char(es, charW); + break; + } + + case WM_UNICHAR: + if (wParam == UNICODE_NOCHAR) return TRUE; + if (wParam <= 0x000fffff) + { + if (wParam > 0xffff) /* convert to surrogates */ + { + wParam -= 0x10000; + EDIT_WM_Char(es, (wParam >> 10) + 0xd800); + EDIT_WM_Char(es, (wParam & 0x03ff) + 0xdc00); + } + else + EDIT_WM_Char(es, wParam); + } + return 0; + + case WM_CLEAR: + EDIT_WM_Clear(es); + break; + + case WM_CONTEXTMENU: + EDIT_WM_ContextMenu(es, (short)LOWORD(lParam), (short)HIWORD(lParam)); + break; + + case WM_COPY: + EDIT_WM_Copy(es); + break; + + case WM_CREATE: + result = EDIT_WM_Create(es, ((LPCREATESTRUCTW)lParam)->lpszName); + break; + + case WM_CUT: + EDIT_WM_Cut(es); + break; + + case WM_ENABLE: + es->bEnableState = (BOOL) wParam; + EDIT_UpdateText(es, NULL, TRUE); + if (theme) + RedrawWindow(hwnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); + break; + + case WM_ERASEBKGND: + /* we do the proper erase in EDIT_WM_Paint */ + result = 1; + break; + + case WM_GETFONT: + result = (LRESULT)es->font; + break; + + case WM_GETTEXT: + result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, (LPWSTR)lParam); + break; + + case WM_GETTEXTLENGTH: + result = get_text_length(es); + break; + + case WM_HSCROLL: + result = EDIT_WM_HScroll(es, LOWORD(wParam), (short)HIWORD(wParam)); + break; + + case WM_KEYDOWN: + result = EDIT_WM_KeyDown(es, (INT)wParam); + break; + + case WM_KILLFOCUS: + result = EDIT_WM_KillFocus(theme, es); + break; + + case WM_LBUTTONDBLCLK: + result = EDIT_WM_LButtonDblClk(es); + break; + + case WM_LBUTTONDOWN: + result = EDIT_WM_LButtonDown(es, wParam, (short)LOWORD(lParam), (short)HIWORD(lParam)); + break; + + case WM_LBUTTONUP: + result = EDIT_WM_LButtonUp(es); + break; + + case WM_MBUTTONDOWN: + result = EDIT_WM_MButtonDown(es); + break; + + case WM_MOUSEMOVE: + result = EDIT_WM_MouseMove(es, (short)LOWORD(lParam), (short)HIWORD(lParam)); + break; + + case WM_PRINTCLIENT: + case WM_PAINT: + EDIT_WM_Paint(es, (HDC)wParam); + break; + + case WM_NCPAINT: + EDIT_WM_NCPaint(hwnd, (HRGN)wParam); + break; + + case WM_PASTE: + EDIT_WM_Paste(es); + break; + + case WM_SETFOCUS: + EDIT_WM_SetFocus(theme, es); + break; + + case WM_SETFONT: + EDIT_WM_SetFont(es, (HFONT)wParam, LOWORD(lParam) != 0); + break; + + case WM_SETREDRAW: + /* FIXME: actually set an internal flag and behave accordingly */ + break; + + case WM_SETTEXT: + EDIT_WM_SetText(es, (const WCHAR *)lParam); + result = TRUE; + break; + + case WM_SIZE: + EDIT_WM_Size(es, (UINT)wParam); + break; + + case WM_STYLECHANGED: + result = EDIT_WM_StyleChanged(es, wParam, (const STYLESTRUCT *)lParam); + break; + + case WM_STYLECHANGING: + result = 0; /* See EDIT_WM_StyleChanged */ + break; + + case WM_SYSKEYDOWN: + result = EDIT_WM_SysKeyDown(es, (INT)wParam, (DWORD)lParam); + break; + + case WM_TIMER: + EDIT_WM_Timer(es); + break; + + case WM_VSCROLL: + result = EDIT_WM_VScroll(es, LOWORD(wParam), (short)HIWORD(wParam)); + break; + + case WM_MOUSEWHEEL: + { + int wheelDelta; + UINT pulScrollLines = 3; + SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); + + if (wParam & (MK_SHIFT | MK_CONTROL)) + { + result = DefWindowProcW(hwnd, msg, wParam, lParam); + break; + } + + wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam); + /* if scrolling changes direction, ignore left overs */ + if ((wheelDelta < 0 && es->wheelDeltaRemainder < 0) || + (wheelDelta > 0 && es->wheelDeltaRemainder > 0)) + es->wheelDeltaRemainder += wheelDelta; + else + es->wheelDeltaRemainder = wheelDelta; + + if (es->wheelDeltaRemainder && pulScrollLines) + { + int cLineScroll; + pulScrollLines = (int) min((UINT) es->line_count, pulScrollLines); + cLineScroll = pulScrollLines * (float)es->wheelDeltaRemainder / WHEEL_DELTA; + es->wheelDeltaRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines; + result = EDIT_EM_LineScroll(es, 0, -cLineScroll); + } + break; + } + + /* IME messages to make the edit control IME aware */ + case WM_IME_SETCONTEXT: + break; + + case WM_IME_STARTCOMPOSITION: + es->composition_start = es->selection_end; + es->composition_len = 0; + break; + + case WM_IME_COMPOSITION: + EDIT_ImeComposition(hwnd, lParam, es); + break; + + case WM_IME_ENDCOMPOSITION: + if (es->composition_len > 0) + { + EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE); + es->selection_end = es->selection_start; + es->composition_len= 0; + } + break; + + case WM_IME_COMPOSITIONFULL: + break; + + case WM_IME_SELECT: + break; + + case WM_IME_CONTROL: + break; + + case WM_IME_REQUEST: + switch (wParam) + { + case IMR_QUERYCHARPOSITION: + { + IMECHARPOSITION *chpos = (IMECHARPOSITION *)lParam; + LRESULT pos; + + pos = EDIT_EM_PosFromChar(es, es->selection_start + chpos->dwCharPos, FALSE); + chpos->pt.x = LOWORD(pos); + chpos->pt.y = HIWORD(pos); + chpos->cLineHeight = es->line_height; + chpos->rcDocument = es->format_rect; + MapWindowPoints(hwnd, 0, &chpos->pt, 1); + MapWindowPoints(hwnd, 0, (POINT*)&chpos->rcDocument, 2); + result = 1; + break; + } + } + break; + + case WM_THEMECHANGED: + CloseThemeData (theme); + OpenThemeData(hwnd, WC_EDITW); + break; + + default: + result = DefWindowProcW(hwnd, msg, wParam, lParam); + break; + } + + if (IsWindow(hwnd) && es && msg != EM_GETHANDLE) + EDIT_UnlockBuffer(es, FALSE); + + TRACE("hwnd=%p msg=%x -- 0x%08lx\n", hwnd, msg, result); + + return result; +} + +void EDIT_Register(void) +{ + WNDCLASSW wndClass; + + memset(&wndClass, 0, sizeof(wndClass)); + wndClass.style = CS_PARENTDC | CS_GLOBALCLASS | CS_DBLCLKS; + wndClass.lpfnWndProc = EDIT_WindowProc; + wndClass.cbClsExtra = 0; +#ifdef __i386__ + wndClass.cbWndExtra = sizeof(EDITSTATE *) + sizeof(WORD); +#else + wndClass.cbWndExtra = sizeof(EDITSTATE *); +#endif + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_IBEAM); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = WC_EDITW; + RegisterClassW(&wndClass); +} diff --git a/dll/win32/comctl32/flatsb.c b/dll/win32/comctl32/flatsb.c index 9dd79437460..66e25c307fa 100644 --- a/dll/win32/comctl32/flatsb.c +++ b/dll/win32/comctl32/flatsb.c @@ -29,7 +29,15 @@ * */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(commctrl); diff --git a/dll/win32/comctl32/header.c b/dll/win32/comctl32/header.c index f6f85c4a6d7..a48530d8882 100644 --- a/dll/win32/comctl32/header.c +++ b/dll/win32/comctl32/header.c @@ -27,7 +27,22 @@ * - New Windows Vista features */ +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wine/unicode.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "vssym32.h" +#include "uxtheme.h" +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(header); @@ -127,7 +142,7 @@ static void HEADER_StoreHDItemInHeader(HEADER_ITEM *lpItem, UINT mask, const HDI if (mask & HDI_TEXT) { - Free(lpItem->pszText); + heap_free(lpItem->pszText); lpItem->pszText = NULL; if (phdi->pszText != LPSTR_TEXTCALLBACKW) /* covers != TEXTCALLBACKA too */ @@ -324,7 +339,7 @@ static HRGN create_sort_arrow( INT x, INT y, INT h, BOOL is_up ) if (size > sizeof(buffer)) { - data = HeapAlloc( GetProcessHeap(), 0, size ); + data = heap_alloc( size ); if (!data) return NULL; } data->rdh.dwSize = sizeof(data->rdh); @@ -350,7 +365,7 @@ static HRGN create_sort_arrow( INT x, INT y, INT h, BOOL is_up ) data->rdh.nCount++; } rgn = ExtCreateRegion( NULL, size, data ); - if (data != (RGNDATA *)buffer) HeapFree( GetProcessHeap(), 0, data ); + if (data != (RGNDATA *)buffer) heap_free( data ); return rgn; } @@ -971,7 +986,7 @@ HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask) if (mask&HDI_TEXT && lpItem->pszText != NULL) { ERR("(): function called without a call to FreeCallbackItems\n"); - Free(lpItem->pszText); + heap_free(lpItem->pszText); lpItem->pszText = NULL; } @@ -982,13 +997,13 @@ HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask) { dispInfo.hdr.code = HDN_GETDISPINFOW; if (mask & HDI_TEXT) - pvBuffer = Alloc(MAX_HEADER_TEXT_LEN * sizeof(WCHAR)); + pvBuffer = heap_alloc_zero(MAX_HEADER_TEXT_LEN * sizeof(WCHAR)); } else { dispInfo.hdr.code = HDN_GETDISPINFOA; if (mask & HDI_TEXT) - pvBuffer = Alloc(MAX_HEADER_TEXT_LEN * sizeof(CHAR)); + pvBuffer = heap_alloc_zero(MAX_HEADER_TEXT_LEN * sizeof(CHAR)); } dispInfo.pszText = pvBuffer; dispInfo.cchTextMax = (pvBuffer!=NULL?MAX_HEADER_TEXT_LEN:0); @@ -1019,7 +1034,7 @@ HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask) else { Str_SetPtrAtoW(&lpItem->pszText, (LPSTR)dispInfo.pszText); - Free(pvBuffer); + heap_free(pvBuffer); } } @@ -1045,7 +1060,7 @@ HEADER_FreeCallbackItems(HEADER_ITEM *lpItem) { if (lpItem->callbackMask&HDI_TEXT) { - Free(lpItem->pszText); + heap_free(lpItem->pszText); lpItem->pszText = NULL; } @@ -1167,15 +1182,15 @@ HEADER_DeleteItem (HEADER_INFO *infoPtr, INT iItem) TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i, infoPtr->order[i], infoPtr->items[i].iOrder, infoPtr->items[infoPtr->order[i]].iOrder); iOrder = infoPtr->items[iItem].iOrder; - Free(infoPtr->items[iItem].pszText); + heap_free(infoPtr->items[iItem].pszText); infoPtr->uNumItem--; memmove(&infoPtr->items[iItem], &infoPtr->items[iItem + 1], (infoPtr->uNumItem - iItem) * sizeof(HEADER_ITEM)); memmove(&infoPtr->order[iOrder], &infoPtr->order[iOrder + 1], (infoPtr->uNumItem - iOrder) * sizeof(INT)); - infoPtr->items = ReAlloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem); - infoPtr->order = ReAlloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem); + infoPtr->items = heap_realloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem); + infoPtr->order = heap_realloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem); /* Correct the orders */ for (i = 0; i < infoPtr->uNumItem; i++) @@ -1405,8 +1420,8 @@ HEADER_InsertItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL b iOrder = infoPtr->uNumItem; infoPtr->uNumItem++; - infoPtr->items = ReAlloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem); - infoPtr->order = ReAlloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem); + infoPtr->items = heap_realloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem); + infoPtr->order = heap_realloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem); /* make space for the new item */ memmove(&infoPtr->items[nItem + 1], &infoPtr->items[nItem], @@ -1525,8 +1540,8 @@ HEADER_SetItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL bUni HEADER_CopyHDItemForNotify(infoPtr, &hdNotify, phdi, bUnicode, &pvScratch); if (HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCHANGINGW, nItem, &hdNotify)) { - Free(pvScratch); - return FALSE; + heap_free(pvScratch); + return FALSE; } lpItem = &infoPtr->items[nItem]; @@ -1542,7 +1557,7 @@ HEADER_SetItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL bUni InvalidateRect(infoPtr->hwndSelf, NULL, FALSE); - Free(pvScratch); + heap_free(pvScratch); return TRUE; } @@ -1565,7 +1580,7 @@ HEADER_Create (HWND hwnd, const CREATESTRUCTW *lpcs) HFONT hOldFont; HDC hdc; - infoPtr = Alloc (sizeof(HEADER_INFO)); + infoPtr = heap_alloc_zero (sizeof(*infoPtr)); SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); infoPtr->hwndSelf = hwnd; @@ -1619,16 +1634,15 @@ HEADER_NCDestroy (HEADER_INFO *infoPtr) if (infoPtr->items) { lpItem = infoPtr->items; - for (nItem = 0; nItem < infoPtr->uNumItem; nItem++, lpItem++) { - Free(lpItem->pszText); - } - Free (infoPtr->items); + for (nItem = 0; nItem < infoPtr->uNumItem; nItem++, lpItem++) + heap_free(lpItem->pszText); + heap_free(infoPtr->items); } - Free(infoPtr->order); + heap_free(infoPtr->order); SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); - Free (infoPtr); + heap_free(infoPtr); return 0; } diff --git a/dll/win32/comctl32/hotkey.c b/dll/win32/comctl32/hotkey.c index 6a35a3ae927..7c3bff7f750 100644 --- a/dll/win32/comctl32/hotkey.c +++ b/dll/win32/comctl32/hotkey.c @@ -28,7 +28,17 @@ * */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(hotkey); @@ -232,7 +242,7 @@ HOTKEY_Destroy (HOTKEY_INFO *infoPtr) { /* free hotkey info data */ SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); - Free (infoPtr); + heap_free (infoPtr); return 0; } @@ -401,7 +411,7 @@ HOTKEY_NCCreate (HWND hwnd, const CREATESTRUCTW *lpcs) dwExStyle | WS_EX_CLIENTEDGE); /* allocate memory for info structure */ - infoPtr = Alloc (sizeof(HOTKEY_INFO)); + infoPtr = heap_alloc_zero (sizeof(*infoPtr)); SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); /* initialize info structure */ diff --git a/dll/win32/comctl32/imagelist.c b/dll/win32/comctl32/imagelist.c index c07600217be..1f119f3504e 100644 --- a/dll/win32/comctl32/imagelist.c +++ b/dll/win32/comctl32/imagelist.c @@ -22,25 +22,30 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 12, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * - Add support for ILD_PRESERVEALPHA, ILD_SCALE, ILD_DPISCALE * - Add support for ILS_GLOW, ILS_SHADOW * - Thread-safe locking */ -#include "comctl32.h" +#include +#include +#include -#include -#include +#define COBJMACROS + +#include "winerror.h" +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commctrl.h" +#include "comctl32.h" +#include "commoncontrols.h" +#include "wine/debug.h" +#include "wine/exception.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(imagelist); @@ -261,7 +266,7 @@ static BOOL add_with_alpha( HIMAGELIST himl, HDC hdc, int pos, int count, SelectObject( hdc, hbmImage ); mask_width = (bm.bmWidth + 31) / 32 * 4; - if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done; + if (!(info = heap_alloc( FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done; info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); info->bmiHeader.biWidth = bm.bmWidth; info->bmiHeader.biHeight = -height; @@ -273,17 +278,17 @@ static BOOL add_with_alpha( HIMAGELIST himl, HDC hdc, int pos, int count, info->bmiHeader.biYPelsPerMeter = 0; info->bmiHeader.biClrUsed = 0; info->bmiHeader.biClrImportant = 0; - if (!(bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto done; + if (!(bits = heap_alloc( info->bmiHeader.biSizeImage ))) goto done; if (!GetDIBits( hdc, hbmImage, 0, height, bits, info, DIB_RGB_COLORS )) goto done; if (hbmMask) { - if (!(mask_info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[2] )))) + if (!(mask_info = heap_alloc( FIELD_OFFSET( BITMAPINFO, bmiColors[2] )))) goto done; mask_info->bmiHeader = info->bmiHeader; mask_info->bmiHeader.biBitCount = 1; mask_info->bmiHeader.biSizeImage = mask_width * height; - if (!(mask_bits = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mask_info->bmiHeader.biSizeImage ))) + if (!(mask_bits = heap_alloc_zero( mask_info->bmiHeader.biSizeImage ))) goto done; if (!GetDIBits( hdc, hbmMask, 0, height, mask_bits, mask_info, DIB_RGB_COLORS )) goto done; } @@ -292,10 +297,10 @@ static BOOL add_with_alpha( HIMAGELIST himl, HDC hdc, int pos, int count, ret = TRUE; done: - HeapFree( GetProcessHeap(), 0, info ); - HeapFree( GetProcessHeap(), 0, mask_info ); - HeapFree( GetProcessHeap(), 0, bits ); - HeapFree( GetProcessHeap(), 0, mask_bits ); + heap_free( info ); + heap_free( mask_info ); + heap_free( bits ); + heap_free( mask_bits ); return ret; } @@ -378,7 +383,7 @@ IMAGELIST_InternalExpandBitmaps(HIMAGELIST himl, INT nImageCount) if (new_alpha) himl->has_alpha = new_alpha; else { - HeapFree( GetProcessHeap(), 0, himl->has_alpha ); + heap_free( himl->has_alpha ); himl->has_alpha = NULL; } } @@ -849,7 +854,7 @@ ImageList_Create (INT cx, INT cy, UINT flags, himl->hbmMask = 0; if (ilc == ILC_COLOR32) - himl->has_alpha = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, himl->cMaxImage ); + himl->has_alpha = heap_alloc_zero( himl->cMaxImage ); else himl->has_alpha = NULL; @@ -1260,7 +1265,7 @@ static BOOL alpha_blend_image( HIMAGELIST himl, HDC dest_dc, int dest_x, int des int i, j; if (!(hdc = CreateCompatibleDC( 0 ))) return FALSE; - if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done; + if (!(info = heap_alloc( FIELD_OFFSET( BITMAPINFO, bmiColors[256] )))) goto done; info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); info->bmiHeader.biWidth = cx; info->bmiHeader.biHeight = cy; @@ -1351,7 +1356,7 @@ done: DeleteDC( hdc ); if (bmp) DeleteObject( bmp ); if (mask) DeleteObject( mask ); - HeapFree( GetProcessHeap(), 0, info ); + heap_free( info ); return ret; } @@ -2066,11 +2071,11 @@ ImageList_LoadImageA (HINSTANCE hi, LPCSTR lpbmp, INT cx, INT cGrow, uType, uFlags); len = MultiByteToWideChar(CP_ACP, 0, lpbmp, -1, NULL, 0); - lpbmpW = Alloc(len * sizeof(WCHAR)); + lpbmpW = heap_alloc(len * sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, lpbmp, -1, lpbmpW, len); himl = ImageList_LoadImageW(hi, lpbmpW, cx, cGrow, clrMask, uType, uFlags); - Free (lpbmpW); + heap_free (lpbmpW); return himl; } @@ -2311,12 +2316,12 @@ static void *read_bitmap(IStream *pstm, BITMAPINFO *bmi) if (palspace && FAILED(IStream_Read(pstm, bmi->bmiColors, palspace, NULL))) return NULL; - bits = Alloc(bmi->bmiHeader.biSizeImage); + bits = heap_alloc_zero(bmi->bmiHeader.biSizeImage); if (!bits) return NULL; if (FAILED(IStream_Read(pstm, bits, bmi->bmiHeader.biSizeImage, NULL))) { - Free(bits); + heap_free(bits); return NULL; } return bits; @@ -2428,8 +2433,8 @@ HIMAGELIST WINAPI ImageList_Read(IStream *pstm) 0, 0, mask_info->bmiHeader.biWidth, mask_info->bmiHeader.biHeight, mask_bits, mask_info, DIB_RGB_COLORS, SRCCOPY); } - Free( image_bits ); - Free( mask_bits ); + heap_free( image_bits ); + heap_free( mask_bits ); himl->cCurImage = ilHead.cCurImage; himl->cMaxImage = ilHead.cMaxImage; @@ -2493,8 +2498,8 @@ ImageList_Remove (HIMAGELIST himl, INT i) if (himl->has_alpha) { - HeapFree( GetProcessHeap(), 0, himl->has_alpha ); - himl->has_alpha = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, himl->cMaxImage ); + heap_free( himl->has_alpha ); + himl->has_alpha = heap_alloc_zero( himl->cMaxImage ); } hbmNewImage = ImageList_CreateImage(himl->hdcImage, himl, himl->cMaxImage); @@ -3041,7 +3046,7 @@ ImageList_SetImageCount (HIMAGELIST himl, UINT iImageCount) if (new_alpha) himl->has_alpha = new_alpha; else { - HeapFree( GetProcessHeap(), 0, himl->has_alpha ); + heap_free( himl->has_alpha ); himl->has_alpha = NULL; } } @@ -3109,7 +3114,7 @@ static BOOL _write_bitmap(HBITMAP hBitmap, IStream *pstm) offBits = totalSize; totalSize += sizeImage; - data = Alloc(totalSize); + data = heap_alloc_zero(totalSize); bmfh = (LPBITMAPFILEHEADER)data; bmih = (LPBITMAPINFOHEADER)(data + sizeof(BITMAPFILEHEADER)); lpBits = data + offBits; @@ -3150,7 +3155,7 @@ static BOOL _write_bitmap(HBITMAP hBitmap, IStream *pstm) result = TRUE; failed: - Free(data); + heap_free(data); return result; } @@ -3387,8 +3392,8 @@ static ULONG WINAPI ImageListImpl_Release(IImageList2 *iface) if (This->hbrBlend50) DeleteObject (This->hbrBlend50); This->IImageList2_iface.lpVtbl = NULL; - HeapFree(GetProcessHeap(), 0, This->has_alpha); - HeapFree(GetProcessHeap(), 0, This); + heap_free(This->has_alpha); + heap_free(This); } return ref; @@ -3917,7 +3922,7 @@ static HRESULT ImageListImpl_CreateInstance(const IUnknown *pUnkOuter, REFIID ii if (pUnkOuter) return CLASS_E_NOAGGREGATION; - This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct _IMAGELIST)); + This = heap_alloc_zero(sizeof(struct _IMAGELIST)); if (!This) return E_OUTOFMEMORY; This->IImageList2_iface.lpVtbl = &ImageListImpl_Vtbl; diff --git a/dll/win32/comctl32/ipaddress.c b/dll/win32/comctl32/ipaddress.c index a7a7ef36f16..6e7b703bb49 100644 --- a/dll/win32/comctl32/ipaddress.c +++ b/dll/win32/comctl32/ipaddress.c @@ -20,19 +20,27 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * */ +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vsstyle.h" +#include "vssym32.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(ipaddress); @@ -211,7 +219,7 @@ static LRESULT IPADDRESS_Create (HWND hwnd, const CREATESTRUCTA *lpCreate) SetWindowLongW (hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~WS_BORDER); - infoPtr = Alloc (sizeof(IPADDRESS_INFO)); + infoPtr = heap_alloc_zero (sizeof(*infoPtr)); if (!infoPtr) return -1; SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); @@ -270,7 +278,7 @@ static LRESULT IPADDRESS_Destroy (IPADDRESS_INFO *infoPtr) } SetWindowLongPtrW (infoPtr->Self, 0, 0); - Free (infoPtr); + heap_free (infoPtr); return 0; } diff --git a/dll/win32/comctl32/listbox.c b/dll/win32/comctl32/listbox.c new file mode 100644 index 00000000000..7857aa8174c --- /dev/null +++ b/dll/win32/comctl32/listbox.c @@ -0,0 +1,3083 @@ +/* + * Listbox controls + * + * Copyright 1996 Alexandre Julliard + * Copyright 2005 Frank Richter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * TODO: + * - LBS_NODATA + */ + +#include +#include +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commctrl.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/unicode.h" +#include "wine/exception.h" +#include "wine/debug.h" + +#include "comctl32.h" + +WINE_DEFAULT_DEBUG_CHANNEL(listbox2); + +/* 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 + +/* flag listbox changed while setredraw false - internal style */ +#define LBS_DISPLAYCHANGED 0x80000000 + +/* Item structure */ +typedef struct +{ + LPWSTR str; /* Item text */ + BOOL selected; /* Is item selected? */ + UINT height; /* Item height (only for OWNERDRAWVARIABLE) */ + ULONG_PTR data; /* User data */ +} LB_ITEMDATA; + +/* Listbox structure */ +typedef struct +{ + HWND self; /* Our own window handle */ + HWND owner; /* Owner window to send notifications to */ + UINT style; /* Window style */ + INT width; /* Window width */ + INT height; /* Window height */ + LB_ITEMDATA *items; /* Array of items */ + INT nb_items; /* Number of items */ + INT top_item; /* Top visible item */ + INT selected_item; /* Selected item */ + INT focus_item; /* Item that has the focus */ + INT anchor_item; /* Anchor item for extended selection */ + INT item_height; /* Default item height */ + INT page_size; /* Items per listbox page */ + INT column_width; /* Column width for multi-column listboxes */ + INT horz_extent; /* Horizontal extent */ + INT horz_pos; /* Horizontal position */ + INT nb_tabs; /* Number of tabs in array */ + INT *tabs; /* Array of tabs */ + INT avg_char_width; /* Average width of characters */ + INT wheel_remain; /* Left over scroll amount */ + BOOL caret_on; /* Is caret on? */ + BOOL captured; /* Is mouse captured? */ + BOOL in_focus; + HFONT font; /* Current font */ + LCID locale; /* Current locale for string comparisons */ + HEADCOMBO *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 IS_MULTISELECT(descr) \ + ((descr)->style & (LBS_MULTIPLESEL|LBS_EXTENDEDSEL) && \ + !((descr)->style & LBS_NOSEL)) + +#define SEND_NOTIFICATION(descr,code) \ + (SendMessageW( (descr)->owner, WM_COMMAND, \ + MAKEWPARAM( GetWindowLongPtrW((descr->self),GWLP_ID), (code)), (LPARAM)(descr->self) )) + +#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03) + +/* 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; + +static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect ); + +/*********************************************************************** + * LISTBOX_GetCurrentPageSize + * + * Return the current page size + */ +static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr ) +{ + INT 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 INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr ) +{ + INT 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( LB_DESCR *descr ) +{ + SCROLLINFO info; + + /* Check the listbox scroll bar flags individually before we call + SetScrollInfo otherwise when the listbox style is WS_HSCROLL and + no WS_VSCROLL, we end up with an uninitialized, visible horizontal + scroll bar when we do not need one. + 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; + if (descr->style & WS_HSCROLL) + SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); + info.nMax = 0; + info.fMask = SIF_RANGE; + if (descr->style & WS_VSCROLL) + SetScrollInfo( descr->self, SB_VERT, &info, TRUE ); + } + else + { + info.nMin = 0; + info.nMax = descr->nb_items - 1; + info.nPos = descr->top_item; + info.nPage = LISTBOX_GetCurrentPageSize( descr ); + info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + if (descr->style & LBS_DISABLENOSCROLL) + info.fMask |= SIF_DISABLENOSCROLL; + if (descr->style & WS_VSCROLL) + SetScrollInfo( descr->self, SB_VERT, &info, TRUE ); + + if ((descr->style & WS_HSCROLL) && descr->horz_extent) + { + info.nPos = descr->horz_pos; + info.nPage = descr->width; + info.fMask = SIF_POS | SIF_PAGE; + if (descr->style & LBS_DISABLENOSCROLL) + info.fMask |= SIF_DISABLENOSCROLL; + SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); + } + else + { + if (descr->style & LBS_DISABLENOSCROLL) + { + info.nMin = 0; + info.nMax = 0; + info.fMask = SIF_RANGE | SIF_DISABLENOSCROLL; + SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); + } + else + { + ShowScrollBar( descr->self, SB_HORZ, FALSE ); + } + } + } +} + + +/*********************************************************************** + * LISTBOX_SetTopItem + * + * Set the top item of the listbox, scrolling up or down if necessary. + */ +static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll ) +{ + INT max = LISTBOX_GetMaxTopIndex( descr ); + + TRACE("setting top item %d, scroll %d\n", index, scroll); + + 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 (scroll) + { + INT diff; + if (descr->style & LBS_MULTICOLUMN) + diff = (descr->top_item - index) / descr->page_size * descr->column_width; + else if (descr->style & LBS_OWNERDRAWVARIABLE) + { + INT 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; + + ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL, + SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); + } + else + InvalidateRect( descr->self, NULL, TRUE ); + descr->top_item = index; + LISTBOX_UpdateScroll( 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( LB_DESCR *descr ) +{ + INT page_size; + + if ((descr->item_height == 0) || (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) + InvalidateRect( descr->self, NULL, TRUE ); + LISTBOX_SetTopItem( 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( LB_DESCR *descr ) +{ + RECT rect; + + GetClientRect( descr->self, &rect ); + descr->width = rect.right - rect.left; + descr->height = rect.bottom - rect.top; + if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE)) + { + INT remaining; + RECT rect; + + GetWindowRect( descr->self, &rect ); + if(descr->item_height != 0) + remaining = descr->height % descr->item_height; + else + remaining = 0; + if ((descr->height > descr->item_height) && remaining) + { + TRACE("[%p]: changing height %d -> %d\n", + descr->self, descr->height, descr->height - remaining ); + SetWindowPos( descr->self, 0, 0, 0, rect.right - rect.left, + rect.bottom - rect.top - remaining, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE ); + return; + } + } + TRACE("[%p]: new size = %d,%d\n", descr->self, descr->width, descr->height ); + LISTBOX_UpdatePage( descr ); + LISTBOX_UpdateScroll( descr ); + + /* Invalidate the focused item so it will be repainted correctly */ + if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1) + { + InvalidateRect( descr->self, &rect, FALSE ); + } +} + + +/*********************************************************************** + * 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( const LB_DESCR *descr, INT index, RECT *rect ) +{ + /* Index <= 0 is legal even on empty listboxes */ + if (index && (index >= descr->nb_items)) + { + SetRectEmpty(rect); + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + SetRect( rect, 0, 0, descr->width, descr->height ); + if (descr->style & LBS_MULTICOLUMN) + { + INT 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) + { + INT 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; + } + + TRACE("item %d, rect %s\n", index, wine_dbgstr_rect(rect)); + + 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 INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y ) +{ + INT index = descr->top_item; + + if (!descr->nb_items) return -1; /* No items */ + if (descr->style & LBS_OWNERDRAWVARIABLE) + { + INT 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( LB_DESCR *descr, HDC hdc, const RECT *rect, + INT index, UINT action, BOOL ignoreFocus ) +{ + LB_ITEMDATA *item = NULL; + if (index < descr->nb_items) item = &descr->items[index]; + + if (IS_OWNERDRAW(descr)) + { + DRAWITEMSTRUCT dis; + RECT r; + HRGN hrgn; + + if (!item) + { + if (action == ODA_FOCUS) + DrawFocusRect( hdc, rect ); + else + ERR("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items); + return; + } + + /* some programs mess with the clipping region when + drawing the item, *and* restore the previous region + after they are done, so a region has better to exist + else everything ends clipped */ + GetClientRect(descr->self, &r); + hrgn = set_control_clipping( hdc, &r ); + + dis.CtlType = ODT_LISTBOX; + dis.CtlID = GetWindowLongPtrW( descr->self, GWLP_ID ); + dis.hwndItem = descr->self; + dis.itemAction = action; + dis.hDC = hdc; + dis.itemID = index; + dis.itemState = 0; + if (item->selected) dis.itemState |= ODS_SELECTED; + if (!ignoreFocus && (descr->focus_item == index) && + (descr->caret_on) && + (descr->in_focus)) dis.itemState |= ODS_FOCUS; + if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED; + dis.itemData = item->data; + dis.rcItem = *rect; + TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n", + descr->self, index, debugstr_w(item->str), action, + dis.itemState, wine_dbgstr_rect(rect) ); + SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); + SelectClipRgn( hdc, hrgn ); + if (hrgn) DeleteObject( hrgn ); + } + else + { + COLORREF oldText = 0, oldBk = 0; + + if (action == ODA_FOCUS) + { + DrawFocusRect( hdc, rect ); + return; + } + if (item && item->selected) + { + oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); + oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } + + TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n", + descr->self, index, item ? debugstr_w(item->str) : "", action, + wine_dbgstr_rect(rect) ); + if (!item) + ExtTextOutW( hdc, rect->left + 1, rect->top, + ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); + else if (!(descr->style & LBS_USETABSTOPS)) + ExtTextOutW( hdc, rect->left + 1, rect->top, + ETO_OPAQUE | ETO_CLIPPED, rect, item->str, + strlenW(item->str), NULL ); + else + { + /* Output empty string to paint background in the full width. */ + ExtTextOutW( hdc, rect->left + 1, rect->top, + ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL ); + TabbedTextOutW( hdc, rect->left + 1 , rect->top, + item->str, strlenW(item->str), + descr->nb_tabs, descr->tabs, 0); + } + if (item && item->selected) + { + SetBkColor( hdc, oldBk ); + SetTextColor( hdc, oldText ); + } + if (!ignoreFocus && (descr->focus_item == index) && + (descr->caret_on) && + (descr->in_focus)) DrawFocusRect( hdc, rect ); + } +} + + +/*********************************************************************** + * LISTBOX_SetRedraw + * + * Change the redraw flag. + */ +static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on ) +{ + if (on) + { + if (!(descr->style & LBS_NOREDRAW)) return; + descr->style &= ~LBS_NOREDRAW; + if (descr->style & LBS_DISPLAYCHANGED) + { /* page was changed while setredraw false, refresh automatically */ + InvalidateRect(descr->self, NULL, TRUE); + if ((descr->top_item + descr->page_size) > descr->nb_items) + { /* reset top of page if less than number of items/page */ + descr->top_item = descr->nb_items - descr->page_size; + if (descr->top_item < 0) descr->top_item = 0; + } + descr->style &= ~LBS_DISPLAYCHANGED; + } + LISTBOX_UpdateScroll( descr ); + } + else descr->style |= LBS_NOREDRAW; +} + + +/*********************************************************************** + * LISTBOX_RepaintItem + * + * Repaint a single item synchronously. + */ +static void LISTBOX_RepaintItem( LB_DESCR *descr, INT index, UINT action ) +{ + HDC hdc; + RECT rect; + HFONT oldFont = 0; + HBRUSH hbrush, oldBrush = 0; + + /* Do not repaint the item if the item is not visible */ + if (!IsWindowVisible(descr->self)) return; + if (descr->style & LBS_NOREDRAW) + { + descr->style |= LBS_DISPLAYCHANGED; + return; + } + if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return; + if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return; + if (descr->font) oldFont = SelectObject( hdc, descr->font ); + hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX, + (WPARAM)hdc, (LPARAM)descr->self ); + if (hbrush) oldBrush = SelectObject( hdc, hbrush ); + if (!IsWindowEnabled(descr->self)) + SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); + SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); + LISTBOX_PaintItem( descr, hdc, &rect, index, action, TRUE ); + if (oldFont) SelectObject( hdc, oldFont ); + if (oldBrush) SelectObject( hdc, oldBrush ); + ReleaseDC( descr->self, hdc ); +} + + +/*********************************************************************** + * LISTBOX_DrawFocusRect + */ +static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on ) +{ + HDC hdc; + RECT rect; + HFONT oldFont = 0; + + /* Do not repaint the item if the item is not visible */ + if (!IsWindowVisible(descr->self)) return; + + if (descr->focus_item == -1) return; + if (!descr->caret_on || !descr->in_focus) return; + + if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) != 1) return; + if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) return; + if (descr->font) oldFont = SelectObject( hdc, descr->font ); + if (!IsWindowEnabled(descr->self)) + SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); + SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); + LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, !on ); + if (oldFont) SelectObject( hdc, oldFont ); + ReleaseDC( descr->self, hdc ); +} + + +/*********************************************************************** + * LISTBOX_InitStorage + */ +static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items ) +{ + LB_ITEMDATA *item; + + nb_items += LB_ARRAY_GRANULARITY - 1; + nb_items -= (nb_items % LB_ARRAY_GRANULARITY); + if (descr->items) { + nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item); + item = HeapReAlloc( GetProcessHeap(), 0, descr->items, + nb_items * sizeof(LB_ITEMDATA)); + } + else { + item = HeapAlloc( GetProcessHeap(), 0, + nb_items * sizeof(LB_ITEMDATA)); + } + + if (!item) + { + SEND_NOTIFICATION( descr, LBN_ERRSPACE ); + return LB_ERRSPACE; + } + descr->items = item; + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_SetTabStops + */ +static BOOL LISTBOX_SetTabStops( LB_DESCR *descr, INT count, LPINT tabs ) +{ + INT i; + + if (!(descr->style & LBS_USETABSTOPS)) + { + SetLastError(ERROR_LB_WITHOUT_TABSTOPS); + return FALSE; + } + + HeapFree( GetProcessHeap(), 0, descr->tabs ); + if (!(descr->nb_tabs = count)) + { + descr->tabs = NULL; + return TRUE; + } + if (!(descr->tabs = HeapAlloc( GetProcessHeap(), 0, + descr->nb_tabs * sizeof(INT) ))) + return FALSE; + memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) ); + + /* convert into "dialog units"*/ + for (i = 0; i < descr->nb_tabs; i++) + descr->tabs[i] = MulDiv(descr->tabs[i], descr->avg_char_width, 4); + + return TRUE; +} + + +/*********************************************************************** + * LISTBOX_GetText + */ +static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL unicode ) +{ + DWORD len; + + if ((index < 0) || (index >= descr->nb_items)) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + + if (HAS_STRINGS(descr)) + { + if (!buffer) + return strlenW(descr->items[index].str); + + TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str)); + + __TRY /* hide a Delphi bug that passes a read-only buffer */ + { + strcpyW( buffer, descr->items[index].str ); + len = strlenW(buffer); + } + __EXCEPT_PAGE_FAULT + { + WARN( "got an invalid buffer (Delphi bug?)\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + return LB_ERR; + } + __ENDTRY + } else + { + if (buffer) + *((DWORD *)buffer) = *(DWORD *)&descr->items[index].data; + len = sizeof(DWORD); + } + return len; +} + +static inline INT LISTBOX_lstrcmpiW( LCID lcid, LPCWSTR str1, LPCWSTR str2 ) +{ + INT ret = CompareStringW( lcid, NORM_IGNORECASE, str1, -1, str2, -1 ); + if (ret == CSTR_LESS_THAN) + return -1; + if (ret == CSTR_EQUAL) + return 0; + if (ret == CSTR_GREATER_THAN) + return 1; + return -1; +} + +/*********************************************************************** + * 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 INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact ) +{ + INT index, min, max, res; + + 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 = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str); + else + { + COMPAREITEMSTRUCT cis; + UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); + + cis.CtlType = ODT_LISTBOX; + cis.CtlID = id; + cis.hwndItem = descr->self; + /* note that some application (MetaStock) expects the second item + * to be in the listbox */ + cis.itemID1 = -1; + cis.itemData1 = (ULONG_PTR)str; + cis.itemID2 = index; + cis.itemData2 = descr->items[index].data; + cis.dwLocaleId = descr->locale; + res = SendMessageW( 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 INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str ) +{ + INT min, max, res; + + if (!HAS_STRINGS(descr)) + return LISTBOX_FindStringPos( descr, str, FALSE ); + min = 0; + max = descr->nb_items; + while (min != max) + { + INT index = (min + max) / 2; + LPCWSTR 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 = LISTBOX_lstrcmpiW( descr->locale, str, p ); + } + } + else /* filename */ + { + if (*str == '[') res = 1; + else res = LISTBOX_lstrcmpiW( descr->locale, 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 INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact ) +{ + INT i; + LB_ITEMDATA *item; + + if (start >= descr->nb_items) start = -1; + item = descr->items + start + 1; + if (HAS_STRINGS(descr)) + { + if (!str || ! str[0] ) return LB_ERR; + if (exact) + { + for (i = start + 1; i < descr->nb_items; i++, item++) + if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i; + for (i = 0, item = descr->items; i <= start; i++, item++) + if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i; + } + else + { + /* Special case for drives and directories: ignore prefix */ +#define CHECK_DRIVE(item) \ + if ((item)->str[0] == '[') \ + { \ + if (!strncmpiW( str, (item)->str+1, len )) return i; \ + if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \ + return i; \ + } + + INT len = strlenW(str); + for (i = start + 1; i < descr->nb_items; i++, item++) + { + if (!strncmpiW( str, item->str, len )) return i; + CHECK_DRIVE(item); + } + for (i = 0, item = descr->items; i <= start; i++, item++) + { + if (!strncmpiW( 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( descr, str, TRUE ); + + /* Otherwise use a linear search */ + for (i = start + 1; i < descr->nb_items; i++, item++) + if (item->data == (ULONG_PTR)str) return i; + for (i = 0, item = descr->items; i <= start; i++, item++) + if (item->data == (ULONG_PTR)str) return i; + } + return LB_ERR; +} + + +/*********************************************************************** + * LISTBOX_GetSelCount + */ +static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr ) +{ + INT i, count; + const LB_ITEMDATA *item = descr->items; + + if (!(descr->style & LBS_MULTIPLESEL) || + (descr->style & LBS_NOSEL)) + return LB_ERR; + for (i = count = 0; i < descr->nb_items; i++, item++) + if (item->selected) count++; + return count; +} + + +/*********************************************************************** + * LISTBOX_GetSelItems + */ +static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array ) +{ + INT i, count; + const 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( LB_DESCR *descr, HDC hdc ) +{ + INT i, col_pos = descr->page_size - 1; + RECT rect; + RECT focusRect = {-1, -1, -1, -1}; + HFONT oldFont = 0; + HBRUSH hbrush, oldBrush = 0; + + if (descr->style & LBS_NOREDRAW) return 0; + + SetRect( &rect, 0, 0, descr->width, descr->height ); + if (descr->style & LBS_MULTICOLUMN) + rect.right = rect.left + descr->column_width; + else if (descr->horz_pos) + { + SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL ); + rect.right += descr->horz_pos; + } + + if (descr->font) oldFont = SelectObject( hdc, descr->font ); + hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX, + (WPARAM)hdc, (LPARAM)descr->self ); + if (hbrush) oldBrush = SelectObject( hdc, hbrush ); + if (!IsWindowEnabled(descr->self)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); + + if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on && + (descr->in_focus)) + { + /* Special case for empty listbox: paint focus rect */ + rect.bottom = rect.top + descr->item_height; + ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, + &rect, NULL, 0, NULL ); + LISTBOX_PaintItem( descr, hdc, &rect, descr->focus_item, ODA_FOCUS, FALSE ); + rect.top = rect.bottom; + } + + /* Paint all the item, regarding the selection + Focus state will be painted after */ + + 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; + + /* keep the focus rect, to paint the focus item after */ + if (i == descr->focus_item) + focusRect = rect; + + LISTBOX_PaintItem( descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE ); + rect.top = rect.bottom; + + if ((descr->style & LBS_MULTICOLUMN) && !col_pos) + { + if (!IS_OWNERDRAW(descr)) + { + /* Clear the bottom of the column */ + if (rect.top < descr->height) + { + rect.bottom = descr->height; + ExtTextOutW( 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; + } + } + + /* Paint the focus item now */ + if (focusRect.top != focusRect.bottom && + descr->caret_on && descr->in_focus) + LISTBOX_PaintItem( descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE ); + + if (!IS_OWNERDRAW(descr)) + { + /* Clear the remainder of the client area */ + if (rect.top < descr->height) + { + rect.bottom = descr->height; + ExtTextOutW( 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; + ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, + &rect, NULL, 0, NULL ); + } + } + if (oldFont) SelectObject( hdc, oldFont ); + if (oldBrush) SelectObject( hdc, oldBrush ); + return 0; +} + +static void LISTBOX_NCPaint( LB_DESCR *descr, HRGN region ) +{ + DWORD exstyle = GetWindowLongW( descr->self, GWL_EXSTYLE); + HTHEME theme = GetWindowTheme( descr->self ); + HRGN cliprgn = region; + int cxEdge, cyEdge; + HDC hdc; + RECT r; + + if (!theme || !(exstyle & WS_EX_CLIENTEDGE)) + return; + + cxEdge = GetSystemMetrics(SM_CXEDGE), + cyEdge = GetSystemMetrics(SM_CYEDGE); + + GetWindowRect(descr->self, &r); + + /* New clipping region passed to default proc to exclude border */ + cliprgn = CreateRectRgn(r.left + cxEdge, r.top + cyEdge, + r.right - cxEdge, r.bottom - cyEdge); + if (region != (HRGN)1) + CombineRgn(cliprgn, cliprgn, region, RGN_AND); + OffsetRect(&r, -r.left, -r.top); + +#ifdef __REACTOS__ /* r73789 */ + hdc = GetWindowDC(descr->self); + /* Exclude client part */ + ExcludeClipRect(hdc, + r.left + cxEdge, + r.top + cyEdge, + r.right - cxEdge, + r.bottom -cyEdge); +#else + hdc = GetDCEx(descr->self, region, DCX_WINDOW|DCX_INTERSECTRGN); + OffsetRect(&r, -r.left, -r.top); +#endif + + if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0)) + DrawThemeParentBackground(descr->self, hdc, &r); + DrawThemeBackground (theme, hdc, 0, 0, &r, 0); + ReleaseDC(descr->self, hdc); +} + +/*********************************************************************** + * LISTBOX_InvalidateItems + * + * Invalidate all items from a given item. If the specified item is not + * visible, nothing happens. + */ +static void LISTBOX_InvalidateItems( LB_DESCR *descr, INT index ) +{ + RECT rect; + + if (LISTBOX_GetItemRect( descr, index, &rect ) == 1) + { + if (descr->style & LBS_NOREDRAW) + { + descr->style |= LBS_DISPLAYCHANGED; + return; + } + rect.bottom = descr->height; + InvalidateRect( descr->self, &rect, TRUE ); + if (descr->style & LBS_MULTICOLUMN) + { + /* Repaint the other columns */ + rect.left = rect.right; + rect.right = descr->width; + rect.top = 0; + InvalidateRect( descr->self, &rect, TRUE ); + } + } +} + +static void LISTBOX_InvalidateItemRect( LB_DESCR *descr, INT index ) +{ + RECT rect; + + if (LISTBOX_GetItemRect( descr, index, &rect ) == 1) + InvalidateRect( descr->self, &rect, TRUE ); +} + +/*********************************************************************** + * LISTBOX_GetItemHeight + */ +static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index ) +{ + if (descr->style & LBS_OWNERDRAWVARIABLE && descr->nb_items > 0) + { + if ((index < 0) || (index >= descr->nb_items)) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + return descr->items[index].height; + } + else return descr->item_height; +} + + +/*********************************************************************** + * LISTBOX_SetItemHeight + */ +static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint ) +{ + if (height > MAXBYTE) + return -1; + + if (!height) height = 1; + + if (descr->style & LBS_OWNERDRAWVARIABLE) + { + if ((index < 0) || (index >= descr->nb_items)) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + TRACE("[%p]: item %d height = %d\n", descr->self, index, height ); + descr->items[index].height = height; + LISTBOX_UpdateScroll( descr ); + if (repaint) + LISTBOX_InvalidateItems( descr, index ); + } + else if (height != descr->item_height) + { + TRACE("[%p]: new height = %d\n", descr->self, height ); + descr->item_height = height; + LISTBOX_UpdatePage( descr ); + LISTBOX_UpdateScroll( descr ); + if (repaint) + InvalidateRect( descr->self, 0, TRUE ); + } + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_SetHorizontalPos + */ +static void LISTBOX_SetHorizontalPos( LB_DESCR *descr, INT pos ) +{ + INT 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("[%p]: new horz pos = %d\n", descr->self, pos ); + descr->horz_pos = pos; + LISTBOX_UpdateScroll( descr ); + if (abs(diff) < descr->width) + { + RECT rect; + /* Invalidate the focused item so it will be repainted correctly */ + if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1) + InvalidateRect( descr->self, &rect, TRUE ); + ScrollWindowEx( descr->self, diff, 0, NULL, NULL, 0, NULL, + SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN ); + } + else + InvalidateRect( descr->self, NULL, TRUE ); +} + + +/*********************************************************************** + * LISTBOX_SetHorizontalExtent + */ +static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent ) +{ + if (descr->style & LBS_MULTICOLUMN) + return LB_OKAY; + if (extent == descr->horz_extent) return LB_OKAY; + TRACE("[%p]: new horz extent = %d\n", descr->self, extent ); + descr->horz_extent = extent; + if (descr->style & WS_HSCROLL) { + SCROLLINFO info; + info.cbSize = sizeof(info); + info.nMin = 0; + info.nMax = descr->horz_extent ? descr->horz_extent - 1 : 0; + info.fMask = SIF_RANGE; + if (descr->style & LBS_DISABLENOSCROLL) + info.fMask |= SIF_DISABLENOSCROLL; + SetScrollInfo( descr->self, SB_HORZ, &info, TRUE ); + } + if (descr->horz_pos > extent - descr->width) + LISTBOX_SetHorizontalPos( descr, extent - descr->width ); + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_SetColumnWidth + */ +static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width) +{ + if (width == descr->column_width) return LB_OKAY; + TRACE("[%p]: new column width = %d\n", descr->self, width ); + descr->column_width = width; + LISTBOX_UpdatePage( descr ); + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_SetFont + * + * Returns the item height. + */ +static INT LISTBOX_SetFont( LB_DESCR *descr, HFONT font ) +{ + HDC hdc; + HFONT oldFont = 0; + const char *alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + SIZE sz; + + descr->font = font; + + if (!(hdc = GetDCEx( descr->self, 0, DCX_CACHE ))) + { + ERR("unable to get DC.\n" ); + return 16; + } + if (font) oldFont = SelectObject( hdc, font ); + GetTextExtentPointA( hdc, alphabet, 52, &sz); + if (oldFont) SelectObject( hdc, oldFont ); + ReleaseDC( descr->self, hdc ); + + descr->avg_char_width = (sz.cx / 26 + 1) / 2; + if (!IS_OWNERDRAW(descr)) + LISTBOX_SetItemHeight( descr, 0, sz.cy, FALSE ); + return sz.cy; +} + + +/*********************************************************************** + * LISTBOX_MakeItemVisible + * + * Make sure that a given item is partially or fully visible. + */ +static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully ) +{ + INT top; + + TRACE("current top item %d, index %d, fully %d\n", descr->top_item, index, fully); + + if (index <= descr->top_item) top = index; + else if (descr->style & LBS_MULTICOLUMN) + { + INT 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) + { + INT 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( descr, top, TRUE ); +} + +/*********************************************************************** + * LISTBOX_SetCaretIndex + * + * NOTES + * index must be between 0 and descr->nb_items-1, or LB_ERR is returned. + * + */ +static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible ) +{ + INT oldfocus = descr->focus_item; + + TRACE("old focus %d, index %d\n", oldfocus, index); + + if (descr->style & LBS_NOSEL) return LB_ERR; + if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; + if (index == oldfocus) return LB_OKAY; + + LISTBOX_DrawFocusRect( descr, FALSE ); + descr->focus_item = index; + + LISTBOX_MakeItemVisible( descr, index, fully_visible ); + LISTBOX_DrawFocusRect( descr, TRUE ); + + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_SelectItemRange + * + * Select a range of items. Should only be used on a MULTIPLESEL listbox. + */ +static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first, + INT last, BOOL on ) +{ + INT i; + + /* A few sanity checks */ + + if (descr->style & LBS_NOSEL) return LB_ERR; + if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR; + + if (!descr->nb_items) return LB_OKAY; + + if (last == -1 || last >= descr->nb_items) last = descr->nb_items - 1; + if (first < 0) first = 0; + if (last < first) return LB_OKAY; + + if (on) /* Turn selection on */ + { + for (i = first; i <= last; i++) + { + if (descr->items[i].selected) continue; + descr->items[i].selected = TRUE; + LISTBOX_InvalidateItemRect(descr, i); + } + } + else /* Turn selection off */ + { + for (i = first; i <= last; i++) + { + if (!descr->items[i].selected) continue; + descr->items[i].selected = FALSE; + LISTBOX_InvalidateItemRect(descr, i); + } + } + return LB_OKAY; +} + +/*********************************************************************** + * LISTBOX_SetSelection + */ +static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index, + BOOL on, BOOL send_notify ) +{ + TRACE( "cur_sel=%d index=%d notify=%s\n", + descr->selected_item, index, send_notify ? "YES" : "NO" ); + + if (descr->style & LBS_NOSEL) + { + descr->selected_item = index; + return LB_ERR; + } + if ((index < -1) || (index >= descr->nb_items)) return LB_ERR; + if (descr->style & LBS_MULTIPLESEL) + { + if (index == -1) /* Select all items */ + return LISTBOX_SelectItemRange( descr, 0, descr->nb_items, on ); + else /* Only one item */ + return LISTBOX_SelectItemRange( descr, index, index, on ); + } + else + { + INT 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; + if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT ); + descr->selected_item = index; + if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT ); + if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr, + (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL ); + else + if( descr->lphc ) /* set selection change flag for parent combo */ + descr->lphc->wState |= CBF_SELCHANGE; + } + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_MoveCaret + * + * Change the caret position and extend the selection to the new caret. + */ +static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible ) +{ + TRACE("old focus %d, index %d\n", descr->focus_item, index); + + if ((index < 0) || (index >= descr->nb_items)) + return; + + /* Important, repaint needs to be done in this order if + you want to mimic Windows behavior: + 1. Remove the focus and paint the item + 2. Remove the selection and paint the item(s) + 3. Set the selection and repaint the item(s) + 4. Set the focus to 'index' and repaint the item */ + + /* 1. remove the focus and repaint the item */ + LISTBOX_DrawFocusRect( descr, FALSE ); + + /* 2. then turn off the previous selection */ + /* 3. repaint the new selected item */ + if (descr->style & LBS_EXTENDEDSEL) + { + if (descr->anchor_item != -1) + { + INT first = min( index, descr->anchor_item ); + INT last = max( index, descr->anchor_item ); + if (first > 0) + LISTBOX_SelectItemRange( descr, 0, first - 1, FALSE ); + LISTBOX_SelectItemRange( descr, last + 1, -1, FALSE ); + LISTBOX_SelectItemRange( descr, first, last, TRUE ); + } + } + else if (!(descr->style & LBS_MULTIPLESEL)) + { + /* Set selection to new caret item */ + LISTBOX_SetSelection( descr, index, TRUE, FALSE ); + } + + /* 4. repaint the new item with the focus */ + descr->focus_item = index; + LISTBOX_MakeItemVisible( descr, index, fully_visible ); + LISTBOX_DrawFocusRect( descr, TRUE ); +} + + +/*********************************************************************** + * LISTBOX_InsertItem + */ +static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index, + LPWSTR str, ULONG_PTR data ) +{ + LB_ITEMDATA *item; + INT max_items; + INT oldfocus = descr->focus_item; + + 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( GetProcessHeap(), 0, descr->items ) / sizeof(*item); + if (descr->nb_items == max_items) + { + /* We need to grow the array */ + max_items += LB_ARRAY_GRANULARITY; + if (descr->items) + item = HeapReAlloc( GetProcessHeap(), 0, descr->items, + max_items * sizeof(LB_ITEMDATA) ); + else + item = HeapAlloc( GetProcessHeap(), 0, + max_items * sizeof(LB_ITEMDATA) ); + if (!item) + { + SEND_NOTIFICATION( 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 = HAS_STRINGS(descr) ? 0 : data; + item->height = 0; + item->selected = FALSE; + descr->nb_items++; + + /* Get item height */ + + if (descr->style & LBS_OWNERDRAWVARIABLE) + { + MEASUREITEMSTRUCT mis; + UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); + + mis.CtlType = ODT_LISTBOX; + mis.CtlID = id; + mis.itemID = index; + mis.itemData = data; + mis.itemHeight = descr->item_height; + SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); + item->height = mis.itemHeight ? mis.itemHeight : 1; + TRACE("[%p]: measure item %d (%s) = %d\n", + descr->self, index, str ? debugstr_w(str) : "", item->height ); + } + + /* Repaint the items */ + + LISTBOX_UpdateScroll( descr ); + LISTBOX_InvalidateItems( descr, index ); + + /* Move selection and focused item */ + /* If listbox was empty, set focus to the first item */ + if (descr->nb_items == 1) + LISTBOX_SetCaretIndex( descr, 0, FALSE ); + /* single select don't change selection index in win31 */ + else if ((ISWIN31) && !(IS_MULTISELECT(descr))) + { + descr->selected_item++; + LISTBOX_SetSelection( descr, descr->selected_item-1, TRUE, FALSE ); + } + else + { + if (index <= descr->selected_item) + { + descr->selected_item++; + descr->focus_item = oldfocus; /* focus not changed */ + } + } + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_InsertString + */ +static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str ) +{ + LPWSTR new_str = NULL; + LRESULT ret; + + if (HAS_STRINGS(descr)) + { + static const WCHAR empty_stringW[] = { 0 }; + if (!str) str = empty_stringW; + if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) ))) + { + SEND_NOTIFICATION( descr, LBN_ERRSPACE ); + return LB_ERRSPACE; + } + strcpyW(new_str, str); + } + + if (index == -1) index = descr->nb_items; + if ((ret = LISTBOX_InsertItem( descr, index, new_str, (ULONG_PTR)str )) != 0) + { + HeapFree( GetProcessHeap(), 0, new_str ); + return ret; + } + + TRACE("[%p]: added item %d %s\n", + descr->self, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" ); + return index; +} + + +/*********************************************************************** + * LISTBOX_DeleteItem + * + * Delete the content of an item. 'index' must be a valid index. + */ +static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index ) +{ + /* save the item data before it gets freed by LB_RESETCONTENT */ + ULONG_PTR item_data = descr->items[index].data; + LPWSTR item_str = descr->items[index].str; + + if (!descr->nb_items) + SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 ); + + /* 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) || item_data) + { + DELETEITEMSTRUCT dis; + UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); + + dis.CtlType = ODT_LISTBOX; + dis.CtlID = id; + dis.itemID = index; + dis.hwndItem = descr->self; + dis.itemData = item_data; + SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis ); + } + if (HAS_STRINGS(descr)) + HeapFree( GetProcessHeap(), 0, item_str ); +} + + +/*********************************************************************** + * LISTBOX_RemoveItem + * + * Remove an item from the listbox and delete its content. + */ +static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index ) +{ + LB_ITEMDATA *item; + INT max_items; + + if ((index < 0) || (index >= descr->nb_items)) return LB_ERR; + + /* We need to invalidate the original rect instead of the updated one. */ + LISTBOX_InvalidateItems( descr, index ); + + descr->nb_items--; + LISTBOX_DeleteItem( descr, index ); + + if (!descr->nb_items) return LB_OKAY; + + /* Remove the item */ + + item = &descr->items[index]; + if (index < descr->nb_items) + RtlMoveMemory( item, item + 1, + (descr->nb_items - index) * sizeof(LB_ITEMDATA) ); + if (descr->anchor_item == descr->nb_items) descr->anchor_item--; + + /* Shrink the item array if possible */ + + max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA); + if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY) + { + max_items -= LB_ARRAY_GRANULARITY; + item = HeapReAlloc( GetProcessHeap(), 0, descr->items, + max_items * sizeof(LB_ITEMDATA) ); + if (item) descr->items = item; + } + /* Repaint the items */ + + LISTBOX_UpdateScroll( descr ); + /* if we removed the scrollbar, reset the top of the list + (correct for owner-drawn ???) */ + if (descr->nb_items == descr->page_size) + LISTBOX_SetTopItem( descr, 0, TRUE ); + + /* Move selection and focused item */ + if (!IS_MULTISELECT(descr)) + { + if (index == descr->selected_item) + descr->selected_item = -1; + else if (index < descr->selected_item) + { + descr->selected_item--; + if (ISWIN31) /* win 31 do not change the selected item number */ + LISTBOX_SetSelection( descr, descr->selected_item + 1, TRUE, FALSE); + } + } + + if (descr->focus_item >= descr->nb_items) + { + descr->focus_item = descr->nb_items - 1; + if (descr->focus_item < 0) descr->focus_item = 0; + } + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_ResetContent + */ +static void LISTBOX_ResetContent( LB_DESCR *descr ) +{ + INT i; + + for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i); + HeapFree( GetProcessHeap(), 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_SetCount + */ +static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count ) +{ + LRESULT ret; + + if (HAS_STRINGS(descr)) + { + SetLastError(ERROR_SETCOUNT_ON_BAD_LB); + return LB_ERR; + } + + /* FIXME: this is far from optimal... */ + if (count > descr->nb_items) + { + while (count > descr->nb_items) + if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0) + return ret; + } + else if (count < descr->nb_items) + { + while (count < descr->nb_items) + if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0) + return ret; + } + + InvalidateRect( descr->self, NULL, TRUE ); + return LB_OKAY; +} + + +/*********************************************************************** + * LISTBOX_Directory + */ +static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib, + LPCWSTR filespec, BOOL long_names ) +{ + HANDLE handle; + LRESULT ret = LB_OKAY; + WIN32_FIND_DATAW entry; + int pos; + LRESULT maxinsert = LB_ERR; + + /* don't scan directory if we just want drives exclusively */ + if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) { + /* scan directory */ + if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE) + { + int le = GetLastError(); + if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR; + } + else + { + do + { + WCHAR buffer[270]; + if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + static const WCHAR bracketW[] = { ']',0 }; + static const WCHAR dotW[] = { '.',0 }; + if (!(attrib & DDL_DIRECTORY) || + !strcmpW( entry.cFileName, dotW )) continue; + buffer[0] = '['; + if (!long_names && entry.cAlternateFileName[0]) + strcpyW( buffer + 1, entry.cAlternateFileName ); + else + strcpyW( buffer + 1, entry.cFileName ); + strcatW(buffer, bracketW); + } + 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 && entry.cAlternateFileName[0]) + strcpyW( buffer, entry.cAlternateFileName ); + else + strcpyW( buffer, entry.cFileName ); + } + if (!long_names) CharLowerW( buffer ); + pos = LISTBOX_FindFileStrPos( descr, buffer ); + if ((ret = LISTBOX_InsertString( descr, pos, buffer )) < 0) + break; + if (ret <= maxinsert) maxinsert++; else maxinsert = ret; + } while (FindNextFileW( handle, &entry )); + FindClose( handle ); + } + } + if (ret >= 0) + { + ret = maxinsert; + + /* scan drives */ + if (attrib & DDL_DRIVES) + { + WCHAR buffer[] = {'[','-','a','-',']',0}; + WCHAR root[] = {'A',':','\\',0}; + int drive; + for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++) + { + if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue; + if ((ret = LISTBOX_InsertString( descr, -1, buffer )) < 0) + break; + } + } + } + return ret; +} + + +/*********************************************************************** + * LISTBOX_HandleVScroll + */ +static LRESULT LISTBOX_HandleVScroll( LB_DESCR *descr, WORD scrollReq, WORD pos ) +{ + SCROLLINFO info; + + if (descr->style & LBS_MULTICOLUMN) return 0; + switch(scrollReq) + { + case SB_LINEUP: + LISTBOX_SetTopItem( descr, descr->top_item - 1, TRUE ); + break; + case SB_LINEDOWN: + LISTBOX_SetTopItem( descr, descr->top_item + 1, TRUE ); + break; + case SB_PAGEUP: + LISTBOX_SetTopItem( descr, descr->top_item - + LISTBOX_GetCurrentPageSize( descr ), TRUE ); + break; + case SB_PAGEDOWN: + LISTBOX_SetTopItem( descr, descr->top_item + + LISTBOX_GetCurrentPageSize( descr ), TRUE ); + break; + case SB_THUMBPOSITION: + LISTBOX_SetTopItem( descr, pos, TRUE ); + break; + case SB_THUMBTRACK: + info.cbSize = sizeof(info); + info.fMask = SIF_TRACKPOS; + GetScrollInfo( descr->self, SB_VERT, &info ); + LISTBOX_SetTopItem( descr, info.nTrackPos, TRUE ); + break; + case SB_TOP: + LISTBOX_SetTopItem( descr, 0, TRUE ); + break; + case SB_BOTTOM: + LISTBOX_SetTopItem( descr, descr->nb_items, TRUE ); + break; + } + return 0; +} + + +/*********************************************************************** + * LISTBOX_HandleHScroll + */ +static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos ) +{ + SCROLLINFO info; + INT page; + + if (descr->style & LBS_MULTICOLUMN) + { + switch(scrollReq) + { + case SB_LINELEFT: + LISTBOX_SetTopItem( descr, descr->top_item-descr->page_size, + TRUE ); + break; + case SB_LINERIGHT: + LISTBOX_SetTopItem( 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( 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( descr, + descr->top_item + page * descr->page_size, TRUE ); + break; + case SB_THUMBPOSITION: + LISTBOX_SetTopItem( descr, pos*descr->page_size, TRUE ); + break; + case SB_THUMBTRACK: + info.cbSize = sizeof(info); + info.fMask = SIF_TRACKPOS; + GetScrollInfo( descr->self, SB_VERT, &info ); + LISTBOX_SetTopItem( descr, info.nTrackPos*descr->page_size, + TRUE ); + break; + case SB_LEFT: + LISTBOX_SetTopItem( descr, 0, TRUE ); + break; + case SB_RIGHT: + LISTBOX_SetTopItem( descr, descr->nb_items, TRUE ); + break; + } + } + else if (descr->horz_extent) + { + switch(scrollReq) + { + case SB_LINELEFT: + LISTBOX_SetHorizontalPos( descr, descr->horz_pos - 1 ); + break; + case SB_LINERIGHT: + LISTBOX_SetHorizontalPos( descr, descr->horz_pos + 1 ); + break; + case SB_PAGELEFT: + LISTBOX_SetHorizontalPos( descr, + descr->horz_pos - descr->width ); + break; + case SB_PAGERIGHT: + LISTBOX_SetHorizontalPos( descr, + descr->horz_pos + descr->width ); + break; + case SB_THUMBPOSITION: + LISTBOX_SetHorizontalPos( descr, pos ); + break; + case SB_THUMBTRACK: + info.cbSize = sizeof(info); + info.fMask = SIF_TRACKPOS; + GetScrollInfo( descr->self, SB_HORZ, &info ); + LISTBOX_SetHorizontalPos( descr, info.nTrackPos ); + break; + case SB_LEFT: + LISTBOX_SetHorizontalPos( descr, 0 ); + break; + case SB_RIGHT: + LISTBOX_SetHorizontalPos( descr, + descr->horz_extent - descr->width ); + break; + } + } + return 0; +} + +static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta ) +{ + UINT pulScrollLines = 3; + + SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); + + /* if scrolling changes direction, ignore left overs */ + if ((delta < 0 && descr->wheel_remain < 0) || + (delta > 0 && descr->wheel_remain > 0)) + descr->wheel_remain += delta; + else + descr->wheel_remain = delta; + + if (descr->wheel_remain && pulScrollLines) + { + int cLineScroll; + pulScrollLines = min((UINT) descr->page_size, pulScrollLines); + cLineScroll = pulScrollLines * (float)descr->wheel_remain / WHEEL_DELTA; + descr->wheel_remain -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines; + LISTBOX_SetTopItem( descr, descr->top_item - cLineScroll, TRUE ); + } + return 0; +} + +/*********************************************************************** + * LISTBOX_HandleLButtonDown + */ +static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, INT y ) +{ + INT index = LISTBOX_GetItemFromPoint( descr, x, y ); + + TRACE("[%p]: lbuttondown %d,%d item %d, focus item %d\n", + descr->self, x, y, index, descr->focus_item); + + if (!descr->caret_on && (descr->in_focus)) return 0; + + if (!descr->in_focus) + { + if( !descr->lphc ) SetFocus( descr->self ); + else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self ); + } + + if (index == -1) return 0; + + if (!descr->lphc) + { + if (descr->style & LBS_NOTIFY ) + SendMessageW( descr->owner, WM_LBTRACKPOINT, index, + MAKELPARAM( x, y ) ); + } + + descr->captured = TRUE; + SetCapture( descr->self ); + + if (descr->style & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL)) + { + /* we should perhaps make sure that all items are deselected + FIXME: needed for !LBS_EXTENDEDSEL, too ? + if (!(keys & (MK_SHIFT|MK_CONTROL))) + LISTBOX_SetSelection( descr, -1, FALSE, FALSE); + */ + + if (!(keys & MK_SHIFT)) descr->anchor_item = index; + if (keys & MK_CONTROL) + { + LISTBOX_SetCaretIndex( descr, index, FALSE ); + LISTBOX_SetSelection( descr, index, + !descr->items[index].selected, + (descr->style & LBS_NOTIFY) != 0); + } + else + { + LISTBOX_MoveCaret( descr, index, FALSE ); + + if (descr->style & LBS_EXTENDEDSEL) + { + LISTBOX_SetSelection( descr, index, + descr->items[index].selected, + (descr->style & LBS_NOTIFY) != 0 ); + } + else + { + LISTBOX_SetSelection( descr, index, + !descr->items[index].selected, + (descr->style & LBS_NOTIFY) != 0 ); + } + } + } + else + { + descr->anchor_item = index; + LISTBOX_MoveCaret( descr, index, FALSE ); + LISTBOX_SetSelection( descr, index, + TRUE, (descr->style & LBS_NOTIFY) != 0 ); + } + + if (!descr->lphc) + { + if (GetWindowLongW( descr->self, GWL_EXSTYLE ) & WS_EX_DRAGDETECT) + { + POINT pt; + + pt.x = x; + pt.y = y; + + if (DragDetect( descr->self, pt )) + SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 ); + } + } + return 0; +} + + +/************************************************************************* + * LISTBOX_HandleLButtonDownCombo [Internal] + * + * Process LButtonDown message for the ComboListBox + * + * PARAMS + * pWnd [I] The windows internal structure + * pDescr [I] The ListBox internal structure + * keys [I] Key Flag (WM_LBUTTONDOWN doc for more info) + * x [I] X Mouse Coordinate + * y [I] Y Mouse Coordinate + * + * RETURNS + * 0 since we are processing the WM_LBUTTONDOWN Message + * + * NOTES + * This function is only to be used when a ListBox is a ComboListBox + */ + +static LRESULT LISTBOX_HandleLButtonDownCombo( LB_DESCR *descr, UINT msg, DWORD keys, INT x, INT y) +{ + RECT clientRect, screenRect; + POINT mousePos; + + mousePos.x = x; + mousePos.y = y; + + GetClientRect(descr->self, &clientRect); + + if(PtInRect(&clientRect, mousePos)) + { + /* MousePos is in client, resume normal processing */ + if (msg == WM_LBUTTONDOWN) + { + descr->lphc->droppedIndex = descr->nb_items ? descr->selected_item : -1; + return LISTBOX_HandleLButtonDown( descr, keys, x, y); + } + else if (descr->style & LBS_NOTIFY) + SEND_NOTIFICATION( descr, LBN_DBLCLK ); + } + else + { + POINT screenMousePos; + HWND hWndOldCapture; + + /* Check the Non-Client Area */ + screenMousePos = mousePos; + hWndOldCapture = GetCapture(); + ReleaseCapture(); + GetWindowRect(descr->self, &screenRect); + ClientToScreen(descr->self, &screenMousePos); + + if(!PtInRect(&screenRect, screenMousePos)) + { + LISTBOX_SetCaretIndex( descr, descr->lphc->droppedIndex, FALSE ); + LISTBOX_SetSelection( descr, descr->lphc->droppedIndex, FALSE, FALSE ); + COMBO_FlipListbox( descr->lphc, FALSE, FALSE ); + } + else + { + /* Check to see the NC is a scrollbar */ + INT nHitTestType=0; + LONG style = GetWindowLongW( descr->self, GWL_STYLE ); + /* Check Vertical scroll bar */ + if (style & WS_VSCROLL) + { + clientRect.right += GetSystemMetrics(SM_CXVSCROLL); + if (PtInRect( &clientRect, mousePos )) + nHitTestType = HTVSCROLL; + } + /* Check horizontal scroll bar */ + if (style & WS_HSCROLL) + { + clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL); + if (PtInRect( &clientRect, mousePos )) + nHitTestType = HTHSCROLL; + } + /* Windows sends this message when a scrollbar is clicked + */ + + if(nHitTestType != 0) + { + SendMessageW(descr->self, WM_NCLBUTTONDOWN, nHitTestType, + MAKELONG(screenMousePos.x, screenMousePos.y)); + } + /* Resume the Capture after scrolling is complete + */ + if(hWndOldCapture != 0) + SetCapture(hWndOldCapture); + } + } + return 0; +} + +/*********************************************************************** + * LISTBOX_HandleLButtonUp + */ +static LRESULT LISTBOX_HandleLButtonUp( LB_DESCR *descr ) +{ + if (LISTBOX_Timer != LB_TIMER_NONE) + KillSystemTimer( descr->self, LB_TIMER_ID ); + LISTBOX_Timer = LB_TIMER_NONE; + if (descr->captured) + { + descr->captured = FALSE; + if (GetCapture() == descr->self) ReleaseCapture(); + if ((descr->style & LBS_NOTIFY) && descr->nb_items) + SEND_NOTIFICATION( descr, LBN_SELCHANGE ); + } + return 0; +} + + +/*********************************************************************** + * LISTBOX_HandleTimer + * + * Handle scrolling upon a timer event. + * Return TRUE if scrolling should continue. + */ +static LRESULT LISTBOX_HandleTimer( LB_DESCR *descr, INT 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( 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( descr, index, FALSE ); + return TRUE; +} + + +/*********************************************************************** + * LISTBOX_HandleSystemTimer + * + * WM_SYSTIMER handler. + */ +static LRESULT LISTBOX_HandleSystemTimer( LB_DESCR *descr ) +{ + if (!LISTBOX_HandleTimer( descr, descr->focus_item, LISTBOX_Timer )) + { + KillSystemTimer( descr->self, LB_TIMER_ID ); + LISTBOX_Timer = LB_TIMER_NONE; + } + return 0; +} + + +/*********************************************************************** + * LISTBOX_HandleMouseMove + * + * WM_MOUSEMOVE handler. + */ +static void LISTBOX_HandleMouseMove( LB_DESCR *descr, + INT x, INT y ) +{ + INT index; + TIMER_DIRECTION dir = LB_TIMER_NONE; + + if (!descr->captured) return; + + 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 + { + if (y < 0) dir = LB_TIMER_UP; /* above */ + else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */ + } + + index = LISTBOX_GetItemFromPoint( descr, x, y ); + if (index == -1) index = descr->focus_item; + if (!LISTBOX_HandleTimer( descr, index, dir )) dir = LB_TIMER_NONE; + + /* Start/stop the system timer */ + + if (dir != LB_TIMER_NONE) + SetSystemTimer( descr->self, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL); + else if (LISTBOX_Timer != LB_TIMER_NONE) + KillSystemTimer( descr->self, LB_TIMER_ID ); + LISTBOX_Timer = dir; +} + + +/*********************************************************************** + * LISTBOX_HandleKeyDown + */ +static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key ) +{ + INT caret = -1; + BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */ + if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item)) + bForceSelection = FALSE; /* only for single select list */ + + if (descr->style & LBS_WANTKEYBOARDINPUT) + { + caret = SendMessageW( descr->owner, WM_VKEYTOITEM, + MAKEWPARAM(LOWORD(key), descr->focus_item), + (LPARAM)descr->self ); + if (caret == -2) return 0; + } + if (caret == -1) switch(key) + { + case VK_LEFT: + if (descr->style & LBS_MULTICOLUMN) + { + bForceSelection = FALSE; + 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) + { + bForceSelection = FALSE; + 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) + { + INT 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(descr) + 1; + if (caret < 0) caret = 0; + break; + case VK_NEXT: + if (descr->style & LBS_MULTICOLUMN) + { + INT 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(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( descr, descr->focus_item, + !descr->items[descr->focus_item].selected, + (descr->style & LBS_NOTIFY) != 0 ); + } + break; + default: + bForceSelection = FALSE; + } + if (bForceSelection) /* focused item is used instead of key */ + caret = descr->focus_item; + if (caret >= 0) + { + if (((descr->style & LBS_EXTENDEDSEL) && + !(GetKeyState( VK_SHIFT ) & 0x8000)) || + !IS_MULTISELECT(descr)) + descr->anchor_item = caret; + LISTBOX_MoveCaret( descr, caret, TRUE ); + + if (descr->style & LBS_MULTIPLESEL) + descr->selected_item = caret; + else + LISTBOX_SetSelection( descr, caret, TRUE, FALSE); + if (descr->style & LBS_NOTIFY) + { + if (descr->lphc && IsWindowVisible( descr->self )) + { + /* make sure that combo parent doesn't hide us */ + descr->lphc->wState |= CBF_NOROLLUP; + } + if (descr->nb_items) SEND_NOTIFICATION( descr, LBN_SELCHANGE ); + } + } + return 0; +} + + +/*********************************************************************** + * LISTBOX_HandleChar + */ +static LRESULT LISTBOX_HandleChar( LB_DESCR *descr, WCHAR charW ) +{ + INT caret = -1; + WCHAR str[2]; + + str[0] = charW; + str[1] = '\0'; + + if (descr->style & LBS_WANTKEYBOARDINPUT) + { + caret = SendMessageW( descr->owner, WM_CHARTOITEM, + MAKEWPARAM(charW, descr->focus_item), + (LPARAM)descr->self ); + if (caret == -2) return 0; + } + if (caret == -1) + caret = LISTBOX_FindString( descr, descr->focus_item, str, FALSE); + if (caret != -1) + { + if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1) + LISTBOX_SetSelection( descr, caret, TRUE, FALSE); + LISTBOX_MoveCaret( descr, caret, TRUE ); + if ((descr->style & LBS_NOTIFY) && descr->nb_items) + SEND_NOTIFICATION( descr, LBN_SELCHANGE ); + } + return 0; +} + + +/*********************************************************************** + * LISTBOX_Create + */ +static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc ) +{ + LB_DESCR *descr; + MEASUREITEMSTRUCT mis; + RECT rect; + + if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) ))) + return FALSE; + + GetClientRect( hwnd, &rect ); + descr->self = hwnd; + descr->owner = GetParent( descr->self ); + descr->style = GetWindowLongW( descr->self, GWL_STYLE ); + 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 = 0; + descr->horz_pos = 0; + descr->nb_tabs = 0; + descr->tabs = NULL; + descr->wheel_remain = 0; + descr->caret_on = !lphc; + if (descr->style & LBS_NOSEL) descr->caret_on = FALSE; + descr->in_focus = FALSE; + descr->captured = FALSE; + descr->font = 0; + descr->locale = GetUserDefaultLCID(); + descr->lphc = lphc; + + if( lphc ) + { + TRACE("[%p]: resetting owner %p -> %p\n", descr->self, descr->owner, lphc->self ); + descr->owner = lphc->self; + } + + SetWindowLongPtrW( descr->self, 0, (LONG_PTR)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( 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->fixedOwnerDrawHeight; + } + else + { + UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID ); + mis.CtlType = ODT_LISTBOX; + mis.CtlID = id; + mis.itemID = -1; + mis.itemWidth = 0; + mis.itemData = 0; + mis.itemHeight = descr->item_height; + SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis ); + descr->item_height = mis.itemHeight ? mis.itemHeight : 1; + } + } + + OpenThemeData( descr->self, WC_LISTBOXW ); + + TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height); + return TRUE; +} + + +/*********************************************************************** + * LISTBOX_Destroy + */ +static BOOL LISTBOX_Destroy( LB_DESCR *descr ) +{ + HTHEME theme = GetWindowTheme( descr->self ); + CloseThemeData( theme ); + LISTBOX_ResetContent( descr ); + SetWindowLongPtrW( descr->self, 0, 0 ); + HeapFree( GetProcessHeap(), 0, descr ); + return TRUE; +} + + +/*********************************************************************** + * ListBoxWndProc_common + */ +static LRESULT CALLBACK LISTBOX_WindowProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + LB_DESCR *descr = (LB_DESCR *)GetWindowLongPtrW( hwnd, 0 ); + HEADCOMBO *lphc = NULL; + HTHEME theme; + LRESULT ret; + + if (!descr) + { + if (!IsWindow(hwnd)) return 0; + + if (msg == WM_CREATE) + { + CREATESTRUCTW *lpcs = (CREATESTRUCTW *)lParam; + if (lpcs->style & LBS_COMBOBOX) lphc = lpcs->lpCreateParams; + if (!LISTBOX_Create( hwnd, lphc )) return -1; + TRACE("creating hwnd %p descr %p\n", hwnd, (void *)GetWindowLongPtrW( hwnd, 0 ) ); + return 0; + } + /* Ignore all other messages before we get a WM_CREATE */ + return DefWindowProcW( hwnd, msg, wParam, lParam ); + } + if (descr->style & LBS_COMBOBOX) lphc = descr->lphc; + + TRACE("[%p]: msg %#x wp %08lx lp %08lx\n", descr->self, msg, wParam, lParam ); + + switch(msg) + { + case LB_RESETCONTENT: + LISTBOX_ResetContent( descr ); + LISTBOX_UpdateScroll( descr ); + InvalidateRect( descr->self, NULL, TRUE ); + return 0; + + case LB_ADDSTRING: + { + const WCHAR *textW = (const WCHAR *)lParam; + INT index = LISTBOX_FindStringPos( descr, textW, FALSE ); + return LISTBOX_InsertString( descr, index, textW ); + } + + case LB_INSERTSTRING: + return LISTBOX_InsertString( descr, wParam, (const WCHAR *)lParam ); + + case LB_ADDFILE: + { + const WCHAR *textW = (const WCHAR *)lParam; + INT index = LISTBOX_FindFileStrPos( descr, textW ); + return LISTBOX_InsertString( descr, index, textW ); + } + + case LB_DELETESTRING: + if (LISTBOX_RemoveItem( descr, wParam) != LB_ERR) + return descr->nb_items; + else + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + + case LB_GETITEMDATA: + if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + return descr->items[wParam].data; + + case LB_SETITEMDATA: + if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + descr->items[wParam].data = lParam; + /* undocumented: returns TRUE, not LB_OKAY (0) */ + return TRUE; + + case LB_GETCOUNT: + return descr->nb_items; + + case LB_GETTEXT: + return LISTBOX_GetText( descr, wParam, (LPWSTR)lParam, TRUE ); + + case LB_GETTEXTLEN: + if ((INT)wParam >= descr->nb_items || (INT)wParam < 0) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + if (!HAS_STRINGS(descr)) return sizeof(DWORD); + return strlenW( descr->items[wParam].str ); + + case LB_GETCURSEL: + if (descr->nb_items == 0) + return LB_ERR; + if (!IS_MULTISELECT(descr)) + return descr->selected_item; + if (descr->selected_item != -1) + return descr->selected_item; + return descr->focus_item; + /* otherwise, if the user tries to move the selection with the */ + /* arrow keys, we will give the application something to choke on */ + case LB_GETTOPINDEX: + return descr->top_item; + + case LB_GETITEMHEIGHT: + return LISTBOX_GetItemHeight( descr, wParam ); + + case LB_SETITEMHEIGHT: + return LISTBOX_SetItemHeight( descr, wParam, lParam, TRUE ); + + case LB_ITEMFROMPOINT: + { + POINT pt; + RECT rect; + int index; + BOOL hit = TRUE; + + /* The hiword of the return value is not a client area + hittest as suggested by MSDN, but rather a hittest on + the returned listbox item. */ + + if(descr->nb_items == 0) + return 0x1ffff; /* win9x returns 0x10000, we copy winnt */ + + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + + SetRect(&rect, 0, 0, descr->width, descr->height); + + if(!PtInRect(&rect, pt)) + { + pt.x = min(pt.x, rect.right - 1); + pt.x = max(pt.x, 0); + pt.y = min(pt.y, rect.bottom - 1); + pt.y = max(pt.y, 0); + hit = FALSE; + } + + index = LISTBOX_GetItemFromPoint(descr, pt.x, pt.y); + + if(index == -1) + { + index = descr->nb_items - 1; + hit = FALSE; + } + return MAKELONG(index, hit ? 0 : 1); + } + + case LB_SETCARETINDEX: + if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR; + if (LISTBOX_SetCaretIndex( descr, wParam, !lParam ) == LB_ERR) + return LB_ERR; + else if (ISWIN31) + return wParam; + else + return LB_OKAY; + + case LB_GETCARETINDEX: + return descr->focus_item; + + case LB_SETTOPINDEX: + return LISTBOX_SetTopItem( descr, wParam, TRUE ); + + case LB_SETCOLUMNWIDTH: + return LISTBOX_SetColumnWidth( descr, wParam ); + + case LB_GETITEMRECT: + return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam ); + + case LB_FINDSTRING: + return LISTBOX_FindString( descr, wParam, (const WCHAR *)lParam, FALSE ); + + case LB_FINDSTRINGEXACT: + return LISTBOX_FindString( descr, wParam, (const WCHAR *)lParam, TRUE ); + + case LB_SELECTSTRING: + { + const WCHAR *textW = (const WCHAR *)lParam; + INT index; + + if (HAS_STRINGS(descr)) + TRACE("LB_SELECTSTRING: %s\n", debugstr_w(textW)); + + index = LISTBOX_FindString( descr, wParam, textW, FALSE ); + if (index != LB_ERR) + { + LISTBOX_MoveCaret( descr, index, TRUE ); + LISTBOX_SetSelection( descr, index, TRUE, FALSE ); + } + return index; + } + + case LB_GETSEL: + if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items)) + return LB_ERR; + return descr->items[wParam].selected; + + case LB_SETSEL: + return LISTBOX_SetSelection( descr, lParam, wParam, FALSE ); + + case LB_SETCURSEL: + if (IS_MULTISELECT(descr)) return LB_ERR; + LISTBOX_SetCaretIndex( descr, wParam, TRUE ); + ret = LISTBOX_SetSelection( descr, wParam, TRUE, FALSE ); + if (ret != LB_ERR) ret = descr->selected_item; + return ret; + + case LB_GETSELCOUNT: + return LISTBOX_GetSelCount( descr ); + + case LB_GETSELITEMS: + return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam ); + + case LB_SELITEMRANGE: + if (LOWORD(lParam) <= HIWORD(lParam)) + return LISTBOX_SelectItemRange( descr, LOWORD(lParam), + HIWORD(lParam), wParam ); + else + return LISTBOX_SelectItemRange( descr, HIWORD(lParam), + LOWORD(lParam), wParam ); + + case LB_SELITEMRANGEEX: + if ((INT)lParam >= (INT)wParam) + return LISTBOX_SelectItemRange( descr, wParam, lParam, TRUE ); + else + return LISTBOX_SelectItemRange( descr, lParam, wParam, FALSE); + + case LB_GETHORIZONTALEXTENT: + return descr->horz_extent; + + case LB_SETHORIZONTALEXTENT: + return LISTBOX_SetHorizontalExtent( descr, wParam ); + + case LB_GETANCHORINDEX: + return descr->anchor_item; + + case LB_SETANCHORINDEX: + if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items)) + { + SetLastError(ERROR_INVALID_INDEX); + return LB_ERR; + } + descr->anchor_item = (INT)wParam; + return LB_OKAY; + + case LB_DIR: + return LISTBOX_Directory( descr, wParam, (const WCHAR *)lParam, msg == LB_DIR ); + + case LB_GETLOCALE: + return descr->locale; + + case LB_SETLOCALE: + { + LCID ret; + if (!IsValidLocale((LCID)wParam, LCID_INSTALLED)) + return LB_ERR; + ret = descr->locale; + descr->locale = (LCID)wParam; + return ret; + } + + case LB_INITSTORAGE: + return LISTBOX_InitStorage( descr, wParam ); + + case LB_SETCOUNT: + return LISTBOX_SetCount( descr, (INT)wParam ); + + case LB_SETTABSTOPS: + return LISTBOX_SetTabStops( descr, wParam, (LPINT)lParam ); + + case LB_CARETON: + if (descr->caret_on) + return LB_OKAY; + descr->caret_on = TRUE; + if ((descr->focus_item != -1) && (descr->in_focus)) + LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); + return LB_OKAY; + + case LB_CARETOFF: + if (!descr->caret_on) + return LB_OKAY; + descr->caret_on = FALSE; + if ((descr->focus_item != -1) && (descr->in_focus)) + LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); + return LB_OKAY; + + case LB_GETLISTBOXINFO: + return descr->page_size; + + case WM_DESTROY: + return LISTBOX_Destroy( descr ); + + case WM_ENABLE: + InvalidateRect( descr->self, NULL, TRUE ); + return 0; + + case WM_SETREDRAW: + LISTBOX_SetRedraw( descr, wParam != 0 ); + return 0; + + case WM_GETDLGCODE: + return DLGC_WANTARROWS | DLGC_WANTCHARS; + + case WM_PRINTCLIENT: + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( descr->self, &ps ); + ret = LISTBOX_Paint( descr, hdc ); + if( !wParam ) EndPaint( descr->self, &ps ); + } + return ret; + + case WM_NCPAINT: + LISTBOX_NCPaint( descr, (HRGN)wParam ); + break; + + case WM_SIZE: + LISTBOX_UpdateSize( descr ); + return 0; + case WM_GETFONT: + return (LRESULT)descr->font; + case WM_SETFONT: + LISTBOX_SetFont( descr, (HFONT)wParam ); + if (lParam) InvalidateRect( descr->self, 0, TRUE ); + return 0; + case WM_SETFOCUS: + descr->in_focus = TRUE; + descr->caret_on = TRUE; + if (descr->focus_item != -1) + LISTBOX_DrawFocusRect( descr, TRUE ); + SEND_NOTIFICATION( descr, LBN_SETFOCUS ); + return 0; + case WM_KILLFOCUS: + LISTBOX_HandleLButtonUp( descr ); /* Release capture if we have it */ + descr->in_focus = FALSE; + descr->wheel_remain = 0; + if ((descr->focus_item != -1) && descr->caret_on) + LISTBOX_RepaintItem( descr, descr->focus_item, ODA_FOCUS ); + SEND_NOTIFICATION( descr, LBN_KILLFOCUS ); + return 0; + case WM_HSCROLL: + return LISTBOX_HandleHScroll( descr, LOWORD(wParam), HIWORD(wParam) ); + case WM_VSCROLL: + return LISTBOX_HandleVScroll( descr, LOWORD(wParam), HIWORD(wParam) ); + case WM_MOUSEWHEEL: + if (wParam & (MK_SHIFT | MK_CONTROL)) + return DefWindowProcW( descr->self, msg, wParam, lParam ); + return LISTBOX_HandleMouseWheel( descr, (SHORT)HIWORD(wParam) ); + case WM_LBUTTONDOWN: + if (lphc) + return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam, + (INT16)LOWORD(lParam), + (INT16)HIWORD(lParam) ); + return LISTBOX_HandleLButtonDown( descr, wParam, + (INT16)LOWORD(lParam), + (INT16)HIWORD(lParam) ); + case WM_LBUTTONDBLCLK: + if (lphc) + return LISTBOX_HandleLButtonDownCombo(descr, msg, wParam, + (INT16)LOWORD(lParam), + (INT16)HIWORD(lParam) ); + if (descr->style & LBS_NOTIFY) + SEND_NOTIFICATION( descr, LBN_DBLCLK ); + return 0; + case WM_MOUSEMOVE: + if ( lphc && ((lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE) ) + { + BOOL captured = descr->captured; + POINT mousePos; + RECT clientRect; + + mousePos.x = (INT16)LOWORD(lParam); + mousePos.y = (INT16)HIWORD(lParam); + + /* + * If we are in a dropdown combobox, we simulate that + * the mouse is captured to show the tracking of the item. + */ + if (GetClientRect(descr->self, &clientRect) && PtInRect( &clientRect, mousePos )) + descr->captured = TRUE; + + LISTBOX_HandleMouseMove( descr, mousePos.x, mousePos.y); + + descr->captured = captured; + } + else if (GetCapture() == descr->self) + { + LISTBOX_HandleMouseMove( descr, (INT16)LOWORD(lParam), + (INT16)HIWORD(lParam) ); + } + return 0; + case WM_LBUTTONUP: + if (lphc) + { + POINT mousePos; + RECT clientRect; + + /* + * If the mouse button "up" is not in the listbox, + * we make sure there is no selection by re-selecting the + * item that was selected when the listbox was made visible. + */ + mousePos.x = (INT16)LOWORD(lParam); + mousePos.y = (INT16)HIWORD(lParam); + + GetClientRect(descr->self, &clientRect); + + /* + * When the user clicks outside the combobox and the focus + * is lost, the owning combobox will send a fake buttonup with + * 0xFFFFFFF as the mouse location, we must also revert the + * selection to the original selection. + */ + if ( (lParam == (LPARAM)-1) || (!PtInRect( &clientRect, mousePos )) ) + LISTBOX_MoveCaret( descr, lphc->droppedIndex, FALSE ); + } + return LISTBOX_HandleLButtonUp( descr ); + case WM_KEYDOWN: + if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != 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, FALSE ); + return 0; + } + } + return LISTBOX_HandleKeyDown( descr, wParam ); + case WM_CHAR: + return LISTBOX_HandleChar( descr, wParam ); + + case WM_SYSTIMER: + return LISTBOX_HandleSystemTimer( descr ); + case WM_ERASEBKGND: + if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED)) + { + RECT rect; + HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX, + wParam, (LPARAM)descr->self ); + TRACE("hbrush = %p\n", hbrush); + if(!hbrush) + hbrush = GetSysColorBrush(COLOR_WINDOW); + if(hbrush) + { + GetClientRect(descr->self, &rect); + FillRect((HDC)wParam, &rect, hbrush); + } + } + return 1; + case WM_DROPFILES: + if( lphc ) return 0; + return SendMessageW( descr->owner, msg, wParam, lParam ); + + case WM_NCDESTROY: + if( lphc && (lphc->dwStyle & CBS_DROPDOWNLIST) != CBS_SIMPLE ) + lphc->hWndLBox = 0; + break; + + case WM_NCACTIVATE: + if (lphc) return 0; + break; + + case WM_THEMECHANGED: + theme = GetWindowTheme( hwnd ); + CloseThemeData( theme ); + OpenThemeData( hwnd, WC_LISTBOXW ); + break; + + default: + if ((msg >= WM_USER) && (msg < 0xc000)) + WARN("[%p]: unknown msg %04x wp %08lx lp %08lx\n", + hwnd, msg, wParam, lParam ); + } + + return DefWindowProcW( hwnd, msg, wParam, lParam ); +} + +void LISTBOX_Register(void) +{ + WNDCLASSW wndClass; + + memset(&wndClass, 0, sizeof(wndClass)); + wndClass.style = CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS; + wndClass.lpfnWndProc = LISTBOX_WindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = sizeof(LB_DESCR *); + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = WC_LISTBOXW; + RegisterClassW(&wndClass); +} + +void COMBOLBOX_Register(void) +{ + static const WCHAR combolboxW[] = {'C','o','m','b','o','L','B','o','x',0}; + WNDCLASSW wndClass; + + memset(&wndClass, 0, sizeof(wndClass)); + wndClass.style = CS_SAVEBITS | CS_DBLCLKS | CS_DROPSHADOW | CS_GLOBALCLASS; + wndClass.lpfnWndProc = LISTBOX_WindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = sizeof(LB_DESCR *); + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = combolboxW; + RegisterClassW(&wndClass); +} diff --git a/dll/win32/comctl32/listview.c b/dll/win32/comctl32/listview.c index 94b1fc28b17..fb40d227df5 100644 --- a/dll/win32/comctl32/listview.c +++ b/dll/win32/comctl32/listview.c @@ -24,15 +24,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTES - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * * Default Message Processing @@ -133,10 +124,29 @@ * -- LVGroupComparE */ -#include "comctl32.h" +#include "config.h" +#include "wine/port.h" +#include +#include +#include +#include +#include #include +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "comctl32.h" +#include "uxtheme.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + WINE_DEFAULT_DEBUG_CHANNEL(listview); typedef struct tagCOLUMN_INFO @@ -1055,7 +1065,7 @@ static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRA COLORREF backcolor, textcolor; /* apparently, for selected items, we have to override the returned values */ - if (!SubItem) + if (!SubItem || (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) { if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED) { @@ -4788,6 +4798,7 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERAT while (iterator_next(subitems)) { DWORD subitemstage = CDRF_DODEFAULT; + NMLVCUSTOMDRAW temp_nmlvcd; /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */ if (subitems->nItem) @@ -4814,19 +4825,16 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERAT if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW) subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd); - else - { - nmlvcd.clrTextBk = infoPtr->clrTextBk; - nmlvcd.clrText = infoPtr->clrText; - } - if (subitems->nItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW)) - prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE); - else if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) - prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE); + /* + * A selection should neither affect the colors in the post paint notification nor + * affect the colors of the next drawn subitem. Copy the structure to prevent this. + */ + temp_nmlvcd = nmlvcd; + prepaint_setup(infoPtr, hdc, &temp_nmlvcd, subitems->nItem); if (!(subitemstage & CDRF_SKIPDEFAULT)) - LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos); + LISTVIEW_DrawItemPart(infoPtr, &lvItem, &temp_nmlvcd, &pos); if (subitemstage & CDRF_NOTIFYPOSTPAINT) subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd); @@ -6688,16 +6696,19 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, /* make a local copy */ isubitem = lpLVItem->iSubItem; + if (isubitem && (lpLVItem->mask & LVIF_STATE)) + lpLVItem->state = 0; + /* a quick optimization if all we're asked is the focus state * these queries are worth optimising since they are common, * and can be answered in constant time, without the heavy accesses */ if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) && !(infoPtr->uCallbackMask & LVIS_FOCUSED) ) { - lpLVItem->state = 0; - if (infoPtr->nFocusedItem == lpLVItem->iItem) - lpLVItem->state |= LVIS_FOCUSED; - return TRUE; + lpLVItem->state = 0; + if (infoPtr->nFocusedItem == lpLVItem->iItem && isubitem == 0) + lpLVItem->state |= LVIS_FOCUSED; + return TRUE; } ZeroMemory(&dispInfo, sizeof(dispInfo)); @@ -11908,7 +11919,7 @@ static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lP SIZE sz; if (!infoPtr->hwndEdit || !hdc) return 0; - GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0])); + GetWindowTextW(infoPtr->hwndEdit, buffer, ARRAY_SIZE(buffer)); GetWindowRect(infoPtr->hwndEdit, &rect); /* Select font to get the right dimension of the string */ diff --git a/dll/win32/comctl32/monthcal.c b/dll/win32/comctl32/monthcal.c index ff20af1838d..744b9d382ce 100644 --- a/dll/win32/comctl32/monthcal.c +++ b/dll/win32/comctl32/monthcal.c @@ -22,15 +22,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Oct. 20, 2004, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * -- MCM_[GS]ETUNICODEFORMAT * -- handle resources better (doesn't work now); @@ -39,7 +30,24 @@ * -- search for FIXME */ +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(monthcal); @@ -60,8 +68,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(monthcal); #define MC_CALENDAR_PADDING 6 -#define countof(arr) (sizeof(arr)/sizeof(arr[0])) - /* convert from days to 100 nanoseconds unit - used as FILETIME unit */ #define DAYSTO100NSECS(days) (((ULONGLONG)(days))*24*60*60*10000000) @@ -881,18 +887,18 @@ static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRU SelectObject(hdc, infoPtr->hBoldFont); /* draw formatted date string */ - GetDateFormatW(LOCALE_USER_DEFAULT, DATE_YEARMONTH, st, NULL, strW, countof(strW)); + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_YEARMONTH, st, NULL, strW, ARRAY_SIZE(strW)); DrawTextW(hdc, strW, strlenW(strW), title, DT_CENTER | DT_VCENTER | DT_SINGLELINE); - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SYEARMONTH, fmtW, countof(fmtW)); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SYEARMONTH, fmtW, ARRAY_SIZE(fmtW)); wsprintfW(yearW, fmtyearW, st->wYear); /* month is trickier as it's possible to have different format pictures, we'll test for M, MM, MMM, and MMMM */ if (strstrW(fmtW, mmmmW)) - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+st->wMonth-1, monthW, countof(monthW)); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+st->wMonth-1, monthW, ARRAY_SIZE(monthW)); else if (strstrW(fmtW, mmmW)) - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1+st->wMonth-1, monthW, countof(monthW)); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1+st->wMonth-1, monthW, ARRAY_SIZE(monthW)); else if (strstrW(fmtW, mmW)) wsprintfW(monthW, fmtmmW, st->wMonth); else @@ -970,7 +976,7 @@ static void MONTHCAL_PaintWeeknumbers(const MONTHCAL_INFO *infoPtr, HDC hdc, con LOCALE_IFIRSTWEEKOFYEAR == 1 (what countries?) The first week of the year must contain only days of the new year */ - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf)); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, ARRAY_SIZE(buf)); weeknum = atoiW(buf); switch (weeknum) { @@ -1062,15 +1068,14 @@ static void MONTHCAL_PaintTodayTitle(const MONTHCAL_INFO *infoPtr, HDC hdc, cons if(infoPtr->dwStyle & MCS_NOTODAY) return; - LoadStringW(COMCTL32_hModule, IDM_TODAY, buf_todayW, countof(buf_todayW)); + LoadStringW(COMCTL32_hModule, IDM_TODAY, buf_todayW, ARRAY_SIZE(buf_todayW)); col = infoPtr->dwStyle & MCS_NOTODAYCIRCLE ? 0 : 1; if (infoPtr->dwStyle & MCS_WEEKNUMBERS) col--; /* label is located below first calendar last row */ MONTHCAL_GetDayRectI(infoPtr, &text_rect, col, 6, infoPtr->dim.cx * infoPtr->dim.cy - infoPtr->dim.cx); box_rect = text_rect; - GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &infoPtr->todaysDate, NULL, - buf_dateW, countof(buf_dateW)); + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &infoPtr->todaysDate, NULL, buf_dateW, ARRAY_SIZE(buf_dateW)); old_font = SelectObject(hdc, infoPtr->hBoldFont); SetTextColor(hdc, infoPtr->colors[MCSC_TEXT]); @@ -1202,7 +1207,7 @@ static void MONTHCAL_PaintCalendar(const MONTHCAL_INFO *infoPtr, HDC hdc, const i = infoPtr->firstDay; for(j = 0; j < 7; j++) { - get_localized_dayname(infoPtr, (i + j + 6) % 7, buf, countof(buf)); + get_localized_dayname(infoPtr, (i + j + 6) % 7, buf, ARRAY_SIZE(buf)); DrawTextW(hdc, buf, strlenW(buf), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); OffsetRect(&r, infoPtr->width_increment, 0); } @@ -1408,7 +1413,7 @@ MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, INT day) { WCHAR buf[80]; - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, buf, countof(buf)); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, buf, ARRAY_SIZE(buf)); TRACE("%s %d\n", debugstr_w(buf), strlenW(buf)); new_day = atoiW(buf); @@ -1953,7 +1958,7 @@ static void MONTHCAL_NotifyDayState(MONTHCAL_INFO *infoPtr) nmds.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); nmds.nmhdr.code = MCN_GETDAYSTATE; nmds.cDayState = MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0); - nmds.prgDayState = state = Alloc(nmds.cDayState * sizeof(MONTHDAYSTATE)); + nmds.prgDayState = state = heap_alloc_zero(nmds.cDayState * sizeof(MONTHDAYSTATE)); MONTHCAL_GetMinDate(infoPtr, &nmds.stStart); nmds.stStart.wDay = 1; @@ -1962,7 +1967,7 @@ static void MONTHCAL_NotifyDayState(MONTHCAL_INFO *infoPtr) memcpy(infoPtr->monthdayState, nmds.prgDayState, MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0)*sizeof(MONTHDAYSTATE)); - Free(state); + heap_free(state); } /* no valid range check performed */ @@ -2038,7 +2043,7 @@ MONTHCAL_RButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam) WCHAR buf[32]; hMenu = CreatePopupMenu(); - LoadStringW(COMCTL32_hModule, IDM_GOTODAY, buf, countof(buf)); + LoadStringW(COMCTL32_hModule, IDM_GOTODAY, buf, ARRAY_SIZE(buf)); AppendMenuW(hMenu, MF_STRING|MF_ENABLED, 1, buf); menupoint.x = (short)LOWORD(lParam); menupoint.y = (short)HIWORD(lParam); @@ -2194,8 +2199,8 @@ MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam) for (i = 0; i < 12; i++) { - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+i, buf, countof(buf)); - AppendMenuW(hMenu, MF_STRING|MF_ENABLED, i + 1, buf); + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+i, buf, ARRAY_SIZE(buf)); + AppendMenuW(hMenu, MF_STRING|MF_ENABLED, i + 1, buf); } menupoint.x = ht.pt.x; menupoint.y = ht.pt.y; @@ -2517,7 +2522,7 @@ static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr) { SIZE sz; - if (get_localized_dayname(infoPtr, i, buff, countof(buff))) + if (get_localized_dayname(infoPtr, i, buff, ARRAY_SIZE(buff))) { GetTextExtentPoint32W(hdc, buff, lstrlenW(buff), &sz); if (sz.cx > day_width) day_width = sz.cx; @@ -2525,7 +2530,7 @@ static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr) else /* locale independent fallback on failure */ { static const WCHAR sunW[] = { 'S','u','n' }; - GetTextExtentPoint32W(hdc, sunW, countof(sunW), &sz); + GetTextExtentPoint32W(hdc, sunW, ARRAY_SIZE(sunW), &sz); day_width = sz.cx; break; } @@ -2602,9 +2607,9 @@ static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr) { infoPtr->dim.cx = x; infoPtr->dim.cy = y; - infoPtr->calendars = ReAlloc(infoPtr->calendars, MONTHCAL_GetCalCount(infoPtr)*sizeof(CALENDAR_INFO)); + infoPtr->calendars = heap_realloc(infoPtr->calendars, MONTHCAL_GetCalCount(infoPtr)*sizeof(CALENDAR_INFO)); - infoPtr->monthdayState = ReAlloc(infoPtr->monthdayState, + infoPtr->monthdayState = heap_realloc(infoPtr->monthdayState, MONTHCAL_GetMonthRange(infoPtr, GMR_DAYSTATE, 0)*sizeof(MONTHDAYSTATE)); MONTHCAL_NotifyDayState(infoPtr); @@ -2760,7 +2765,7 @@ MONTHCAL_Create(HWND hwnd, LPCREATESTRUCTW lpcs) MONTHCAL_INFO *infoPtr; /* allocate memory for info structure */ - infoPtr = Alloc(sizeof(MONTHCAL_INFO)); + infoPtr = heap_alloc_zero(sizeof(*infoPtr)); SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); if (infoPtr == NULL) { @@ -2772,9 +2777,9 @@ MONTHCAL_Create(HWND hwnd, LPCREATESTRUCTW lpcs) infoPtr->hwndNotify = lpcs->hwndParent; infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE); infoPtr->dim.cx = infoPtr->dim.cy = 1; - infoPtr->calendars = Alloc(sizeof(CALENDAR_INFO)); + infoPtr->calendars = heap_alloc_zero(sizeof(CALENDAR_INFO)); if (!infoPtr->calendars) goto fail; - infoPtr->monthdayState = Alloc(3*sizeof(MONTHDAYSTATE)); + infoPtr->monthdayState = heap_alloc_zero(3 * sizeof(MONTHDAYSTATE)); if (!infoPtr->monthdayState) goto fail; /* initialize info structure */ @@ -2815,9 +2820,9 @@ MONTHCAL_Create(HWND hwnd, LPCREATESTRUCTW lpcs) return 0; fail: - Free(infoPtr->monthdayState); - Free(infoPtr->calendars); - Free(infoPtr); + heap_free(infoPtr->monthdayState); + heap_free(infoPtr->calendars); + heap_free(infoPtr); return 0; } @@ -2827,8 +2832,8 @@ MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr) INT i; /* free month calendar info data */ - Free(infoPtr->monthdayState); - Free(infoPtr->calendars); + heap_free(infoPtr->monthdayState); + heap_free(infoPtr->calendars); SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0); CloseThemeData (GetWindowTheme (infoPtr->hwndSelf)); @@ -2836,7 +2841,7 @@ MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr) for (i = 0; i < BrushLast; i++) DeleteObject(infoPtr->brushes[i]); for (i = 0; i < PenLast; i++) DeleteObject(infoPtr->pens[i]); - Free(infoPtr); + heap_free(infoPtr); return 0; } diff --git a/dll/win32/comctl32/nativefont.c b/dll/win32/comctl32/nativefont.c index c64acfca038..2e11a9e665a 100644 --- a/dll/win32/comctl32/nativefont.c +++ b/dll/win32/comctl32/nativefont.c @@ -27,7 +27,15 @@ * - All notifications. */ +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(nativefont); diff --git a/dll/win32/comctl32/pager.c b/dll/win32/comctl32/pager.c index 3004435c6a7..fe68515e673 100644 --- a/dll/win32/comctl32/pager.c +++ b/dll/win32/comctl32/pager.c @@ -52,7 +52,17 @@ * Robert Shearman */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(pager); @@ -547,7 +557,7 @@ PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs) PAGER_INFO *infoPtr; /* allocate memory for info structure */ - infoPtr = Alloc (sizeof(PAGER_INFO)); + infoPtr = heap_alloc_zero (sizeof(*infoPtr)); if (!infoPtr) return -1; SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); @@ -579,7 +589,7 @@ static LRESULT PAGER_Destroy (PAGER_INFO *infoPtr) { SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0); - Free (infoPtr); /* free pager info data */ + heap_free (infoPtr); return 0; } diff --git a/dll/win32/comctl32/precomp.h b/dll/win32/comctl32/precomp.h new file mode 100644 index 00000000000..05f7b082dc6 --- /dev/null +++ b/dll/win32/comctl32/precomp.h @@ -0,0 +1,28 @@ + +#ifndef _WINE_COMCTL32_PRECOMP_H +#define _WINE_COMCTL32_PRECOMP_H + +#include + +#include + +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include "comctl32.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#endif /* !_WINE_COMCTL32_PRECOMP_H */ diff --git a/dll/win32/comctl32/progress.c b/dll/win32/comctl32/progress.c index 111bc6a5c3b..28d9fd2fd37 100644 --- a/dll/win32/comctl32/progress.c +++ b/dll/win32/comctl32/progress.c @@ -18,15 +18,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * * Styles: @@ -34,7 +25,19 @@ * */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(progress); @@ -551,7 +554,7 @@ static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); /* allocate memory for info struct */ - infoPtr = Alloc (sizeof(PROGRESS_INFO)); + infoPtr = heap_alloc_zero (sizeof(*infoPtr)); if (!infoPtr) return -1; SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); @@ -573,7 +576,7 @@ static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message, case WM_DESTROY: TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd); - Free (infoPtr); + heap_free (infoPtr); SetWindowLongPtrW(hwnd, 0, 0); theme = GetWindowTheme (hwnd); CloseThemeData (theme); @@ -652,8 +655,14 @@ static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message, INT oldVal; oldVal = infoPtr->CurVal; infoPtr->CurVal += infoPtr->Step; - if(infoPtr->CurVal > infoPtr->MaxVal) - infoPtr->CurVal = infoPtr->MinVal; + if (infoPtr->CurVal > infoPtr->MaxVal) + { + infoPtr->CurVal = (infoPtr->CurVal - infoPtr->MinVal) % (infoPtr->MaxVal - infoPtr->MinVal) + infoPtr->MinVal; + } + if (infoPtr->CurVal < infoPtr->MinVal) + { + infoPtr->CurVal = (infoPtr->CurVal - infoPtr->MinVal) % (infoPtr->MaxVal - infoPtr->MinVal) + infoPtr->MaxVal; + } if(oldVal != infoPtr->CurVal) { TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal); diff --git a/dll/win32/comctl32/propsheet.c b/dll/win32/comctl32/propsheet.c index e5f5d50d385..70316b5a2bf 100644 --- a/dll/win32/comctl32/propsheet.c +++ b/dll/win32/comctl32/propsheet.c @@ -49,7 +49,23 @@ * o PSP_USEREFPARENT */ +#include +#include + +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "prsht.h" #include "comctl32.h" +#include "uxtheme.h" + +#include "wine/debug.h" +#include "wine/unicode.h" /****************************************************************************** * Data structures @@ -535,7 +551,7 @@ static BOOL PROPSHEET_CollectPageInfo(LPCPROPSHEETPAGEW lppsp, if (IS_INTRESOURCE( lppsp->pszTitle )) { - if (LoadStringW( lppsp->hInstance, (DWORD_PTR)lppsp->pszTitle, szTitle, sizeof(szTitle)/sizeof(szTitle[0]) )) + if (LoadStringW( lppsp->hInstance, (DWORD_PTR)lppsp->pszTitle, szTitle, ARRAY_SIZE(szTitle))) pTitle = szTitle; else if (*p) pTitle = p; @@ -2099,8 +2115,7 @@ static void PROPSHEET_SetTitleA(HWND hwndDlg, DWORD dwStyle, LPCSTR lpszText) if(!IS_INTRESOURCE(lpszText)) { WCHAR szTitle[256]; - MultiByteToWideChar(CP_ACP, 0, lpszText, -1, - szTitle, sizeof(szTitle)/sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, lpszText, -1, szTitle, ARRAY_SIZE(szTitle)); PROPSHEET_SetTitleW(hwndDlg, dwStyle, szTitle); } else @@ -2119,8 +2134,7 @@ static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText) TRACE("%s (style %08x)\n", debugstr_w(lpszText), dwStyle); if (IS_INTRESOURCE(lpszText)) { - if (!LoadStringW(psInfo->ppshheader.hInstance, - LOWORD(lpszText), szTitle, sizeof(szTitle)/sizeof(szTitle[0]))) + if (!LoadStringW(psInfo->ppshheader.hInstance, LOWORD(lpszText), szTitle, ARRAY_SIZE(szTitle))) return; lpszText = szTitle; } @@ -3708,7 +3722,7 @@ PROPSHEET_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) HWND hwndCancel = GetDlgItem(hwnd, IDCANCEL); EnableWindow(hwndCancel, FALSE); - if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, sizeof(buf)/sizeof(buf[0]))) + if (LoadStringW(COMCTL32_hModule, IDS_CLOSE, buf, ARRAY_SIZE(buf))) SetWindowTextW(hwndOK, buf); return FALSE; diff --git a/dll/win32/comctl32/rebar.c b/dll/win32/comctl32/rebar.c index 38a700407a6..19019d7a547 100644 --- a/dll/win32/comctl32/rebar.c +++ b/dll/win32/comctl32/rebar.c @@ -76,7 +76,22 @@ * at least RB_INSERTBAND */ +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "wine/unicode.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(rebar); @@ -1039,7 +1054,7 @@ REBAR_MoveChildWindows (const REBAR_INFO *infoPtr, UINT start, UINT endplus) lpBand->rcChild = rbcz.rcChild; /* *** ??? */ } - GetClassNameW (lpBand->hwndChild, szClassName, sizeof(szClassName)/sizeof(szClassName[0])); + GetClassNameW (lpBand->hwndChild, szClassName, ARRAY_SIZE(szClassName)); if (!lstrcmpW (szClassName, strComboBox) || !lstrcmpW (szClassName, WC_COMBOBOXEXW)) { INT nEditHeight, yPos; diff --git a/dll/win32/comctl32/rsrc.rc b/dll/win32/comctl32/rsrc.rc index 2bf1bbf3611..948c02ad464 100644 --- a/dll/win32/comctl32/rsrc.rc +++ b/dll/win32/comctl32/rsrc.rc @@ -19,11 +19,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#include -#include -#include +#include "comctl32.h" -#include "resource.h" LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL diff --git a/dll/win32/comctl32/smoothscroll.c b/dll/win32/comctl32/smoothscroll.c index 52fb8cb7a33..e71fac330e3 100644 --- a/dll/win32/comctl32/smoothscroll.c +++ b/dll/win32/comctl32/smoothscroll.c @@ -21,7 +21,14 @@ * - actually add smooth scrolling */ -#include "comctl32.h" +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winerror.h" +#include "winuser.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(commctrl); diff --git a/dll/win32/comctl32/static.c b/dll/win32/comctl32/static.c new file mode 100644 index 00000000000..d9e2fe30dc7 --- /dev/null +++ b/dll/win32/comctl32/static.c @@ -0,0 +1,793 @@ +/* + * Static control + * + * Copyright David W. Metcalfe, 1993 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Notes: + * - Windows XP introduced new behavior: The background of centered + * icons and bitmaps is painted differently. This is only done if + * a manifest is present. + * Because it has not yet been decided how to implement the two + * different modes in Wine, only the Windows XP mode is implemented. + * - Controls with SS_SIMPLE but without SS_NOPREFIX: + * The text should not be changed. Windows doesn't clear the + * client rectangle, so the new text must be larger than the old one. + * - The SS_RIGHTJUST style is currently not implemented by Windows + * (or it does something different than documented). + * + * TODO: + * - Animated cursors + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commctrl.h" + +#include "wine/debug.h" + +#include "comctl32.h" + +WINE_DEFAULT_DEBUG_CHANNEL(static); + +static void STATIC_PaintOwnerDrawfn( HWND hwnd, HDC hdc, DWORD style ); +static void STATIC_PaintTextfn( HWND hwnd, HDC hdc, DWORD style ); +static void STATIC_PaintRectfn( HWND hwnd, HDC hdc, DWORD style ); +static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, DWORD style ); +static void STATIC_PaintBitmapfn( HWND hwnd, HDC hdc, DWORD style ); +static void STATIC_PaintEnhMetafn( HWND hwnd, HDC hdc, DWORD style ); +static void STATIC_PaintEtchedfn( HWND hwnd, HDC hdc, DWORD style ); + +/* offsets for GetWindowLong for static private information */ +#define HFONT_GWL_OFFSET 0 +#define HICON_GWL_OFFSET (sizeof(HFONT)) +#define STATIC_EXTRA_BYTES (HICON_GWL_OFFSET + sizeof(HICON)) + +typedef void (*pfPaint)( HWND hwnd, HDC hdc, DWORD style ); + +static const pfPaint staticPaintFunc[SS_TYPEMASK+1] = +{ + STATIC_PaintTextfn, /* SS_LEFT */ + STATIC_PaintTextfn, /* SS_CENTER */ + STATIC_PaintTextfn, /* SS_RIGHT */ + STATIC_PaintIconfn, /* SS_ICON */ + STATIC_PaintRectfn, /* SS_BLACKRECT */ + STATIC_PaintRectfn, /* SS_GRAYRECT */ + STATIC_PaintRectfn, /* SS_WHITERECT */ + STATIC_PaintRectfn, /* SS_BLACKFRAME */ + STATIC_PaintRectfn, /* SS_GRAYFRAME */ + STATIC_PaintRectfn, /* SS_WHITEFRAME */ + NULL, /* SS_USERITEM */ + STATIC_PaintTextfn, /* SS_SIMPLE */ + STATIC_PaintTextfn, /* SS_LEFTNOWORDWRAP */ + STATIC_PaintOwnerDrawfn, /* SS_OWNERDRAW */ + STATIC_PaintBitmapfn, /* SS_BITMAP */ + STATIC_PaintEnhMetafn, /* SS_ENHMETAFILE */ + STATIC_PaintEtchedfn, /* SS_ETCHEDHORZ */ + STATIC_PaintEtchedfn, /* SS_ETCHEDVERT */ + STATIC_PaintEtchedfn, /* SS_ETCHEDFRAME */ +}; + +static BOOL get_icon_size( HICON handle, SIZE *size ) +{ + ICONINFO info; + BITMAP bmp; + int ret; + + if (!GetIconInfo(handle, &info)) + return FALSE; + + ret = GetObjectW(info.hbmColor, sizeof(bmp), &bmp); + if (ret) + { + size->cx = bmp.bmWidth; + size->cy = bmp.bmHeight; + } + + DeleteObject(info.hbmMask); + DeleteObject(info.hbmColor); + + return !!ret; +} + +/*********************************************************************** + * STATIC_SetIcon + * + * Set the icon for an SS_ICON control. + */ +static HICON STATIC_SetIcon( HWND hwnd, HICON hicon, DWORD style ) +{ + HICON prevIcon; + SIZE size; + + if ((style & SS_TYPEMASK) != SS_ICON) return 0; + if (hicon && !get_icon_size( hicon, &size )) + { + WARN("hicon != 0, but invalid\n"); + return 0; + } + prevIcon = (HICON)SetWindowLongPtrW( hwnd, HICON_GWL_OFFSET, (LONG_PTR)hicon ); + if (hicon && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL)) + { + /* Windows currently doesn't implement SS_RIGHTJUST */ + /* + if ((style & SS_RIGHTJUST) != 0) + { + RECT wr; + GetWindowRect(hwnd, &wr); + SetWindowPos( hwnd, 0, wr.right - info->nWidth, wr.bottom - info->nHeight, + info->nWidth, info->nHeight, SWP_NOACTIVATE | SWP_NOZORDER ); + } + else */ + { + SetWindowPos( hwnd, 0, 0, 0, size.cx, size.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); + } + } + return prevIcon; +} + +/*********************************************************************** + * STATIC_SetBitmap + * + * Set the bitmap for an SS_BITMAP control. + */ +static HBITMAP STATIC_SetBitmap( HWND hwnd, HBITMAP hBitmap, DWORD style ) +{ + HBITMAP hOldBitmap; + + if ((style & SS_TYPEMASK) != SS_BITMAP) return 0; + if (hBitmap && GetObjectType(hBitmap) != OBJ_BITMAP) + { + WARN("hBitmap != 0, but it's not a bitmap\n"); + return 0; + } + hOldBitmap = (HBITMAP)SetWindowLongPtrW( hwnd, HICON_GWL_OFFSET, (LONG_PTR)hBitmap ); + if (hBitmap && !(style & SS_CENTERIMAGE) && !(style & SS_REALSIZECONTROL)) + { + BITMAP bm; + GetObjectW(hBitmap, sizeof(bm), &bm); + /* Windows currently doesn't implement SS_RIGHTJUST */ + /* + if ((style & SS_RIGHTJUST) != 0) + { + RECT wr; + GetWindowRect(hwnd, &wr); + SetWindowPos( hwnd, 0, wr.right - bm.bmWidth, wr.bottom - bm.bmHeight, + bm.bmWidth, bm.bmHeight, SWP_NOACTIVATE | SWP_NOZORDER ); + } + else */ + { + SetWindowPos( hwnd, 0, 0, 0, bm.bmWidth, bm.bmHeight, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); + } + } + return hOldBitmap; +} + +/*********************************************************************** + * STATIC_SetEnhMetaFile + * + * Set the enhanced metafile for an SS_ENHMETAFILE control. + */ +static HENHMETAFILE STATIC_SetEnhMetaFile( HWND hwnd, HENHMETAFILE hEnhMetaFile, DWORD style ) +{ + if ((style & SS_TYPEMASK) != SS_ENHMETAFILE) return 0; + if (hEnhMetaFile && GetObjectType(hEnhMetaFile) != OBJ_ENHMETAFILE) + { + WARN("hEnhMetaFile != 0, but it's not an enhanced metafile\n"); + return 0; + } + return (HENHMETAFILE)SetWindowLongPtrW( hwnd, HICON_GWL_OFFSET, (LONG_PTR)hEnhMetaFile ); +} + +/*********************************************************************** + * STATIC_GetImage + * + * Gets the bitmap for an SS_BITMAP control, the icon/cursor for an + * SS_ICON control or the enhanced metafile for an SS_ENHMETAFILE control. + */ +static HANDLE STATIC_GetImage( HWND hwnd, WPARAM wParam, DWORD style ) +{ + switch (style & SS_TYPEMASK) + { + case SS_ICON: + if ((wParam != IMAGE_ICON) && + (wParam != IMAGE_CURSOR)) return NULL; + break; + case SS_BITMAP: + if (wParam != IMAGE_BITMAP) return NULL; + break; + case SS_ENHMETAFILE: + if (wParam != IMAGE_ENHMETAFILE) return NULL; + break; + default: + return NULL; + } + return (HANDLE)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ); +} + +/*********************************************************************** + * STATIC_LoadIconW + * + * Load the icon for an SS_ICON control. + */ +static HICON STATIC_LoadIconW( HINSTANCE hInstance, LPCWSTR name, DWORD style ) +{ + HICON hicon = 0; + + if (hInstance && ((ULONG_PTR)hInstance >> 16)) + { + if ((style & SS_REALSIZEIMAGE) != 0) + hicon = LoadImageW(hInstance, name, IMAGE_ICON, 0, 0, LR_SHARED); + else + { + hicon = LoadIconW( hInstance, name ); + if (!hicon) hicon = LoadCursorW( hInstance, name ); + } + } + if (!hicon) hicon = LoadIconW( 0, name ); + /* Windows doesn't try to load a standard cursor, + probably because most IDs for standard cursors conflict + with the IDs for standard icons anyway */ + return hicon; +} + +/*********************************************************************** + * STATIC_TryPaintFcn + * + * Try to immediately paint the control. + */ +static VOID STATIC_TryPaintFcn(HWND hwnd, LONG full_style) +{ + LONG style = full_style & SS_TYPEMASK; + RECT rc; + + GetClientRect( hwnd, &rc ); + if (!IsRectEmpty(&rc) && IsWindowVisible(hwnd) && staticPaintFunc[style]) + { + HDC hdc; + HRGN hrgn; + + hdc = GetDC( hwnd ); + hrgn = set_control_clipping( hdc, &rc ); + (staticPaintFunc[style])( hwnd, hdc, full_style ); + SelectClipRgn( hdc, hrgn ); + if (hrgn) DeleteObject( hrgn ); + ReleaseDC( hwnd, hdc ); + } +} + +static HBRUSH STATIC_SendWmCtlColorStatic(HWND hwnd, HDC hdc) +{ + HBRUSH hBrush; + HWND parent = GetParent(hwnd); + + if (!parent) parent = hwnd; + hBrush = (HBRUSH) SendMessageW( parent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd ); + if (!hBrush) /* did the app forget to call DefWindowProc ? */ + { + /* FIXME: DefWindowProc should return different colors if a + manifest is present */ + hBrush = (HBRUSH)DefWindowProcW( parent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd); + } + return hBrush; +} + +/*********************************************************************** + * hasTextStyle + * + * Tests if the control displays text. + */ +static BOOL hasTextStyle( DWORD style ) +{ + switch (style & SS_TYPEMASK) + { + case SS_SIMPLE: + case SS_LEFT: + case SS_LEFTNOWORDWRAP: + case SS_CENTER: + case SS_RIGHT: + case SS_OWNERDRAW: + return TRUE; + } + + return FALSE; +} + +static LRESULT CALLBACK STATIC_WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + LRESULT lResult = 0; + LONG full_style = GetWindowLongW( hwnd, GWL_STYLE ); + LONG style = full_style & SS_TYPEMASK; + + if (!IsWindow( hwnd )) return 0; + + switch (uMsg) + { + case WM_CREATE: + if (style < 0L || style > SS_TYPEMASK) + { + ERR("Unknown style 0x%02x\n", style ); + return -1; + } + break; + + case WM_NCDESTROY: + if (style == SS_ICON) + { +/* + * FIXME + * DestroyIcon32( STATIC_SetIcon( wndPtr, 0 ) ); + * + * We don't want to do this yet because DestroyIcon32 is broken. If the icon + * had already been loaded by the application the last thing we want to do is + * GlobalFree16 the handle. + */ + break; + } + else + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + + case WM_ERASEBKGND: + /* do all painting in WM_PAINT like Windows does */ + return 1; + + case WM_PRINTCLIENT: + case WM_PAINT: + { + PAINTSTRUCT ps; + RECT rect; + HDC hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps); + GetClientRect( hwnd, &rect ); + if (staticPaintFunc[style]) + { + HRGN hrgn = set_control_clipping( hdc, &rect ); + (staticPaintFunc[style])( hwnd, hdc, full_style ); + SelectClipRgn( hdc, hrgn ); + if (hrgn) DeleteObject( hrgn ); + } + if (!wParam) EndPaint(hwnd, &ps); + } + break; + + case WM_ENABLE: + STATIC_TryPaintFcn( hwnd, full_style ); + if (full_style & SS_NOTIFY) + { + if (wParam) + SendMessageW( GetParent(hwnd), WM_COMMAND, + MAKEWPARAM( GetWindowLongPtrW(hwnd,GWLP_ID), STN_ENABLE ), (LPARAM)hwnd); + else + SendMessageW( GetParent(hwnd), WM_COMMAND, + MAKEWPARAM( GetWindowLongPtrW(hwnd,GWLP_ID), STN_DISABLE ), (LPARAM)hwnd); + } + break; + + case WM_SYSCOLORCHANGE: + COMCTL32_RefreshSysColors(); + STATIC_TryPaintFcn( hwnd, full_style ); + break; + + case WM_NCCREATE: + { + CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam; + + if (full_style & SS_SUNKEN) + SetWindowLongW( hwnd, GWL_EXSTYLE, + GetWindowLongW( hwnd, GWL_EXSTYLE ) | WS_EX_STATICEDGE ); + + switch (style) + { + case SS_ICON: + { + HICON hIcon; + + hIcon = STATIC_LoadIconW(cs->hInstance, cs->lpszName, full_style); + STATIC_SetIcon(hwnd, hIcon, full_style); + } + break; + case SS_BITMAP: + if ((ULONG_PTR)cs->hInstance >> 16) + { + HBITMAP hBitmap; + hBitmap = LoadBitmapW(cs->hInstance, cs->lpszName); + STATIC_SetBitmap(hwnd, hBitmap, full_style); + } + break; + } + /* SS_ENHMETAFILE: Despite what MSDN says, Windows does not load + the enhanced metafile that was specified as the window text. */ + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + + case WM_SETTEXT: + if (hasTextStyle( full_style )) + { + lResult = DefWindowProcW( hwnd, uMsg, wParam, lParam ); + STATIC_TryPaintFcn( hwnd, full_style ); + } + break; + + case WM_SETFONT: + if (hasTextStyle( full_style )) + { + SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, wParam ); + if (LOWORD(lParam)) + RedrawWindow( hwnd, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN ); + } + break; + + case WM_GETFONT: + return GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); + + case WM_NCHITTEST: + if (full_style & SS_NOTIFY) + return HTCLIENT; + else + return HTTRANSPARENT; + + case WM_GETDLGCODE: + return DLGC_STATIC; + + case WM_LBUTTONDOWN: + case WM_NCLBUTTONDOWN: + if (full_style & SS_NOTIFY) + SendMessageW( GetParent(hwnd), WM_COMMAND, + MAKEWPARAM( GetWindowLongPtrW(hwnd,GWLP_ID), STN_CLICKED ), (LPARAM)hwnd); + return 0; + + case WM_LBUTTONDBLCLK: + case WM_NCLBUTTONDBLCLK: + if (full_style & SS_NOTIFY) + SendMessageW( GetParent(hwnd), WM_COMMAND, + MAKEWPARAM( GetWindowLongPtrW(hwnd,GWLP_ID), STN_DBLCLK ), (LPARAM)hwnd); + return 0; + + case STM_GETIMAGE: + return (LRESULT)STATIC_GetImage( hwnd, wParam, full_style ); + + case STM_GETICON: + return (LRESULT)STATIC_GetImage( hwnd, IMAGE_ICON, full_style ); + + case STM_SETIMAGE: + switch (wParam) + { + case IMAGE_BITMAP: + lResult = (LRESULT)STATIC_SetBitmap( hwnd, (HBITMAP)lParam, full_style ); + break; + case IMAGE_ENHMETAFILE: + lResult = (LRESULT)STATIC_SetEnhMetaFile( hwnd, (HENHMETAFILE)lParam, full_style ); + break; + case IMAGE_ICON: + case IMAGE_CURSOR: + lResult = (LRESULT)STATIC_SetIcon( hwnd, (HICON)lParam, full_style ); + break; + default: + FIXME("STM_SETIMAGE: Unhandled type %lx\n", wParam); + break; + } + STATIC_TryPaintFcn( hwnd, full_style ); + break; + + case STM_SETICON: + lResult = (LRESULT)STATIC_SetIcon( hwnd, (HICON)wParam, full_style ); + STATIC_TryPaintFcn( hwnd, full_style ); + break; + + default: + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + return lResult; +} + +static void STATIC_PaintOwnerDrawfn( HWND hwnd, HDC hdc, DWORD style ) +{ + DRAWITEMSTRUCT dis; + HFONT font, oldFont = NULL; + UINT id = (UINT)GetWindowLongPtrW( hwnd, GWLP_ID ); + + dis.CtlType = ODT_STATIC; + dis.CtlID = id; + dis.itemID = 0; + dis.itemAction = ODA_DRAWENTIRE; + dis.itemState = IsWindowEnabled(hwnd) ? 0 : ODS_DISABLED; + dis.hwndItem = hwnd; + dis.hDC = hdc; + dis.itemData = 0; + GetClientRect( hwnd, &dis.rcItem ); + + font = (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); + if (font) oldFont = SelectObject( hdc, font ); + SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd ); + SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); + if (font) SelectObject( hdc, oldFont ); +} + +static void STATIC_PaintTextfn( HWND hwnd, HDC hdc, DWORD style ) +{ + RECT rc; + HBRUSH hBrush; + HFONT hFont, hOldFont = NULL; + UINT format; + INT len, buf_size; + WCHAR *text; + + GetClientRect( hwnd, &rc); + + switch (style & SS_TYPEMASK) + { + case SS_LEFT: + format = DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK; + break; + + case SS_CENTER: + format = DT_CENTER | DT_EXPANDTABS | DT_WORDBREAK; + break; + + case SS_RIGHT: + format = DT_RIGHT | DT_EXPANDTABS | DT_WORDBREAK; + break; + + case SS_SIMPLE: + format = DT_LEFT | DT_SINGLELINE; + break; + + case SS_LEFTNOWORDWRAP: + format = DT_LEFT | DT_EXPANDTABS; + break; + + default: + return; + } + + if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_RIGHT) + format = DT_RIGHT | (format & ~(DT_LEFT | DT_CENTER)); + + if (style & SS_NOPREFIX) + format |= DT_NOPREFIX; + + if ((style & SS_TYPEMASK) != SS_SIMPLE) + { + if (style & SS_CENTERIMAGE) + format |= DT_SINGLELINE | DT_VCENTER; + if (style & SS_EDITCONTROL) + format |= DT_EDITCONTROL; + if (style & SS_ENDELLIPSIS) + format |= DT_SINGLELINE | DT_END_ELLIPSIS; + if (style & SS_PATHELLIPSIS) + format |= DT_SINGLELINE | DT_PATH_ELLIPSIS; + if (style & SS_WORDELLIPSIS) + format |= DT_SINGLELINE | DT_WORD_ELLIPSIS; + } + + if ((hFont = (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ))) + hOldFont = SelectObject( hdc, hFont ); + + /* SS_SIMPLE controls: WM_CTLCOLORSTATIC is sent, but the returned + brush is not used */ + hBrush = STATIC_SendWmCtlColorStatic(hwnd, hdc); + + if ((style & SS_TYPEMASK) != SS_SIMPLE) + { + FillRect( hdc, &rc, hBrush ); + if (!IsWindowEnabled(hwnd)) SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + } + + buf_size = 256; + if (!(text = HeapAlloc( GetProcessHeap(), 0, buf_size * sizeof(WCHAR) ))) + goto no_TextOut; + + while ((len = InternalGetWindowText( hwnd, text, buf_size )) == buf_size - 1) + { + buf_size *= 2; + if (!(text = HeapReAlloc( GetProcessHeap(), 0, text, buf_size * sizeof(WCHAR) ))) + goto no_TextOut; + } + + if (!len) goto no_TextOut; + + if (((style & SS_TYPEMASK) == SS_SIMPLE) && (style & SS_NOPREFIX)) + { + /* Windows uses the faster ExtTextOut() to draw the text and + to paint the whole client rectangle with the text background + color. Reference: "Static Controls" by Kyle Marsh, 1992 */ + ExtTextOutW( hdc, rc.left, rc.top, ETO_CLIPPED | ETO_OPAQUE, + &rc, text, len, NULL ); + } + else + { + DrawTextW( hdc, text, -1, &rc, format ); + } + +no_TextOut: + HeapFree( GetProcessHeap(), 0, text ); + + if (hFont) + SelectObject( hdc, hOldFont ); +} + +static void STATIC_PaintRectfn( HWND hwnd, HDC hdc, DWORD style ) +{ + RECT rc; + HBRUSH hBrush; + + GetClientRect( hwnd, &rc); + + /* FIXME: send WM_CTLCOLORSTATIC */ + switch (style & SS_TYPEMASK) + { + case SS_BLACKRECT: + hBrush = CreateSolidBrush(comctl32_color.clr3dDkShadow); + FillRect( hdc, &rc, hBrush ); + break; + case SS_GRAYRECT: + hBrush = CreateSolidBrush(comctl32_color.clr3dShadow); + FillRect( hdc, &rc, hBrush ); + break; + case SS_WHITERECT: + hBrush = CreateSolidBrush(comctl32_color.clr3dHilight); + FillRect( hdc, &rc, hBrush ); + break; + case SS_BLACKFRAME: + hBrush = CreateSolidBrush(comctl32_color.clr3dDkShadow); + FrameRect( hdc, &rc, hBrush ); + break; + case SS_GRAYFRAME: + hBrush = CreateSolidBrush(comctl32_color.clr3dShadow); + FrameRect( hdc, &rc, hBrush ); + break; + case SS_WHITEFRAME: + hBrush = CreateSolidBrush(comctl32_color.clr3dHilight); + FrameRect( hdc, &rc, hBrush ); + break; + default: + return; + } + DeleteObject( hBrush ); +} + + +static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, DWORD style ) +{ + RECT rc, iconRect; + HBRUSH hbrush; + HICON hIcon; + SIZE size; + + GetClientRect( hwnd, &rc ); + hbrush = STATIC_SendWmCtlColorStatic(hwnd, hdc); + hIcon = (HICON)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ); + if (!hIcon || !get_icon_size( hIcon, &size )) + { + FillRect(hdc, &rc, hbrush); + } + else + { + if (style & SS_CENTERIMAGE) + { + iconRect.left = (rc.right - rc.left) / 2 - size.cx / 2; + iconRect.top = (rc.bottom - rc.top) / 2 - size.cy / 2; + iconRect.right = iconRect.left + size.cx; + iconRect.bottom = iconRect.top + size.cy; + } + else + iconRect = rc; + FillRect( hdc, &rc, hbrush ); + DrawIconEx( hdc, iconRect.left, iconRect.top, hIcon, iconRect.right - iconRect.left, + iconRect.bottom - iconRect.top, 0, NULL, DI_NORMAL ); + } +} + +static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, DWORD style ) +{ + HDC hMemDC; + HBITMAP hBitmap, oldbitmap; + HBRUSH hbrush; + + /* message is still sent, even if the returned brush is not used */ + hbrush = STATIC_SendWmCtlColorStatic(hwnd, hdc); + + if ((hBitmap = (HBITMAP)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET )) + && (GetObjectType(hBitmap) == OBJ_BITMAP) + && (hMemDC = CreateCompatibleDC( hdc ))) + { + BITMAP bm; + RECT rcClient; + LOGBRUSH brush; + + GetObjectW(hBitmap, sizeof(bm), &bm); + oldbitmap = SelectObject(hMemDC, hBitmap); + + /* Set the background color for monochrome bitmaps + to the color of the background brush */ + if (GetObjectW( hbrush, sizeof(brush), &brush )) + { + if (brush.lbStyle == BS_SOLID) + SetBkColor(hdc, brush.lbColor); + } + GetClientRect(hwnd, &rcClient); + if (style & SS_CENTERIMAGE) + { + FillRect( hdc, &rcClient, hbrush ); + rcClient.left = (rcClient.right - rcClient.left)/2 - bm.bmWidth/2; + rcClient.top = (rcClient.bottom - rcClient.top)/2 - bm.bmHeight/2; + rcClient.right = rcClient.left + bm.bmWidth; + rcClient.bottom = rcClient.top + bm.bmHeight; + } + StretchBlt(hdc, rcClient.left, rcClient.top, rcClient.right - rcClient.left, + rcClient.bottom - rcClient.top, hMemDC, + 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); + SelectObject(hMemDC, oldbitmap); + DeleteDC(hMemDC); + } +} + +static void STATIC_PaintEnhMetafn(HWND hwnd, HDC hdc, DWORD style ) +{ + HENHMETAFILE hEnhMetaFile; + RECT rc; + HBRUSH hbrush; + + GetClientRect(hwnd, &rc); + hbrush = STATIC_SendWmCtlColorStatic(hwnd, hdc); + FillRect(hdc, &rc, hbrush); + if ((hEnhMetaFile = (HENHMETAFILE)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ))) + { + /* The control's current font is not selected into the + device context! */ + if (GetObjectType(hEnhMetaFile) == OBJ_ENHMETAFILE) + PlayEnhMetaFile(hdc, hEnhMetaFile, &rc); + } +} + +static void STATIC_PaintEtchedfn( HWND hwnd, HDC hdc, DWORD style ) +{ + RECT rc; + + /* FIXME: sometimes (not always) sends WM_CTLCOLORSTATIC */ + GetClientRect( hwnd, &rc ); + switch (style & SS_TYPEMASK) + { + case SS_ETCHEDHORZ: + DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP | BF_BOTTOM); + break; + case SS_ETCHEDVERT: + DrawEdge(hdc, &rc, EDGE_ETCHED, BF_LEFT | BF_RIGHT); + break; + case SS_ETCHEDFRAME: + DrawEdge(hdc, &rc, EDGE_ETCHED, BF_RECT); + break; + } +} + +void STATIC_Register(void) +{ + WNDCLASSW wndClass; + + memset(&wndClass, 0, sizeof(wndClass)); + wndClass.style = CS_DBLCLKS | CS_PARENTDC | CS_GLOBALCLASS; + wndClass.lpfnWndProc = STATIC_WindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = STATIC_EXTRA_BYTES; + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = WC_STATICW; + RegisterClassW(&wndClass); +} diff --git a/dll/win32/comctl32/status.c b/dll/win32/comctl32/status.c index cdfad321a64..02e26b0a9d9 100644 --- a/dll/win32/comctl32/status.c +++ b/dll/win32/comctl32/status.c @@ -19,15 +19,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 24, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * * TODO: * -- CCS_BOTTOM (default) * -- CCS_LEFT @@ -40,7 +31,20 @@ * -- CCS_VERT (defaults to RIGHT) */ +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wine/unicode.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(statusbar); diff --git a/dll/win32/comctl32/string.c b/dll/win32/comctl32/string.c index 3a9b2f9ca84..112d9d37b43 100644 --- a/dll/win32/comctl32/string.c +++ b/dll/win32/comctl32/string.c @@ -22,8 +22,24 @@ * */ +#include "config.h" +#include "wine/port.h" + +#include +#include +#include /* atoi */ + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" + #include "comctl32.h" +#include "wine/unicode.h" + +#include "wine/debug.h" + WINE_DEFAULT_DEBUG_CHANNEL(commctrl); /************************************************************************* diff --git a/dll/win32/comctl32/syslink.c b/dll/win32/comctl32/syslink.c index d54b0f5f357..91796a32e44 100644 --- a/dll/win32/comctl32/syslink.c +++ b/dll/win32/comctl32/syslink.c @@ -16,18 +16,20 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTES - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Apr. 4, 2005, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. */ +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/list.h" WINE_DEFAULT_DEBUG_CHANNEL(syslink); @@ -181,7 +183,7 @@ static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text) { if(*current == '<') { - if(!strncmpiW(current, SL_LINKOPEN, sizeof(SL_LINKOPEN)/sizeof(SL_LINKOPEN[0])) && (CurrentType == slText)) + if(!strncmpiW(current, SL_LINKOPEN, ARRAY_SIZE(SL_LINKOPEN)) && (CurrentType == slText)) { BOOL ValidParam = FALSE, ValidLink = FALSE; @@ -209,14 +211,14 @@ static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text) CheckParameter: /* compare the current position with all known parameters */ - if(!strncmpiW(tmp, SL_HREF, sizeof(SL_HREF)/sizeof(SL_HREF[0]))) + if(!strncmpiW(tmp, SL_HREF, ARRAY_SIZE(SL_HREF))) { taglen += 6; ValidParam = TRUE; CurrentParameter = &lpUrl; CurrentParameterLen = &lenUrl; } - else if(!strncmpiW(tmp, SL_ID, sizeof(SL_ID)/sizeof(SL_ID[0]))) + else if(!strncmpiW(tmp, SL_ID, ARRAY_SIZE(SL_ID))) { taglen += 4; ValidParam = TRUE; @@ -290,7 +292,7 @@ CheckParameter: } } } - else if(!strncmpiW(current, SL_LINKCLOSE, sizeof(SL_LINKCLOSE)/sizeof(SL_LINKCLOSE[0])) && (CurrentType == slLink) && firsttag) + else if(!strncmpiW(current, SL_LINKCLOSE, ARRAY_SIZE(SL_LINKCLOSE)) && (CurrentType == slLink) && firsttag) { /* there's a tag opened, first add the previous text, if present */ if(textstart != NULL && textlen > 0 && firsttag > textstart) diff --git a/dll/win32/comctl32/tab.c b/dll/win32/comctl32/tab.c index c5e3a8672ff..cf387cf2837 100644 --- a/dll/win32/comctl32/tab.c +++ b/dll/win32/comctl32/tab.c @@ -52,7 +52,21 @@ * */ +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" +#include WINE_DEFAULT_DEBUG_CHANNEL(tab); diff --git a/dll/win32/comctl32/taskdialog.c b/dll/win32/comctl32/taskdialog.c index 339ebcea18c..1ab9e133dac 100644 --- a/dll/win32/comctl32/taskdialog.c +++ b/dll/win32/comctl32/taskdialog.c @@ -19,8 +19,24 @@ * */ +#include +#include +#include + +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commctrl.h" +#include "winerror.h" #include "comctl32.h" +#include "wine/debug.h" +#include "wine/list.h" +#include "wine/unicode.h" + WINE_DEFAULT_DEBUG_CHANNEL(taskdialog); #define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align)) @@ -423,11 +439,11 @@ static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfi /* Window title */ if (!taskconfig->pszWindowTitle) - titleW = taskdialog_get_exe_name(taskconfig, pathW, sizeof(pathW)/sizeof(pathW[0])); + titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW)); else if (IS_INTRESOURCE(taskconfig->pszWindowTitle)) { if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0)) - titleW = taskdialog_get_exe_name(taskconfig, pathW, sizeof(pathW)/sizeof(pathW[0])); + titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW)); } else titleW = taskconfig->pszWindowTitle; diff --git a/dll/win32/comctl32/theme_button.c b/dll/win32/comctl32/theme_button.c index 4e72b0aa296..2864b8b6658 100644 --- a/dll/win32/comctl32/theme_button.c +++ b/dll/win32/comctl32/theme_button.c @@ -24,6 +24,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(theme_button); #define BUTTON_TYPE 0x0f /* bit mask for the available button types */ +#if 0 + /* These are indices into a states array to determine the theme state for a given theme part. */ typedef enum { @@ -489,3 +491,5 @@ BOOL BUTTON_PaintWithTheme(HTHEME theme, HWND hwnd, HDC hParamDC, LPARAM prfFlag paint(theme, hwnd, hParamDC, drawState, dtFlags, state & BST_FOCUS, prfFlag); return TRUE; } + +#endif \ No newline at end of file diff --git a/dll/win32/comctl32/theme_dialog.c b/dll/win32/comctl32/theme_dialog.c index 1b8567150ad..656e52dbf57 100644 --- a/dll/win32/comctl32/theme_dialog.c +++ b/dll/win32/comctl32/theme_dialog.c @@ -19,7 +19,18 @@ * */ +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "uxtheme.h" +#include "vssym32.h" #include "comctl32.h" +#include "wine/debug.h" /********************************************************************** * The dialog subclass window proc. @@ -103,8 +114,7 @@ LRESULT CALLBACK THEMING_DialogSubclassProc (HWND hWnd, UINT msg, WCHAR controlClass[32]; RECT rc; - GetClassNameW (controlWnd, controlClass, - sizeof(controlClass) / sizeof(controlClass[0])); + GetClassNameW (controlWnd, controlClass, ARRAY_SIZE(controlClass)); if (lstrcmpiW (controlClass, WC_STATICW) == 0) { /* Static control - draw parent background and set text to diff --git a/dll/win32/comctl32/theme_scrollbar.c b/dll/win32/comctl32/theme_scrollbar.c index 99977873c86..bb42267bf21 100644 --- a/dll/win32/comctl32/theme_scrollbar.c +++ b/dll/win32/comctl32/theme_scrollbar.c @@ -19,9 +19,18 @@ * */ -#include "comctl32.h" +#include +#include +#include -WINE_DEFAULT_DEBUG_CHANNEL(theme_scroll); +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "comctl32.h" +#include "wine/debug.h" /* Minimum size of the thumb in pixels */ #define SCROLL_MIN_THUMB 6 @@ -42,6 +51,8 @@ enum SCROLL_HITTEST static HWND tracking_win = 0; static enum SCROLL_HITTEST tracking_hot_part = SCROLL_NOWHERE; +WINE_DEFAULT_DEBUG_CHANNEL(theme_scroll); + static void calc_thumb_dimensions(unsigned int size, SCROLLINFO *si, unsigned int *thumbpos, unsigned int *thumbsize) { if (size <= SCROLL_MIN_RECT) diff --git a/dll/win32/comctl32/theming.c b/dll/win32/comctl32/theming.c index 9cfd8e92929..ec17033729b 100644 --- a/dll/win32/comctl32/theming.c +++ b/dll/win32/comctl32/theming.c @@ -19,34 +19,31 @@ * */ +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" #include "comctl32.h" +#include "uxtheme.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(theming); typedef LRESULT (CALLBACK* THEMING_SUBCLASSPROC)(HWND, UINT, WPARAM, LPARAM, ULONG_PTR); -#ifndef __REACTOS__ /* r73871 */ -extern LRESULT CALLBACK THEMING_ButtonSubclassProc (HWND, UINT, WPARAM, LPARAM, - ULONG_PTR) DECLSPEC_HIDDEN; -#endif -extern LRESULT CALLBACK THEMING_ComboSubclassProc (HWND, UINT, WPARAM, LPARAM, - ULONG_PTR) DECLSPEC_HIDDEN; #ifndef __REACTOS__ /* r73803 */ extern LRESULT CALLBACK THEMING_DialogSubclassProc (HWND, UINT, WPARAM, LPARAM, ULONG_PTR) DECLSPEC_HIDDEN; #endif -extern LRESULT CALLBACK THEMING_EditSubclassProc (HWND, UINT, WPARAM, LPARAM, - ULONG_PTR) DECLSPEC_HIDDEN; -extern LRESULT CALLBACK THEMING_ListBoxSubclassProc (HWND, UINT, WPARAM, LPARAM, - ULONG_PTR) DECLSPEC_HIDDEN; extern LRESULT CALLBACK THEMING_ScrollbarSubclassProc (HWND, UINT, WPARAM, LPARAM, ULONG_PTR) DECLSPEC_HIDDEN; #ifndef __REACTOS__ static const WCHAR dialogClass[] = {'#','3','2','7','7','0',0}; #endif -static const WCHAR comboLboxClass[] = {'C','o','m','b','o','L','b','o','x',0}; static const struct ThemingSubclass { @@ -56,16 +53,11 @@ static const struct ThemingSubclass /* Note: list must be sorted by class name */ #ifndef __REACTOS__ /* r73803 & r73871 */ {dialogClass, THEMING_DialogSubclassProc}, - {WC_BUTTONW, THEMING_ButtonSubclassProc}, #endif - {WC_COMBOBOXW, THEMING_ComboSubclassProc}, - {comboLboxClass, THEMING_ListBoxSubclassProc}, - {WC_EDITW, THEMING_EditSubclassProc}, - {WC_LISTBOXW, THEMING_ListBoxSubclassProc}, {WC_SCROLLBARW, THEMING_ScrollbarSubclassProc} }; -#define NUM_SUBCLASSES (sizeof(subclasses)/sizeof(subclasses[0])) +#define NUM_SUBCLASSES (ARRAY_SIZE(subclasses)) static WNDPROC originalProcs[NUM_SUBCLASSES]; static ATOM atRefDataProp; @@ -93,26 +85,14 @@ static LRESULT CALLBACK subclass_proc ## N (HWND wnd, UINT msg, \ } MAKE_SUBCLASS_PROC(0) +#ifndef __REACTOS__ MAKE_SUBCLASS_PROC(1) -MAKE_SUBCLASS_PROC(2) -MAKE_SUBCLASS_PROC(3) -MAKE_SUBCLASS_PROC(4) -#ifndef __REACTOS__ /* r73803 & r73871 */ -MAKE_SUBCLASS_PROC(5) -MAKE_SUBCLASS_PROC(6) #endif static const WNDPROC subclassProcs[NUM_SUBCLASSES] = { subclass_proc0, +#ifndef __REACTOS__ subclass_proc1, - subclass_proc2, - subclass_proc3, -#ifdef __REACTOS__ /* r73871 */ - subclass_proc4 -#else - subclass_proc4, - subclass_proc5, - subclass_proc6 #endif }; diff --git a/dll/win32/comctl32/toolbar.c b/dll/win32/comctl32/toolbar.c index 949eadb8d7a..c43dc0c980d 100644 --- a/dll/win32/comctl32/toolbar.c +++ b/dll/win32/comctl32/toolbar.c @@ -66,7 +66,21 @@ * */ +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "wingdi.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(toolbar); @@ -289,9 +303,9 @@ TOOLBAR_GetText(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr) static void TOOLBAR_DumpTBButton(const TBBUTTON *tbb, BOOL fUnicode) { - TRACE("TBBUTTON: id %d, bitmap=%d, state=%02x, style=%02x, data=%08lx, stringid=0x%08lx (%s)\n", - tbb->idCommand,tbb->iBitmap, tbb->fsState, tbb->fsStyle, tbb->dwData, tbb->iString, - (fUnicode ? wine_dbgstr_w((LPWSTR)tbb->iString) : wine_dbgstr_a((LPSTR)tbb->iString))); + TRACE("TBBUTTON: id %d, bitmap=%d, state=%02x, style=%02x, data=%p, stringid=%p (%s)\n", tbb->idCommand, + tbb->iBitmap, tbb->fsState, tbb->fsStyle, (void *)tbb->dwData, (void *)tbb->iString, + tbb->iString != -1 ? (fUnicode ? debugstr_w((LPWSTR)tbb->iString) : debugstr_a((LPSTR)tbb->iString)) : ""); } static void @@ -4416,7 +4430,7 @@ TOOLBAR_Restore(TOOLBAR_INFO *infoPtr, const TBSAVEPARAMSW *lpSave) memset( &tb, 0, sizeof(tb) ); tb.iItem = i; - tb.cchText = sizeof(buf) / sizeof(buf[0]); + tb.cchText = ARRAY_SIZE(buf); tb.pszText = buf; /* Use the same struct for both A and W versions since the layout is the same. */ @@ -6397,7 +6411,7 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm TRACE("TBN_GETINFOTIPW - got string %s\n", debugstr_w(tbgit.pszText)); len = strlenW(tbgit.pszText); - if (len > sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0])-1) + if (len > ARRAY_SIZE(lpnmtdi->szText) - 1) { /* need to allocate temporary buffer in infoPtr as there * isn't enough space in buffer passed to us by the @@ -6435,7 +6449,7 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm TRACE("TBN_GETINFOTIPA - got string %s\n", debugstr_a(tbgit.pszText)); len = MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, NULL, 0); - if (len > sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0])) + if (len > ARRAY_SIZE(lpnmtdi->szText)) { /* need to allocate temporary buffer in infoPtr as there * isn't enough space in buffer passed to us by the @@ -6450,8 +6464,7 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm } else if (tbgit.pszText && tbgit.pszText[0]) { - MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, - lpnmtdi->lpszText, sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0])); + MultiByteToWideChar(CP_ACP, 0, tbgit.pszText, -1, lpnmtdi->lpszText, ARRAY_SIZE(lpnmtdi->szText)); return 0; } } @@ -6466,7 +6479,7 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm TRACE("using button hidden text %s\n", debugstr_w(pszText)); - if (len > sizeof(lpnmtdi->szText)/sizeof(lpnmtdi->szText[0])-1) + if (len > ARRAY_SIZE(lpnmtdi->szText) - 1) { /* need to allocate temporary buffer in infoPtr as there * isn't enough space in buffer passed to us by the diff --git a/dll/win32/comctl32/tooltips.c b/dll/win32/comctl32/tooltips.c index b3144934c69..0d89a0256f9 100644 --- a/dll/win32/comctl32/tooltips.c +++ b/dll/win32/comctl32/tooltips.c @@ -89,9 +89,20 @@ * */ -#include "comctl32.h" -#include + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wine/unicode.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "comctl32.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(tooltips); @@ -613,18 +624,10 @@ TOOLTIPS_Show (TOOLTIPS_INFO *infoPtr, BOOL track_activate) } toolPtr = &infoPtr->tools[nTool]; - - TRACE("Show tooltip %d\n", nTool); - - hdr.hwndFrom = infoPtr->hwndSelf; - hdr.idFrom = toolPtr->uId; - hdr.code = TTN_SHOW; - SendMessageW (toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&hdr); - - TRACE("%s\n", debugstr_w(infoPtr->szTipText)); - TOOLTIPS_CalcTipSize (infoPtr, &size); - TRACE("size %d x %d\n", size.cx, size.cy); + + TRACE("Show tooltip %d, %s, size %d x %d\n", nTool, debugstr_w(infoPtr->szTipText), + size.cx, size.cy); if (track_activate && (toolPtr->uFlags & TTF_TRACK)) { @@ -797,7 +800,7 @@ TOOLTIPS_Show (TOOLTIPS_INFO *infoPtr, BOOL track_activate) } } - hrStem = CreatePolygonRgn(pts, sizeof(pts) / sizeof(pts[0]), ALTERNATE); + hrStem = CreatePolygonRgn(pts, ARRAY_SIZE(pts), ALTERNATE); hRgn = CreateRoundRectRgn(0, (infoPtr->bToolBelow ? BALLOON_STEMHEIGHT : 0), @@ -813,9 +816,16 @@ TOOLTIPS_Show (TOOLTIPS_INFO *infoPtr, BOOL track_activate) * it is no longer needed */ } - SetWindowPos (infoPtr->hwndSelf, HWND_TOPMOST, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_SHOWWINDOW | SWP_NOACTIVATE); + SetWindowPos (infoPtr->hwndSelf, NULL, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE); + + hdr.hwndFrom = infoPtr->hwndSelf; + hdr.idFrom = toolPtr->uId; + hdr.code = TTN_SHOW; + SendMessageW (toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&hdr); + + SetWindowPos (infoPtr->hwndSelf, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW | SWP_NOACTIVATE); /* repaint the tooltip */ InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); @@ -1048,7 +1058,7 @@ TOOLTIPS_AddToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW) TRACE("add tool (%p) %p %ld%s\n", infoPtr->hwndSelf, ti->hwnd, ti->uId, (ti->uFlags & TTF_IDISHWND) ? " TTF_IDISHWND" : ""); - if (ti->cbSize >= TTTOOLINFOW_V2_SIZE && !ti->lpszText && isW) + if (ti->cbSize > TTTOOLINFOW_V3_SIZE && isW) return FALSE; if (infoPtr->uNumTools == 0) { diff --git a/dll/win32/comctl32/trackbar.c b/dll/win32/comctl32/trackbar.c index 198e3709ef7..233baedf274 100644 --- a/dll/win32/comctl32/trackbar.c +++ b/dll/win32/comctl32/trackbar.c @@ -18,22 +18,26 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 12, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * */ -#include "comctl32.h" - +#include +#include +#include +#include #include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/debug.h" + +#include "comctl32.h" + WINE_DEFAULT_DEBUG_CHANNEL(trackbar); typedef struct @@ -680,7 +684,7 @@ TRACKBAR_FillThumb (const TRACKBAR_INFO *infoPtr, HDC hdc, HBRUSH hbrush) oldbr = SelectObject(hdc, hbrush); SetPolyFillMode(hdc, WINDING); - Polygon(hdc, points, sizeof(points) / sizeof(points[0])); + Polygon(hdc, points, ARRAY_SIZE(points)); SelectObject(hdc, oldbr); return PointDepth; diff --git a/dll/win32/comctl32/treeview.c b/dll/win32/comctl32/treeview.c index 26ff8e19a09..9e5eb43894f 100644 --- a/dll/win32/comctl32/treeview.c +++ b/dll/win32/comctl32/treeview.c @@ -40,9 +40,31 @@ * Scroll (instead of repaint) as much as possible. */ -#include "comctl32.h" +#include "config.h" +#include "wine/port.h" -#include +#include +#include +#include +#include +#include +#include + +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" +#include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "wine/exception.h" +#include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(treeview); @@ -531,7 +553,7 @@ TREEVIEW_TVItemFromItem(const TREEVIEW_INFO *infoPtr, UINT mask, TVITEMW *tvItem if (!infoPtr->bNtfUnicode) { tvItem->cchTextMax = WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, NULL, 0, NULL, NULL ); - tvItem->pszText = Alloc (tvItem->cchTextMax); + tvItem->pszText = heap_alloc (tvItem->cchTextMax); WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, (LPSTR)tvItem->pszText, tvItem->cchTextMax, 0, 0 ); } else @@ -572,8 +594,8 @@ TREEVIEW_SendTreeviewNotify(const TREEVIEW_INFO *infoPtr, UINT code, UINT action ret = TREEVIEW_SendRealNotify(infoPtr, code, &nmhdr.hdr); if (!infoPtr->bNtfUnicode) { - Free(nmhdr.itemOld.pszText); - Free(nmhdr.itemNew.pszText); + heap_free(nmhdr.itemOld.pszText); + heap_free(nmhdr.itemNew.pszText); } return ret; } @@ -672,7 +694,7 @@ TREEVIEW_BeginLabelEditNotify(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editI ret = TREEVIEW_SendRealNotify(infoPtr, TVN_BEGINLABELEDITW, &tvdi.hdr); if (!infoPtr->bNtfUnicode) - Free(tvdi.item.pszText); + heap_free(tvdi.item.pszText); return ret; } @@ -718,7 +740,7 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, (LPSTR)callback.item.pszText, -1, NULL, 0); buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE); - newText = ReAlloc(item->pszText, buflen); + newText = heap_realloc(item->pszText, buflen); TRACE("returned str %s, len=%d, buflen=%d\n", debugstr_a((LPSTR)callback.item.pszText), len, buflen); @@ -731,12 +753,12 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, item->pszText, buflen/sizeof(WCHAR)); item->cchTextMax = buflen/sizeof(WCHAR); } - /* If ReAlloc fails we have nothing to do, but keep original text */ + /* If realloc fails we have nothing to do, but keep original text */ } else { int len = max(lstrlenW(callback.item.pszText) + 1, TEXT_CALLBACK_SIZE); - LPWSTR newText = ReAlloc(item->pszText, len); + LPWSTR newText = heap_realloc(item->pszText, len); TRACE("returned wstr %s, len=%d\n", debugstr_w(callback.item.pszText), len); @@ -747,7 +769,7 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, strcpyW(item->pszText, callback.item.pszText); item->cchTextMax = len; } - /* If ReAlloc fails we have nothing to do, but keep original text */ + /* If realloc fails we have nothing to do, but keep original text */ } } else if (mask & TVIF_TEXT) { @@ -759,7 +781,7 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, (LPSTR)callback.item.pszText, -1, NULL, 0); buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE); - newText = Alloc(buflen); + newText = heap_alloc(buflen); TRACE("same buffer str %s, len=%d, buflen=%d\n", debugstr_a((LPSTR)callback.item.pszText), len, buflen); @@ -772,7 +794,7 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, (LPSTR)callback.item.pszText, -1, item->pszText, buflen/sizeof(WCHAR)); item->cchTextMax = buflen/sizeof(WCHAR); - Free(oldText); + heap_free(oldText); } } } @@ -980,7 +1002,7 @@ TREEVIEW_UpdateSubTree(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root) static TREEVIEW_ITEM * TREEVIEW_AllocateItem(const TREEVIEW_INFO *infoPtr) { - TREEVIEW_ITEM *newItem = Alloc(sizeof(TREEVIEW_ITEM)); + TREEVIEW_ITEM *newItem = heap_alloc_zero(sizeof(*newItem)); if (!newItem) return NULL; @@ -996,8 +1018,8 @@ TREEVIEW_AllocateItem(const TREEVIEW_INFO *infoPtr) if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1) { - Free(newItem); - return NULL; + heap_free(newItem); + return NULL; } return newItem; @@ -1021,7 +1043,7 @@ TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) infoPtr->dropItem = NULL; if (infoPtr->insertMarkItem == item) infoPtr->insertMarkItem = NULL; - Free(item); + heap_free(item); } @@ -1112,7 +1134,7 @@ TREEVIEW_DoSetItemT(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, else len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1, NULL, 0); - newText = ReAlloc(item->pszText, len * sizeof(WCHAR)); + newText = heap_realloc(item->pszText, len * sizeof(WCHAR)); if (newText == NULL) return FALSE; @@ -1130,10 +1152,8 @@ TREEVIEW_DoSetItemT(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, } else { - callbackSet |= TVIF_TEXT; - - item->pszText = ReAlloc(item->pszText, - TEXT_CALLBACK_SIZE * sizeof(WCHAR)); + callbackSet |= TVIF_TEXT; + item->pszText = heap_realloc(item->pszText, TEXT_CALLBACK_SIZE * sizeof(WCHAR)); item->cchTextMax = TEXT_CALLBACK_SIZE; TRACE("setting callback, item %p\n", item); } @@ -1478,7 +1498,7 @@ TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) infoPtr->uNumItems--; if (item->pszText != LPSTR_TEXTCALLBACKW) - Free(item->pszText); + heap_free(item->pszText); TREEVIEW_FreeItem(infoPtr, item); } @@ -3816,7 +3836,7 @@ TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) infoPtr->bLabelChanged = TRUE; - GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0])); + GetWindowTextW(infoPtr->hwndEdit, buffer, ARRAY_SIZE(buffer)); /* Select font to get the right dimension of the string */ hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0); @@ -4017,18 +4037,18 @@ TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel) if (!infoPtr->bNtfUnicode) { DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, NULL, 0 ); - newText = Alloc(len * sizeof(WCHAR)); + newText = heap_alloc(len * sizeof(WCHAR)); MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, newText, len ); iLength = len - 1; } if (strcmpW(newText, editedItem->pszText) != 0) { - WCHAR *ptr = ReAlloc(editedItem->pszText, sizeof(WCHAR)*(iLength + 1)); + WCHAR *ptr = heap_realloc(editedItem->pszText, sizeof(WCHAR)*(iLength + 1)); if (ptr == NULL) { ERR("OutOfMemory, cannot allocate space for label\n"); - if(newText != tmpText) Free(newText); + if (newText != tmpText) heap_free(newText); DestroyWindow(infoPtr->hwndEdit); infoPtr->hwndEdit = 0; infoPtr->editItem = NULL; @@ -4042,7 +4062,7 @@ TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel) TREEVIEW_ComputeTextWidth(infoPtr, editedItem, 0); } } - if(newText != tmpText) Free(newText); + if (newText != tmpText) heap_free(newText); } ShowWindow(infoPtr->hwndEdit, SW_HIDE); @@ -4626,7 +4646,7 @@ static INT TREEVIEW_ProcessLetterKeys(TREEVIEW_INFO *infoPtr, WPARAM charCode, L /* update the search parameters */ infoPtr->lastKeyPressTimestamp=timestamp; if (elapsed < KEY_DELAY) { - if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam) / sizeof(WCHAR)) { + if (infoPtr->nSearchParamLength < ARRAY_SIZE(infoPtr->szSearchParam)) { infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode; } if (infoPtr->charCode != charCode) { @@ -5064,7 +5084,7 @@ TREEVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs) TRACE("wnd %p, style 0x%x\n", hwnd, GetWindowLongW(hwnd, GWL_STYLE)); - infoPtr = Alloc(sizeof(TREEVIEW_INFO)); + infoPtr = heap_alloc_zero(sizeof(TREEVIEW_INFO)); if (infoPtr == NULL) { @@ -5174,8 +5194,6 @@ TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr) TREEVIEW_FreeItem(infoPtr, infoPtr->root); DPA_Destroy(infoPtr->items); - /* tool tip is automatically destroyed: we are its owner */ - /* Restore original wndproc */ if (infoPtr->hwndEdit) SetWindowLongPtrW(infoPtr->hwndEdit, GWLP_WNDPROC, @@ -5190,7 +5208,8 @@ TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr) DeleteObject(infoPtr->hBoldFont); DeleteObject(infoPtr->hUnderlineFont); DeleteObject(infoPtr->hBoldUnderlineFont); - Free(infoPtr); + DestroyWindow(infoPtr->hwndToolTip); + heap_free(infoPtr); return 0; } diff --git a/dll/win32/comctl32/updown.c b/dll/win32/comctl32/updown.c index 0a53c7baf61..8dec7a4807a 100644 --- a/dll/win32/comctl32/updown.c +++ b/dll/win32/comctl32/updown.c @@ -16,19 +16,25 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * NOTE - * - * This code was audited for completeness against the documented features - * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun. - * - * Unless otherwise noted, we believe this code to be complete, as per - * the specification mentioned above. - * If you discover missing features, or bugs, please note them below. - * */ +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "commctrl.h" #include "comctl32.h" +#include "uxtheme.h" +#include "vssym32.h" +#include "wine/heap.h" +#include "wine/unicode.h" +#include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(updown); @@ -83,7 +89,6 @@ typedef struct #define TIMER_AUTOPRESS 3 #define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongPtrW (hwnd,0)) -#define COUNT_OF(a) (sizeof(a)/sizeof(a[0])) /* id used for SetWindowSubclass */ #define BUDDY_SUBCLASSID 1 @@ -269,7 +274,7 @@ static BOOL UPDOWN_GetBuddyInt (UPDOWN_INFO *infoPtr) /* we have a regular window, so will get the text */ /* note that a zero-length string is a legitimate value for 'txt', * and ought to result in a successful conversion to '0'. */ - if (GetWindowTextW(infoPtr->Buddy, txt, COUNT_OF(txt)) < 0) + if (GetWindowTextW(infoPtr->Buddy, txt, ARRAY_SIZE(txt)) < 0) return FALSE; sep = UPDOWN_GetThousandSep(); @@ -322,7 +327,7 @@ static BOOL UPDOWN_SetBuddyInt (const UPDOWN_INFO *infoPtr) /* Do thousands separation if necessary */ if ((infoPtr->Base == 10) && !(infoPtr->dwStyle & UDS_NOTHOUSANDS) && (len > 3)) { - WCHAR tmp[COUNT_OF(txt)], *src = tmp, *dst = txt; + WCHAR tmp[ARRAY_SIZE(txt)], *src = tmp, *dst = txt; WCHAR sep = UPDOWN_GetThousandSep(); int start = len % 3; @@ -338,7 +343,7 @@ static BOOL UPDOWN_SetBuddyInt (const UPDOWN_INFO *infoPtr) } /* if nothing changed exit earlier */ - GetWindowTextW(infoPtr->Buddy, txt_old, sizeof(txt_old)/sizeof(WCHAR)); + GetWindowTextW(infoPtr->Buddy, txt_old, ARRAY_SIZE(txt_old)); if (lstrcmpiW(txt_old, txt) == 0) return FALSE; return SetWindowTextW(infoPtr->Buddy, txt); @@ -577,14 +582,22 @@ UPDOWN_Buddy_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, switch(uMsg) { case WM_KEYDOWN: - UPDOWN_KeyPressed(infoPtr, (int)wParam); - if ((wParam == VK_UP) || (wParam == VK_DOWN)) return 0; - break; + if (infoPtr) + { + UPDOWN_KeyPressed(infoPtr, (int)wParam); + if (wParam == VK_UP || wParam == VK_DOWN) + return 0; + } + break; case WM_MOUSEWHEEL: - UPDOWN_MouseWheel(infoPtr, (int)wParam); - break; + if (infoPtr) + UPDOWN_MouseWheel(infoPtr, (int)wParam); + break; + case WM_NCDESTROY: + RemoveWindowSubclass(hwnd, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID); + break; default: break; } @@ -592,6 +605,11 @@ UPDOWN_Buddy_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, return DefSubclassProc(hwnd, uMsg, wParam, lParam); } +static void UPDOWN_ResetSubclass (UPDOWN_INFO *infoPtr) +{ + SetWindowSubclass(infoPtr->Buddy, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID, 0); +} + /*********************************************************************** * UPDOWN_SetBuddy * @@ -613,9 +631,8 @@ static HWND UPDOWN_SetBuddy (UPDOWN_INFO* infoPtr, HWND bud) old_buddy = infoPtr->Buddy; - /* there is already a buddy assigned */ - if (infoPtr->Buddy) RemoveWindowSubclass(infoPtr->Buddy, UPDOWN_Buddy_SubclassProc, - BUDDY_SUBCLASSID); + UPDOWN_ResetSubclass (infoPtr); + if (!IsWindow(bud)) bud = NULL; /* Store buddy window handle */ @@ -624,7 +641,7 @@ static HWND UPDOWN_SetBuddy (UPDOWN_INFO* infoPtr, HWND bud) if(bud) { /* Store buddy window class type */ infoPtr->BuddyType = BUDDY_TYPE_UNKNOWN; - if (GetClassNameW(bud, buddyClass, COUNT_OF(buddyClass))) { + if (GetClassNameW(bud, buddyClass, ARRAY_SIZE(buddyClass))) { if (lstrcmpiW(buddyClass, WC_EDITW) == 0) infoPtr->BuddyType = BUDDY_TYPE_EDIT; else if (lstrcmpiW(buddyClass, WC_LISTBOXW) == 0) @@ -893,7 +910,7 @@ static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, L { CREATESTRUCTW *pcs = (CREATESTRUCTW*)lParam; - infoPtr = Alloc (sizeof(UPDOWN_INFO)); + infoPtr = heap_alloc_zero(sizeof(*infoPtr)); SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); /* initialize the info struct */ @@ -926,12 +943,9 @@ static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, L break; case WM_DESTROY: - Free (infoPtr->AccelVect); - - if (infoPtr->Buddy) - RemoveWindowSubclass(infoPtr->Buddy, UPDOWN_Buddy_SubclassProc, - BUDDY_SUBCLASSID); - Free (infoPtr); + heap_free (infoPtr->AccelVect); + UPDOWN_ResetSubclass (infoPtr); + heap_free (infoPtr); SetWindowLongPtrW (hwnd, 0, 0); theme = GetWindowTheme (hwnd); CloseThemeData (theme); @@ -1056,13 +1070,13 @@ static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, L TRACE("UDM_SETACCEL\n"); if(infoPtr->AccelVect) { - Free (infoPtr->AccelVect); + heap_free (infoPtr->AccelVect); infoPtr->AccelCount = 0; infoPtr->AccelVect = 0; } if(wParam==0) return TRUE; - infoPtr->AccelVect = Alloc (wParam*sizeof(UDACCEL)); - if(infoPtr->AccelVect == 0) return FALSE; + infoPtr->AccelVect = heap_alloc(wParam*sizeof(UDACCEL)); + if(!infoPtr->AccelVect) return FALSE; memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL)); infoPtr->AccelCount = wParam; diff --git a/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.2600.2982_none_deadbeef.manifest b/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.2600.2982_none_deadbeef.manifest index 480451ad10f..8029acbb64c 100644 --- a/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.2600.2982_none_deadbeef.manifest +++ b/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.2600.2982_none_deadbeef.manifest @@ -12,6 +12,7 @@ NativeFontCtl ReBarWindow32 ScrollBar + Static SysAnimate32 SysDateTimePick32 SysHeader32 diff --git a/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.2982_none_deadbeef.manifest b/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.2982_none_deadbeef.manifest index 1d82715710a..5f9aae353ab 100644 --- a/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.2982_none_deadbeef.manifest +++ b/dll/win32/comctl32/x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.2982_none_deadbeef.manifest @@ -12,6 +12,7 @@ NativeFontCtl ReBarWindow32 ScrollBar + Static SysAnimate32 SysDateTimePick32 SysHeader32