2 * Theming - Combo box control
4 * Copyright (c) 2005 by Frank Richter
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(themingcombo
);
37 /* Subclass-private state flags */
38 #define STATE_NOREDRAW 1
41 /* some constants for metrics, same as in user32 */
42 #define COMBO_XBORDERSIZE 2
43 #define COMBO_YBORDERSIZE 2
44 #define COMBO_EDITBUTTONSPACE 0
45 #define EDIT_CONTROL_PADDING 1
47 /* paint text of combobox, needed for read-only drop downs. */
48 static void paint_text (HWND hwnd
, HDC hdc
, DWORD dwStyle
, COMBOBOXINFO
* cbi
)
52 UINT itemState
= ODS_COMBOBOXEDIT
;
53 HFONT font
= (HFONT
)SendMessageW (hwnd
, WM_GETFONT
, 0, 0);
54 HFONT hPrevFont
= (font
) ? SelectObject(hdc
, font
) : 0;
56 BOOL focused
= GetFocus () == hwnd
;
57 BOOL dropped
= cbi
->stateButton
== STATE_SYSTEM_PRESSED
;
61 /* follow Windows combobox that sends a bunch of text
62 * inquiries to its listbox while processing WM_PAINT. */
64 if( (id
= SendMessageW (cbi
->hwndList
, LB_GETCURSEL
, 0, 0) ) != LB_ERR
)
66 size
= SendMessageW (cbi
->hwndList
, LB_GETTEXTLEN
, id
, 0);
68 FIXME("LB_ERR probably not handled yet\n");
69 if( (pText
= HeapAlloc( GetProcessHeap(), 0, (size
+ 1) * sizeof(WCHAR
))) )
71 /* size from LB_GETTEXTLEN may be too large, from LB_GETTEXT is accurate */
72 size
=SendMessageW (cbi
->hwndList
, LB_GETTEXT
, (WPARAM
)id
, (LPARAM
)pText
);
73 pText
[size
] = '\0'; /* just in case */
77 if( !(dwStyle
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
)) )
81 * Give ourselves some space.
83 CopyRect (&rectEdit
, &cbi
->rcItem
);
84 InflateRect( &rectEdit
, -1, -1 );
86 if(dwStyle
& (CBS_OWNERDRAWFIXED
| CBS_OWNERDRAWVARIABLE
))
90 UINT ctlid
= (UINT
)GetWindowLongPtrW( hwnd
, GWLP_ID
);
92 /* setup state for DRAWITEM message. Owner will highlight */
93 if ( focused
&& !dropped
)
94 itemState
|= ODS_SELECTED
| ODS_FOCUS
;
97 * Save the current clip region.
98 * To retrieve the clip region, we need to create one "dummy"
101 clipRegion
= CreateRectRgnIndirect(&rectEdit
);
103 if (GetClipRgn(hdc
, clipRegion
)!=1)
105 DeleteObject(clipRegion
);
109 if (!IsWindowEnabled(hwnd
) & WS_DISABLED
) itemState
|= ODS_DISABLED
;
111 dis
.CtlType
= ODT_COMBOBOX
;
114 dis
.itemAction
= ODA_DRAWENTIRE
;
116 dis
.itemState
= itemState
;
118 dis
.rcItem
= rectEdit
;
119 dis
.itemData
= SendMessageW(cbi
->hwndList
, LB_GETITEMDATA
,
123 * Clip the DC and have the parent draw the item.
125 IntersectClipRect(hdc
,
126 rectEdit
.left
, rectEdit
.top
,
127 rectEdit
.right
, rectEdit
.bottom
);
129 SendMessageW(GetParent (hwnd
), WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
132 * Reset the clipping region.
134 SelectClipRgn(hdc
, clipRegion
);
138 static const WCHAR empty_stringW
[] = { 0 };
140 if ( focused
&& !dropped
) {
143 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
144 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
145 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
151 ETO_OPAQUE
| ETO_CLIPPED
,
153 pText
? pText
: empty_stringW
, size
, NULL
);
155 if ( focused
&& !dropped
)
156 DrawFocusRect( hdc
, &rectEdit
);
160 SelectObject(hdc
, hPrevFont
);
162 HeapFree( GetProcessHeap(), 0, pText
);
165 /* paint the combobox */
166 static LRESULT
paint (HTHEME theme
, HWND hwnd
, HDC hParamDC
, ULONG state
)
171 DWORD dwStyle
= GetWindowLongW (hwnd
, GWL_STYLE
);
173 hDC
= (hParamDC
) ? hParamDC
174 : BeginPaint( hwnd
, &ps
);
176 TRACE("hdc=%p\n", hDC
);
178 if( hDC
&& !(state
& STATE_NOREDRAW
) )
183 cbi
.cbSize
= sizeof (cbi
);
184 SendMessageW (hwnd
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)&cbi
);
187 if ((dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
188 GetClientRect (hwnd
, &frameRect
);
191 CopyRect (&frameRect
, &cbi
.rcItem
);
193 InflateRect(&frameRect
,
194 EDIT_CONTROL_PADDING
+ COMBO_XBORDERSIZE
,
195 EDIT_CONTROL_PADDING
+ COMBO_YBORDERSIZE
);
198 DrawThemeBackground (theme
, hDC
, 0,
199 IsWindowEnabled (hwnd
) ? CBXS_NORMAL
: CBXS_DISABLED
, &frameRect
, NULL
);
202 if (cbi
.stateButton
!= STATE_SYSTEM_INVISIBLE
)
204 if (!IsWindowEnabled (hwnd
))
205 buttonState
= CBXS_DISABLED
;
206 else if (cbi
.stateButton
== STATE_SYSTEM_PRESSED
)
207 buttonState
= CBXS_PRESSED
;
208 else if (state
& STATE_HOT
)
209 buttonState
= CBXS_HOT
;
211 buttonState
= CBXS_NORMAL
;
212 DrawThemeBackground (theme
, hDC
, CP_DROPDOWNBUTTON
, buttonState
,
213 &cbi
.rcButton
, NULL
);
216 /* paint text, if we need to */
217 if ((dwStyle
& CBS_DROPDOWNLIST
) == CBS_DROPDOWNLIST
)
218 paint_text (hwnd
, hDC
, dwStyle
, &cbi
);
228 /**********************************************************************
229 * The combo control subclass window proc.
231 LRESULT CALLBACK
THEMING_ComboSubclassProc (HWND hwnd
, UINT msg
,
232 WPARAM wParam
, LPARAM lParam
,
235 const WCHAR
* themeClass
= WC_COMBOBOXW
;
242 result
= THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
243 OpenThemeData( hwnd
, themeClass
);
247 theme
= GetWindowTheme( hwnd
);
248 CloseThemeData ( theme
);
249 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
251 case WM_THEMECHANGED
:
252 theme
= GetWindowTheme( hwnd
);
253 CloseThemeData ( theme
);
254 OpenThemeData( hwnd
, themeClass
);
257 case WM_SYSCOLORCHANGE
:
258 theme
= GetWindowTheme( hwnd
);
259 if (!theme
) return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
260 /* Do nothing. When themed, a WM_THEMECHANGED will be received, too,
261 * which will do the repaint. */
265 theme
= GetWindowTheme( hwnd
);
266 if (!theme
) return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
267 return paint (theme
, hwnd
, (HDC
)wParam
, dwRefData
);
270 /* Since there doesn't seem to be WM_GETREDRAW, do redraw tracking in
271 * the subclass as well. */
273 dwRefData
&= ~STATE_NOREDRAW
;
275 dwRefData
|= STATE_NOREDRAW
;
276 THEMING_SetSubclassData (hwnd
, dwRefData
);
277 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
281 /* Dropdown button hot-tracking */
285 pt
.x
= (short)LOWORD(lParam
);
286 pt
.y
= (short)HIWORD(lParam
);
287 cbi
.cbSize
= sizeof (cbi
);
288 SendMessageW (hwnd
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)&cbi
);
290 if (cbi
.stateButton
!= STATE_SYSTEM_INVISIBLE
)
292 if (PtInRect (&cbi
.rcButton
, pt
))
294 if (!(dwRefData
& STATE_HOT
))
296 dwRefData
|= STATE_HOT
;
297 THEMING_SetSubclassData (hwnd
, dwRefData
);
298 RedrawWindow (hwnd
, &cbi
.rcButton
, 0,
299 RDW_INVALIDATE
| RDW_UPDATENOW
);
304 if (dwRefData
& STATE_HOT
)
306 dwRefData
&= ~STATE_HOT
;
307 THEMING_SetSubclassData (hwnd
, dwRefData
);
308 RedrawWindow (hwnd
, &cbi
.rcButton
, 0,
309 RDW_INVALIDATE
| RDW_UPDATENOW
);
314 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
318 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);