https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b3fb8555bf9abcc04eeac3...
commit b3fb8555bf9abcc04eeac31c7153d33e2f027fc0 Author: Giannis Adamopoulos gadamopoulos@reactos.org AuthorDate: Wed Apr 4 16:39:21 2018 +0300 Commit: David Quintana gigaherz@gmail.com CommitDate: Fri Apr 20 16:27:17 2018 +0200
[COMCTL32] Sync with Wine Staging 3.3. CORE-14434 --- dll/win32/comctl32/CMakeLists.txt | 15 +- dll/win32/comctl32/animate.c | 86 +- dll/win32/comctl32/button.c | 1743 ++++--- dll/win32/comctl32/combo.c | 2160 +++++++++ dll/win32/comctl32/comboex.c | 58 +- dll/win32/comctl32/comctl32.h | 173 +- dll/win32/comctl32/comctl32undoc.c | 25 +- dll/win32/comctl32/commctrl.c | 62 +- dll/win32/comctl32/datetime.c | 36 +- dll/win32/comctl32/dpa.c | 12 + dll/win32/comctl32/draglist.c | 9 + dll/win32/comctl32/dsa.c | 8 + dll/win32/comctl32/edit.c | 5041 ++++++++++++++++++++ dll/win32/comctl32/flatsb.c | 8 + dll/win32/comctl32/header.c | 60 +- dll/win32/comctl32/hotkey.c | 14 +- dll/win32/comctl32/imagelist.c | 81 +- dll/win32/comctl32/ipaddress.c | 32 +- dll/win32/comctl32/listbox.c | 3083 ++++++++++++ dll/win32/comctl32/listview.c | 63 +- dll/win32/comctl32/monthcal.c | 83 +- dll/win32/comctl32/nativefont.c | 8 + dll/win32/comctl32/pager.c | 14 +- dll/win32/comctl32/precomp.h | 28 + dll/win32/comctl32/progress.c | 35 +- dll/win32/comctl32/propsheet.c | 26 +- dll/win32/comctl32/rebar.c | 17 +- dll/win32/comctl32/rsrc.rc | 5 +- dll/win32/comctl32/smoothscroll.c | 9 +- dll/win32/comctl32/static.c | 793 +++ dll/win32/comctl32/status.c | 22 +- dll/win32/comctl32/string.c | 16 + dll/win32/comctl32/syslink.c | 28 +- dll/win32/comctl32/tab.c | 14 + dll/win32/comctl32/taskdialog.c | 20 +- dll/win32/comctl32/theme_button.c | 4 + dll/win32/comctl32/theme_dialog.c | 14 +- dll/win32/comctl32/theme_scrollbar.c | 15 +- dll/win32/comctl32/theming.c | 42 +- dll/win32/comctl32/toolbar.c | 31 +- dll/win32/comctl32/tooltips.c | 46 +- dll/win32/comctl32/trackbar.c | 30 +- dll/win32/comctl32/treeview.c | 83 +- dll/win32/comctl32/updown.c | 80 +- ...144ccf1df_5.82.2600.2982_none_deadbeef.manifest | 1 + ...4144ccf1df_6.0.2600.2982_none_deadbeef.manifest | 1 + 46 files changed, 12986 insertions(+), 1248 deletions(-)
diff --git a/dll/win32/comctl32/CMakeLists.txt b/dll/win32/comctl32/CMakeLists.txt index 9285001478..4a1151bd98 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 594a227568..f8ba159779 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 <stdarg.h> +#include <string.h> +#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 <vfw.h> +#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); + if (infoPtr->hMMio) + { + ANIMATE_DoStop(infoPtr); + mmioClose(infoPtr->hMMio, 0); + if (infoPtr->hRes) + { + FreeResource(infoPtr->hRes); + infoPtr->hRes = 0; + } + heap_free (infoPtr->lpIndex); infoPtr->lpIndex = NULL; - if (infoPtr->hic) { - fnIC.fnICClose(infoPtr->hic); - infoPtr->hic = 0; - } - Free (infoPtr->inbih); + if (infoPtr->hic) + { + fnIC.fnICClose(infoPtr->hic); + infoPtr->hic = 0; + } + heap_free (infoPtr->inbih); infoPtr->inbih = NULL; - Free (infoPtr->outbih); + heap_free (infoPtr->outbih); infoPtr->outbih = NULL; - Free (infoPtr->indata); + heap_free (infoPtr->indata); infoPtr->indata = NULL; - Free (infoPtr->outdata); + heap_free (infoPtr->outdata); infoPtr->outdata = NULL; - if( infoPtr->hbmPrevFrame ) + if (infoPtr->hbmPrevFrame) { - DeleteObject(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 bdc91840d5..c313aa81e8 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 <stdarg.h> +#include <string.h> +#include <stdlib.h> + +#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/debug.h> 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 */ -#else - WINPROC_BUTTON, /* proc */ -#endif - NB_EXTRA_BYTES, /* extra */ - IDC_ARROW, /* cursor */ - 0 /* brush */ -}; - - -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__ /* r73885 */ +typedef void (*pfThemedPaint)( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused, LPARAM prfFlag);
-#ifdef __REACTOS__ +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);
-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 ); -} +#else +typedef void (*pfThemedPaint)( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, ButtonState drawState, UINT dtflags, BOOL focused);
-#endif /* __REACTOS__ */ +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);
-static inline HFONT get_button_font( HWND hwnd ) -{ - return (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET ); -} +#endif
-static inline void set_button_font( HWND hwnd, HFONT font ) +static const pfThemedPaint btnThemedPaintFunc[MAX_BTN_TYPE] = { - SetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET, (LONG_PTR)font ); -} + 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 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 */
-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; -} + /* "Convert" pushlike buttons to pushbuttons */ + if (style & BS_PUSHLIKE) + style &= ~BS_TYPEMASK;
-static inline LONG get_button_state( HWND hwnd ) -{ - return _GetButtonData(hwnd)->state; -} + if (!(style & BS_MULTILINE)) + dtStyle |= DT_SINGLELINE; + else + dtStyle |= DT_WORDBREAK;
-static inline void set_button_state( HWND hwnd, LONG state ) -{ - _GetButtonData(hwnd)->state = state; -} + 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 */ + }
-static __inline void set_ui_state( HWND hwnd, LONG flags ) -{ - _GetButtonData(hwnd)->ui_state = flags; -} + if (ex_style & WS_EX_RIGHT) dtStyle = DT_RIGHT | (dtStyle & ~(DT_LEFT | DT_CENTER));
-static __inline LONG get_ui_state( HWND hwnd ) -{ - return _GetButtonData(hwnd)->ui_state; -} + /* 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;
-static inline HFONT get_button_font( HWND hwnd ) -{ - return (HFONT)_GetButtonData(hwnd)->font; + return dtStyle; }
-static inline void set_button_font( HWND hwnd, HFONT font ) -{ - _GetButtonData(hwnd)->font = font; -}
-static inline UINT get_button_type( LONG window_style ) +#ifdef __REACTOS__ +BOOL BUTTON_PaintWithTheme(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hParamDC, LPARAM prfFlag) { - return (window_style & BS_TYPEMASK); + DWORD dwStyle; + DWORD dwStyleEx; + DWORD type; + UINT dtFlags; + ButtonState drawState; + pfThemedPaint paint; + + /* Don't draw with themes on a button with BS_ICON or BS_BITMAP */ + if (infoPtr->u.image != 0) + return FALSE; + + dwStyle = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); + type = get_button_type(dwStyle); + + if (type != BS_PUSHBUTTON && type != BS_DEFPUSHBUTTON && (dwStyle & BS_PUSHLIKE)) + type = BS_PUSHBUTTON; + + paint = btnThemedPaintFunc[type]; + if (!paint) + return FALSE; + + dwStyleEx = GetWindowLongW(infoPtr->hwnd, GWL_EXSTYLE); + dtFlags = BUTTON_BStoDT(dwStyle, dwStyleEx); + + 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; - - if (unicode) - flags = DefWindowProcW(hwnd, WM_QUERYUISTATE, 0, 0); - else - flags = DefWindowProcA(hwnd, WM_QUERYUISTATE, 0, 0); - - prevflags = get_ui_state(hwnd); + LONG flags = DefWindowProcW(infoPtr->hwnd, WM_QUERYUISTATE, 0, 0);
- 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 + if ((btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON)) + { + 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 0000000000..712e27276a --- /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 <stdarg.h> +#include <string.h> + +#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 68709f018e..3c3714ac56 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 - * ... 11900 lines suppressed ...