Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentheme_button.c
Go to the documentation of this file.
00001 /* 00002 * Theming - Button control 00003 * 00004 * Copyright (c) 2008 by Reece H. Dunn 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00019 * 00020 */ 00021 00022 #include <stdarg.h> 00023 #include <string.h> 00024 #include <stdlib.h> 00025 00026 #include "windef.h" 00027 #include "winbase.h" 00028 #include "wingdi.h" 00029 #include "winuser.h" 00030 #include "uxtheme.h" 00031 #include "vssym32.h" 00032 #include "comctl32.h" 00033 00034 #define BUTTON_TYPE 0x0f /* bit mask for the available button types */ 00035 00036 /* These are indices into a states array to determine the theme state for a given theme part. */ 00037 typedef enum 00038 { 00039 STATE_NORMAL, 00040 STATE_DISABLED, 00041 STATE_HOT, 00042 STATE_PRESSED, 00043 STATE_DEFAULTED 00044 } ButtonState; 00045 00046 typedef void (*pfThemedPaint)(HTHEME theme, HWND hwnd, HDC hdc, ButtonState drawState, UINT dtFlags); 00047 00048 static UINT get_drawtext_flags(DWORD style, DWORD ex_style) 00049 { 00050 UINT flags = 0; 00051 00052 if (style & BS_PUSHLIKE) 00053 style &= ~BUTTON_TYPE; 00054 00055 if (!(style & BS_MULTILINE)) 00056 flags |= DT_SINGLELINE; 00057 else 00058 flags |= DT_WORDBREAK; 00059 00060 switch (style & BS_CENTER) 00061 { 00062 case BS_LEFT: flags |= DT_LEFT; break; 00063 case BS_RIGHT: flags |= DT_RIGHT; break; 00064 case BS_CENTER: flags |= DT_CENTER; break; 00065 default: 00066 flags |= ((style & BUTTON_TYPE) <= BS_DEFPUSHBUTTON) 00067 ? DT_CENTER : DT_LEFT; 00068 } 00069 00070 if (ex_style & WS_EX_RIGHT) 00071 flags = DT_RIGHT | (flags & ~(DT_LEFT | DT_CENTER)); 00072 00073 if ((style & BUTTON_TYPE) != BS_GROUPBOX) 00074 { 00075 switch (style & BS_VCENTER) 00076 { 00077 case BS_TOP: flags |= DT_TOP; break; 00078 case BS_BOTTOM: flags |= DT_BOTTOM; break; 00079 case BS_VCENTER: /* fall through */ 00080 default: flags |= DT_VCENTER; break; 00081 } 00082 } 00083 else 00084 /* GroupBox's text is always single line and is top aligned. */ 00085 flags |= DT_SINGLELINE | DT_TOP; 00086 00087 return flags; 00088 } 00089 00090 static inline WCHAR *get_button_text(HWND hwnd) 00091 { 00092 INT len = 512; 00093 WCHAR *text; 00094 text = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); 00095 if (text) InternalGetWindowText(hwnd, text, len + 1); 00096 return text; 00097 } 00098 00099 static void PB_draw(HTHEME theme, HWND hwnd, HDC hDC, ButtonState drawState, UINT dtFlags) 00100 { 00101 static const int states[] = { PBS_NORMAL, PBS_DISABLED, PBS_HOT, PBS_PRESSED, PBS_DEFAULTED }; 00102 00103 RECT bgRect, textRect; 00104 HFONT font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0); 00105 HFONT hPrevFont = font ? SelectObject(hDC, font) : NULL; 00106 int state = states[ drawState ]; 00107 WCHAR *text = get_button_text(hwnd); 00108 00109 GetClientRect(hwnd, &bgRect); 00110 GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &bgRect, &textRect); 00111 00112 if (IsThemeBackgroundPartiallyTransparent(theme, BP_PUSHBUTTON, state)) 00113 DrawThemeParentBackground(hwnd, hDC, NULL); 00114 DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &bgRect, NULL); 00115 if (text) 00116 { 00117 DrawThemeText(theme, hDC, BP_PUSHBUTTON, state, text, lstrlenW(text), dtFlags, 0, &textRect); 00118 HeapFree(GetProcessHeap(), 0, text); 00119 } 00120 00121 if (hPrevFont) SelectObject(hDC, hPrevFont); 00122 } 00123 00124 static void CB_draw(HTHEME theme, HWND hwnd, HDC hDC, ButtonState drawState, UINT dtFlags) 00125 { 00126 static const int cb_states[3][5] = 00127 { 00128 { CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDDISABLED, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED, CBS_UNCHECKEDNORMAL }, 00129 { CBS_CHECKEDNORMAL, CBS_CHECKEDDISABLED, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED, CBS_CHECKEDNORMAL }, 00130 { CBS_MIXEDNORMAL, CBS_MIXEDDISABLED, CBS_MIXEDHOT, CBS_MIXEDPRESSED, CBS_MIXEDNORMAL } 00131 }; 00132 00133 static const int rb_states[2][5] = 00134 { 00135 { RBS_UNCHECKEDNORMAL, RBS_UNCHECKEDDISABLED, RBS_UNCHECKEDHOT, RBS_UNCHECKEDPRESSED, RBS_UNCHECKEDNORMAL }, 00136 { RBS_CHECKEDNORMAL, RBS_CHECKEDDISABLED, RBS_CHECKEDHOT, RBS_CHECKEDPRESSED, RBS_CHECKEDNORMAL } 00137 }; 00138 00139 static const int cb_size = 13; 00140 00141 RECT bgRect, textRect; 00142 HFONT font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0); 00143 HFONT hPrevFont = font ? SelectObject(hDC, font) : NULL; 00144 LRESULT checkState = SendMessageW(hwnd, BM_GETCHECK, 0, 0); 00145 DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 00146 int part = ((dwStyle & BUTTON_TYPE) == BS_RADIOBUTTON) || ((dwStyle & BUTTON_TYPE) == BS_AUTORADIOBUTTON) 00147 ? BP_RADIOBUTTON 00148 : BP_CHECKBOX; 00149 int state = (part == BP_CHECKBOX) 00150 ? cb_states[ checkState ][ drawState ] 00151 : rb_states[ checkState ][ drawState ]; 00152 WCHAR *text = get_button_text(hwnd); 00153 00154 GetClientRect(hwnd, &bgRect); 00155 GetThemeBackgroundContentRect(theme, hDC, part, state, &bgRect, &textRect); 00156 00157 if (dtFlags & DT_SINGLELINE) /* Center the checkbox / radio button to the text. */ 00158 bgRect.top = bgRect.top + (textRect.bottom - textRect.top - cb_size) / 2; 00159 00160 /* adjust for the check/radio marker */ 00161 bgRect.bottom = bgRect.top + cb_size; 00162 bgRect.right = bgRect.left + cb_size; 00163 textRect.left = bgRect.right + 6; 00164 00165 if (IsThemeBackgroundPartiallyTransparent(theme, part, state)) 00166 DrawThemeParentBackground(hwnd, hDC, NULL); 00167 DrawThemeBackground(theme, hDC, part, state, &bgRect, NULL); 00168 if (text) 00169 { 00170 DrawThemeText(theme, hDC, part, state, text, lstrlenW(text), dtFlags, 0, &textRect); 00171 HeapFree(GetProcessHeap(), 0, text); 00172 } 00173 00174 if (hPrevFont) SelectObject(hDC, hPrevFont); 00175 } 00176 00177 static void GB_draw(HTHEME theme, HWND hwnd, HDC hDC, ButtonState drawState, UINT dtFlags) 00178 { 00179 static const int states[] = { GBS_NORMAL, GBS_DISABLED, GBS_NORMAL, GBS_NORMAL, GBS_NORMAL }; 00180 00181 RECT bgRect, textRect, contentRect; 00182 HFONT font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0); 00183 HFONT hPrevFont = font ? SelectObject(hDC, font) : NULL; 00184 int state = states[ drawState ]; 00185 WCHAR *text = get_button_text(hwnd); 00186 00187 GetClientRect(hwnd, &bgRect); 00188 textRect = bgRect; 00189 00190 if (text) 00191 { 00192 SIZE textExtent; 00193 GetTextExtentPoint32W(hDC, text, lstrlenW(text), &textExtent); 00194 bgRect.top += (textExtent.cy / 2); 00195 textRect.left += 10; 00196 textRect.bottom = textRect.top + textExtent.cy; 00197 textRect.right = textRect.left + textExtent.cx + 4; 00198 00199 ExcludeClipRect(hDC, textRect.left, textRect.top, textRect.right, textRect.bottom); 00200 } 00201 00202 GetThemeBackgroundContentRect(theme, hDC, BP_GROUPBOX, state, &bgRect, &contentRect); 00203 ExcludeClipRect(hDC, contentRect.left, contentRect.top, contentRect.right, contentRect.bottom); 00204 00205 if (IsThemeBackgroundPartiallyTransparent(theme, BP_GROUPBOX, state)) 00206 DrawThemeParentBackground(hwnd, hDC, NULL); 00207 DrawThemeBackground(theme, hDC, BP_GROUPBOX, state, &bgRect, NULL); 00208 00209 SelectClipRgn(hDC, NULL); 00210 00211 if (text) 00212 { 00213 textRect.left += 2; 00214 textRect.right -= 2; 00215 DrawThemeText(theme, hDC, BP_GROUPBOX, state, text, lstrlenW(text), 0, 0, &textRect); 00216 HeapFree(GetProcessHeap(), 0, text); 00217 } 00218 00219 if (hPrevFont) SelectObject(hDC, hPrevFont); 00220 } 00221 00222 static const pfThemedPaint btnThemedPaintFunc[BUTTON_TYPE + 1] = 00223 { 00224 PB_draw, /* BS_PUSHBUTTON */ 00225 PB_draw, /* BS_DEFPUSHBUTTON */ 00226 CB_draw, /* BS_CHECKBOX */ 00227 CB_draw, /* BS_AUTOCHECKBOX */ 00228 CB_draw, /* BS_RADIOBUTTON */ 00229 CB_draw, /* BS_3STATE */ 00230 CB_draw, /* BS_AUTO3STATE */ 00231 GB_draw, /* BS_GROUPBOX */ 00232 NULL, /* BS_USERBUTTON */ 00233 CB_draw, /* BS_AUTORADIOBUTTON */ 00234 NULL, /* Not defined */ 00235 NULL, /* BS_OWNERDRAW */ 00236 NULL, /* Not defined */ 00237 NULL, /* Not defined */ 00238 NULL, /* Not defined */ 00239 NULL, /* Not defined */ 00240 }; 00241 00242 static BOOL BUTTON_Paint(HTHEME theme, HWND hwnd, HDC hParamDC) 00243 { 00244 PAINTSTRUCT ps; 00245 HDC hDC; 00246 DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 00247 DWORD dwStyleEx = GetWindowLongW(hwnd, GWL_EXSTYLE); 00248 UINT dtFlags = get_drawtext_flags(dwStyle, dwStyleEx); 00249 int state = (int)SendMessageW(hwnd, BM_GETSTATE, 0, 0); 00250 ButtonState drawState; 00251 pfThemedPaint paint = btnThemedPaintFunc[ dwStyle & BUTTON_TYPE ]; 00252 00253 if(!paint) 00254 return FALSE; 00255 00256 if(IsWindowEnabled(hwnd)) 00257 { 00258 if(state & BST_PUSHED) drawState = STATE_PRESSED; 00259 else if(state & BST_HOT) drawState = STATE_HOT; 00260 else if(state & BST_FOCUS) drawState = STATE_DEFAULTED; 00261 else drawState = STATE_NORMAL; 00262 } 00263 else drawState = STATE_DISABLED; 00264 00265 hDC = hParamDC ? hParamDC : BeginPaint(hwnd, &ps); 00266 paint(theme, hwnd, hDC, drawState, dtFlags); 00267 if (!hParamDC) EndPaint(hwnd, &ps); 00268 return TRUE; 00269 } 00270 00271 /********************************************************************** 00272 * The button control subclass window proc. 00273 */ 00274 LRESULT CALLBACK THEMING_ButtonSubclassProc(HWND hwnd, UINT msg, 00275 WPARAM wParam, LPARAM lParam, 00276 ULONG_PTR dwRefData) 00277 { 00278 const WCHAR* themeClass = WC_BUTTONW; 00279 HTHEME theme; 00280 LRESULT result; 00281 00282 switch (msg) 00283 { 00284 case WM_CREATE: 00285 result = THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); 00286 OpenThemeData(hwnd, themeClass); 00287 return result; 00288 00289 case WM_DESTROY: 00290 theme = GetWindowTheme(hwnd); 00291 CloseThemeData (theme); 00292 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); 00293 00294 case WM_THEMECHANGED: 00295 theme = GetWindowTheme(hwnd); 00296 CloseThemeData (theme); 00297 OpenThemeData(hwnd, themeClass); 00298 break; 00299 00300 case WM_SYSCOLORCHANGE: 00301 theme = GetWindowTheme(hwnd); 00302 if (!theme) return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); 00303 /* Do nothing. When themed, a WM_THEMECHANGED will be received, too, 00304 * which will do the repaint. */ 00305 break; 00306 00307 case WM_PAINT: 00308 theme = GetWindowTheme(hwnd); 00309 if (theme && BUTTON_Paint(theme, hwnd, (HDC)wParam)) 00310 return 0; 00311 else 00312 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); 00313 00314 case WM_ENABLE: 00315 theme = GetWindowTheme(hwnd); 00316 if (theme) RedrawWindow(hwnd, NULL, NULL, 00317 RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); 00318 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); 00319 00320 case WM_MOUSEMOVE: 00321 { 00322 TRACKMOUSEEVENT mouse_event; 00323 mouse_event.cbSize = sizeof(TRACKMOUSEEVENT); 00324 mouse_event.dwFlags = TME_QUERY; 00325 if(!TrackMouseEvent(&mouse_event) || !(mouse_event.dwFlags&(TME_HOVER|TME_LEAVE))) 00326 { 00327 mouse_event.dwFlags = TME_HOVER|TME_LEAVE; 00328 mouse_event.hwndTrack = hwnd; 00329 mouse_event.dwHoverTime = 1; 00330 TrackMouseEvent(&mouse_event); 00331 } 00332 break; 00333 } 00334 00335 case WM_MOUSEHOVER: 00336 { 00337 int state = (int)SendMessageW(hwnd, BM_GETSTATE, 0, 0); 00338 SetWindowLongW(hwnd, 0, state|BST_HOT); 00339 InvalidateRect(hwnd, NULL, FALSE); 00340 break; 00341 } 00342 00343 case WM_MOUSELEAVE: 00344 { 00345 int state = (int)SendMessageW(hwnd, BM_GETSTATE, 0, 0); 00346 SetWindowLongW(hwnd, 0, state&(~BST_HOT)); 00347 InvalidateRect(hwnd, NULL, FALSE); 00348 break; 00349 } 00350 00351 default: 00352 /* Call old proc */ 00353 return THEMING_CallOriginalClass(hwnd, msg, wParam, lParam); 00354 } 00355 return 0; 00356 } Generated on Sat May 26 2012 04:21:38 for ReactOS by
1.7.6.1
|