OSDN Git Service

Moved translation support into MUtilities library + make clean-up of temporary files...
[mutilities/MUtilities.git] / src / GUI_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //Win32 API
23 #define WIN32_LEAN_AND_MEAN 1
24 #include <Windows.h>
25
26 //MUtils
27 #include <MUtils/GUI.h>
28 #include <MUtils/OSSupport.h>
29
30 //Internal
31 #include "Utils_Win32.h"
32
33 //Qt
34 #include <QIcon>
35 #include <QApplication>
36 #include <QWidget>
37 #include <QReadWriteLock>
38 #include <QLibrary>
39 #include <Dwmapi.h>
40
41 ///////////////////////////////////////////////////////////////////////////////
42 // THEME SUPPORT
43 ///////////////////////////////////////////////////////////////////////////////
44
45 static QReadWriteLock g_themes_lock;
46 static bool g_themes_initialized = false;
47 static bool g_themes_enabled = false;
48
49 typedef int (WINAPI IsAppThemedFunction)(void);
50
51 bool MUtils::GUI::themes_enabled(void)
52 {
53         QReadLocker readLock(&g_themes_lock);
54
55         if(g_themes_initialized)
56         {
57                 return g_themes_enabled;
58         }
59
60         readLock.unlock();
61         QWriteLocker writeLock(&g_themes_lock);
62
63         if(g_themes_initialized)
64         {
65                 return g_themes_enabled;
66         }
67
68         const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
69         if(osVersion >= MUtils::OS::Version::WINDOWS_WINXP)
70         {
71                 QLibrary uxTheme("UxTheme.dll");
72                 if(uxTheme.load())
73                 {
74                         if(IsAppThemedFunction *const IsAppThemedPtr = (IsAppThemedFunction*) uxTheme.resolve("IsAppThemed"))
75                         {
76                                 g_themes_enabled = IsAppThemedPtr();
77                                 if(!g_themes_enabled)
78                                 {
79                                         qWarning("Theme support is disabled for this process!");
80                                 }
81                         }
82                 }
83         }
84
85         g_themes_initialized = true;
86         return g_themes_enabled;
87 }
88
89 ///////////////////////////////////////////////////////////////////////////////
90 // SYSTEM MENU
91 ///////////////////////////////////////////////////////////////////////////////
92
93 bool MUtils::GUI::sysmenu_append(const QWidget *win, const unsigned int identifier, const QString &text)
94 {
95         bool ok = false;
96         
97         if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
98         {
99                 ok = (AppendMenuW(hMenu, MF_SEPARATOR, 0, 0) == TRUE);
100                 ok = (AppendMenuW(hMenu, MF_STRING, identifier, MUTILS_WCHR(text)) == TRUE);
101         }
102
103         return ok;
104 }
105
106 bool MUtils::GUI::sysmenu_update(const QWidget *win, const unsigned int identifier, const QString &text)
107 {
108         bool ok = false;
109         
110         if(HMENU hMenu = ::GetSystemMenu(win->winId(), FALSE))
111         {
112                 ok = (ModifyMenu(hMenu, identifier, MF_STRING | MF_BYCOMMAND, identifier, MUTILS_WCHR(text)) == TRUE);
113         }
114         return ok;
115 }
116
117 bool MUtils::GUI::sysmenu_check_msg(void *const message, const unsigned int &identifier)
118 {
119         return (((MSG*)message)->message == WM_SYSCOMMAND) && ((((MSG*)message)->wParam & 0xFFF0) == identifier);
120 }
121
122 ///////////////////////////////////////////////////////////////////////////////
123 // CLOSE BUTTON
124 ///////////////////////////////////////////////////////////////////////////////
125
126 bool MUtils::GUI::enable_close_button(const QWidget *win, const bool &bEnable)
127 {
128         bool ok = false;
129
130         if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
131         {
132                 ok = (EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | (bEnable ? MF_ENABLED : MF_GRAYED)) == TRUE);
133         }
134
135         return ok;
136 }
137
138 ///////////////////////////////////////////////////////////////////////////////
139 // BRING WINDOW TO FRONT
140 ///////////////////////////////////////////////////////////////////////////////
141
142 static BOOL CALLBACK bring_process_to_front_helper(HWND hwnd, LPARAM lParam)
143 {
144         DWORD processId = *reinterpret_cast<WORD*>(lParam);
145         DWORD windowProcessId = NULL;
146         GetWindowThreadProcessId(hwnd, &windowProcessId);
147         if(windowProcessId == processId)
148         {
149                 SwitchToThisWindow(hwnd, TRUE);
150                 SetForegroundWindow(hwnd);
151                 return FALSE;
152         }
153
154         return TRUE;
155 }
156
157 bool MUtils::GUI::bring_to_front(const QWidget *window)
158 {
159         bool ret = false;
160         
161         if(window)
162         {
163                 for(int i = 0; (i < 5) && (!ret); i++)
164                 {
165                         ret = (SetForegroundWindow(window->winId()) != FALSE);
166                         SwitchToThisWindow(window->winId(), TRUE);
167                 }
168                 LockSetForegroundWindow(LSFW_LOCK);
169         }
170
171         return ret;
172 }
173
174 bool MUtils::GUI::bring_to_front(const unsigned long pid)
175 {
176         return EnumWindows(bring_process_to_front_helper, reinterpret_cast<LPARAM>(&pid)) == TRUE;
177 }
178
179 ///////////////////////////////////////////////////////////////////////////////
180 // SHEET OF GLASS EFFECT
181 ///////////////////////////////////////////////////////////////////////////////
182
183 static QReadWriteLock g_dwmapi_lock;
184 static QScopedPointer<QLibrary> g_dwmapi_library;
185 static bool g_dwmapi_initialized = false;
186
187 static struct
188 {
189         HRESULT (__stdcall *dwmIsCompositionEnabled)(BOOL *bEnabled);
190         HRESULT (__stdcall *dwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
191         HRESULT (__stdcall *dwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
192 }
193 g_dwmapi_pointers = { NULL, NULL, NULL };
194
195 static void initialize_dwmapi(void)
196 {
197         QReadLocker writeLock(&g_dwmapi_lock);
198
199         //Not initialized yet?
200         if(g_dwmapi_initialized)
201         {
202                 return;
203         }
204         
205         //Reset function pointers
206         g_dwmapi_pointers.dwmIsCompositionEnabled      = NULL;
207         g_dwmapi_pointers.dwmExtendFrameIntoClientArea = NULL;
208         g_dwmapi_pointers.dwmEnableBlurBehindWindow    = NULL;
209                         
210         //Does OS support DWM?
211         const MUtils::OS::Version::os_version_t &osVersion = MUtils::OS::os_version();
212         if(osVersion >= MUtils::OS::Version::WINDOWS_VISTA)
213         {
214                 //Load DWMAPI.DLL
215                 g_dwmapi_library.reset(new QLibrary("dwmapi.dll"));
216                 if(g_dwmapi_library->load())
217                 {
218                         //Initialize function pointers
219                         g_dwmapi_pointers.dwmIsCompositionEnabled      = (HRESULT (__stdcall*)(BOOL*))                       g_dwmapi_library->resolve("DwmIsCompositionEnabled");
220                         g_dwmapi_pointers.dwmExtendFrameIntoClientArea = (HRESULT (__stdcall*)(HWND, const MARGINS*))        g_dwmapi_library->resolve("DwmExtendFrameIntoClientArea");
221                         g_dwmapi_pointers.dwmEnableBlurBehindWindow    = (HRESULT (__stdcall*)(HWND, const DWM_BLURBEHIND*)) g_dwmapi_library->resolve("DwmEnableBlurBehindWindow");
222                 }
223                 else
224                 {
225                         g_dwmapi_library.reset(NULL);
226                         qWarning("Failed to load DWMAPI.DLL on a DWM-enabled system!");
227                 }
228         }
229
230         g_dwmapi_initialized = true;
231 }
232
233 bool MUtils::GUI::sheet_of_glass(QWidget *const window)
234 {
235         QReadLocker readLock(&g_dwmapi_lock);
236
237         //Initialize the DWM API
238         if(!g_dwmapi_initialized)
239         {
240                 readLock.unlock();
241                 initialize_dwmapi();
242                 readLock.relock();
243         }
244
245         //Required functions available?
246         BOOL bCompositionEnabled = FALSE;
247         if(g_dwmapi_pointers.dwmIsCompositionEnabled && g_dwmapi_pointers.dwmExtendFrameIntoClientArea && g_dwmapi_pointers.dwmEnableBlurBehindWindow)
248         {
249                 //Check if composition is currently enabled
250                 if(HRESULT hr = g_dwmapi_pointers.dwmIsCompositionEnabled(&bCompositionEnabled))
251                 {
252                         qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
253                         return false;
254                 }
255         }
256         
257         //All functions available *and* composition enabled?
258         if(!bCompositionEnabled)
259         {
260                 return false;
261         }
262
263         //Enable the "sheet of glass" effect on this window
264         MARGINS margins = {-1, -1, -1, -1};
265         if(HRESULT hr = g_dwmapi_pointers.dwmExtendFrameIntoClientArea(window->winId(), &margins))
266         {
267                 qWarning("DwmExtendFrameIntoClientArea function has failed! (error %d)", hr);
268                 return false;
269         }
270
271         //Create and populate the Blur Behind structure
272         DWM_BLURBEHIND bb;
273         memset(&bb, 0, sizeof(DWM_BLURBEHIND));
274         bb.fEnable = TRUE;
275         bb.dwFlags = DWM_BB_ENABLE;
276         if(HRESULT hr = g_dwmapi_pointers.dwmEnableBlurBehindWindow(window->winId(), &bb))
277         {
278                 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
279                 return false;
280         }
281
282         //Required for Qt
283         window->setAutoFillBackground(false);
284         window->setAttribute(Qt::WA_TranslucentBackground);
285         window->setAttribute(Qt::WA_NoSystemBackground);
286
287         return true;
288 }
289
290 bool MUtils::GUI::sheet_of_glass_update(QWidget *const window)
291 {
292         QReadLocker readLock(&g_dwmapi_lock);
293
294         //Initialize the DWM API
295         if(!g_dwmapi_initialized)
296         {
297                 readLock.unlock();
298                 initialize_dwmapi();
299                 readLock.relock();
300         }
301
302         //Required functions available?
303         BOOL bCompositionEnabled = FALSE;
304         if(g_dwmapi_pointers.dwmIsCompositionEnabled && g_dwmapi_pointers.dwmExtendFrameIntoClientArea && g_dwmapi_pointers.dwmEnableBlurBehindWindow)
305         {
306                 //Check if composition is currently enabled
307                 if(HRESULT hr = g_dwmapi_pointers.dwmIsCompositionEnabled(&bCompositionEnabled))
308                 {
309                         qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
310                         return false;
311                 }
312         }
313         
314         //All functions available *and* composition enabled?
315         if(!bCompositionEnabled)
316         {
317                 return false;
318         }
319
320         //Create and populate the Blur Behind structure
321         DWM_BLURBEHIND bb;
322         memset(&bb, 0, sizeof(DWM_BLURBEHIND));
323         bb.fEnable = TRUE;
324         bb.dwFlags = DWM_BB_ENABLE;
325         if(HRESULT hr = g_dwmapi_pointers.dwmEnableBlurBehindWindow(window->winId(), &bb))
326         {
327                 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
328                 return false;
329         }
330
331         return true;
332 }
333
334 ///////////////////////////////////////////////////////////////////////////////
335 // SYSTEM COLORS
336 ///////////////////////////////////////////////////////////////////////////////
337
338 QColor MUtils::GUI::system_color(const int &color_id)
339 {
340         int nIndex = -1;
341
342         switch(color_id)
343         {
344         case SYSCOLOR_TEXT:
345                 nIndex = COLOR_WINDOWTEXT;              /*Text in windows*/
346                 break;
347         case SYSCOLOR_BACKGROUND:
348                 nIndex = COLOR_WINDOW;                  /*Window background*/
349                 break;
350         case SYSCOLOR_CAPTION:
351                 nIndex = COLOR_CAPTIONTEXT;             /*Text in caption, size box, and scroll bar arrow box*/
352                 break;
353         default:
354                 qWarning("Unknown system color id (%d) specified!", color_id);
355                 nIndex = COLOR_WINDOWTEXT;
356         }
357         
358         const DWORD rgb = GetSysColor(nIndex);
359         QColor color(GetRValue(rgb), GetGValue(rgb), GetBValue(rgb));
360         return color;
361 }