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
, const 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
, 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
)) 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
, id
, 0);
122 * Clip the DC and have the parent draw the item.
124 IntersectClipRect(hdc
,
125 rectEdit
.left
, rectEdit
.top
,
126 rectEdit
.right
, rectEdit
.bottom
);
128 SendMessageW(GetParent (hwnd
), WM_DRAWITEM
, ctlid
, (LPARAM
)&dis
);
131 * Reset the clipping region.
133 SelectClipRgn(hdc
, clipRegion
);
137 static const WCHAR empty_stringW
[] = { 0 };
139 if ( focused
&& !dropped
) {
142 FillRect( hdc
, &rectEdit
, GetSysColorBrush(COLOR_HIGHLIGHT
) );
143 SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
144 SetTextColor( hdc
, GetSysColor( COLOR_HIGHLIGHTTEXT
) );
150 ETO_OPAQUE
| ETO_CLIPPED
,
152 pText
? pText
: empty_stringW
, size
, NULL
);
154 if ( focused
&& !dropped
)
155 DrawFocusRect( hdc
, &rectEdit
);
159 SelectObject(hdc
, hPrevFont
);
161 HeapFree( GetProcessHeap(), 0, pText
);
164 /* paint the combobox */
165 static LRESULT
paint (HTHEME theme
, HWND hwnd
, HDC hParamDC
, ULONG state
)
170 DWORD dwStyle
= GetWindowLongW (hwnd
, GWL_STYLE
);
172 hDC
= (hParamDC
) ? hParamDC
173 : BeginPaint( hwnd
, &ps
);
175 TRACE("hdc=%p\n", hDC
);
177 if( hDC
&& !(state
& STATE_NOREDRAW
) )
182 cbi
.cbSize
= sizeof (cbi
);
183 SendMessageW (hwnd
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)&cbi
);
186 if ((dwStyle
& CBS_DROPDOWNLIST
) != CBS_SIMPLE
)
187 GetClientRect (hwnd
, &frameRect
);
190 CopyRect (&frameRect
, &cbi
.rcItem
);
192 InflateRect(&frameRect
,
193 EDIT_CONTROL_PADDING
+ COMBO_XBORDERSIZE
,
194 EDIT_CONTROL_PADDING
+ COMBO_YBORDERSIZE
);
197 DrawThemeBackground (theme
, hDC
, 0,
198 IsWindowEnabled (hwnd
) ? CBXS_NORMAL
: CBXS_DISABLED
, &frameRect
, NULL
);
201 if (cbi
.stateButton
!= STATE_SYSTEM_INVISIBLE
)
203 if (!IsWindowEnabled (hwnd
))
204 buttonState
= CBXS_DISABLED
;
205 else if (cbi
.stateButton
== STATE_SYSTEM_PRESSED
)
206 buttonState
= CBXS_PRESSED
;
207 else if (state
& STATE_HOT
)
208 buttonState
= CBXS_HOT
;
210 buttonState
= CBXS_NORMAL
;
211 DrawThemeBackground (theme
, hDC
, CP_DROPDOWNBUTTON
, buttonState
,
212 &cbi
.rcButton
, NULL
);
215 /* paint text, if we need to */
216 if ((dwStyle
& CBS_DROPDOWNLIST
) == CBS_DROPDOWNLIST
)
217 paint_text (hwnd
, hDC
, dwStyle
, &cbi
);
227 /**********************************************************************
228 * The combo control subclass window proc.
230 LRESULT CALLBACK
THEMING_ComboSubclassProc (HWND hwnd
, UINT msg
,
231 WPARAM wParam
, LPARAM lParam
,
234 const WCHAR
* themeClass
= WC_COMBOBOXW
;
241 result
= THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
242 OpenThemeData( hwnd
, themeClass
);
246 theme
= GetWindowTheme( hwnd
);
247 CloseThemeData ( theme
);
248 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
250 case WM_THEMECHANGED
:
251 theme
= GetWindowTheme( hwnd
);
252 CloseThemeData ( theme
);
253 OpenThemeData( hwnd
, themeClass
);
256 case WM_SYSCOLORCHANGE
:
257 theme
= GetWindowTheme( hwnd
);
258 if (!theme
) return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
259 /* Do nothing. When themed, a WM_THEMECHANGED will be received, too,
260 * which will do the repaint. */
264 theme
= GetWindowTheme( hwnd
);
265 if (!theme
) return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
266 return paint (theme
, hwnd
, (HDC
)wParam
, dwRefData
);
269 /* Since there doesn't seem to be WM_GETREDRAW, do redraw tracking in
270 * the subclass as well. */
272 dwRefData
&= ~STATE_NOREDRAW
;
274 dwRefData
|= STATE_NOREDRAW
;
275 THEMING_SetSubclassData (hwnd
, dwRefData
);
276 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
280 /* Dropdown button hot-tracking */
284 pt
.x
= (short)LOWORD(lParam
);
285 pt
.y
= (short)HIWORD(lParam
);
286 cbi
.cbSize
= sizeof (cbi
);
287 SendMessageW (hwnd
, CB_GETCOMBOBOXINFO
, 0, (LPARAM
)&cbi
);
289 if (cbi
.stateButton
!= STATE_SYSTEM_INVISIBLE
)
291 if (PtInRect (&cbi
.rcButton
, pt
))
293 if (!(dwRefData
& STATE_HOT
))
295 dwRefData
|= STATE_HOT
;
296 THEMING_SetSubclassData (hwnd
, dwRefData
);
297 RedrawWindow (hwnd
, &cbi
.rcButton
, 0,
298 RDW_INVALIDATE
| RDW_UPDATENOW
);
303 if (dwRefData
& STATE_HOT
)
305 dwRefData
&= ~STATE_HOT
;
306 THEMING_SetSubclassData (hwnd
, dwRefData
);
307 RedrawWindow (hwnd
, &cbi
.rcButton
, 0,
308 RDW_INVALIDATE
| RDW_UPDATENOW
);
313 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);
317 return THEMING_CallOriginalClass (hwnd
, msg
, wParam
, lParam
);