blob: c9e333dd7dd3d7c024a36ad62c80533bf34f2953 [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
DRCc75dc442010-05-20 07:44:49 +00002 * Copyright (C) 2010 D. R. Commander. All Rights Reserved.
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20// -=- Dialog.cxx
21
22// Base-class for any Dialog classes we might require
23
24#include <rfb_win32/Dialog.h>
25#include <rfb_win32/TCharArray.h>
26#include <rfb/LogWriter.h>
27#include <rdr/Exception.h>
28#include <rfb_win32/Win32Util.h>
29
30#ifdef _DIALOG_CAPTURE
31#ifdef PropSheet_IndexToId
32#include <rfb_win32/DeviceFrameBuffer.h>
33#include <extra/LoadBMP.cxx>
34#else
35#undef _DIALOG_CAPTURE
36#pragma message(" NOTE: Not building Dialog Capture support.")
37#endif
38#endif
39
40using namespace rfb;
41using namespace rfb::win32;
42
43static LogWriter dlog("Dialog");
44static LogWriter plog("PropSheet");
45
46
47Dialog::Dialog(HINSTANCE inst_)
Peter Åstranddc30f8e2008-12-10 10:20:54 +000048: inst(inst_), handle(0), alreadyShowing(false)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000049{
50}
51
52Dialog::~Dialog()
53{
54}
55
56
57bool Dialog::showDialog(const TCHAR* resource, HWND owner)
58{
59 if (alreadyShowing) return false;
60 handle = 0;
61 alreadyShowing = true;
62 INT_PTR result = DialogBoxParam(inst, resource, owner,
63 staticDialogProc, (LPARAM)this);
64 if (result<0)
65 throw rdr::SystemException("DialogBoxParam failed", GetLastError());
66 alreadyShowing = false;
67 return (result == 1);
68}
69
70
71bool Dialog::isItemChecked(int id) {
72 return SendMessage(GetDlgItem(handle, id), BM_GETCHECK, 0, 0) == BST_CHECKED;
73}
74int Dialog::getItemInt(int id) {
75 BOOL trans;
76 int result = GetDlgItemInt(handle, id, &trans, TRUE);
77 if (!trans)
78 throw rdr::Exception("unable to read dialog Int");
79 return result;
80}
81TCHAR* Dialog::getItemString(int id) {
82 TCharArray tmp(256);
83 if (!GetDlgItemText(handle, id, tmp.buf, 256))
84 tmp.buf[0] = 0;
85 return tmp.takeBuf();
86}
87
88void Dialog::setItemChecked(int id, bool state) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000089 SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0);
90}
91void Dialog::setItemInt(int id, int value) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000092 SetDlgItemInt(handle, id, value, TRUE);
93}
94void Dialog::setItemString(int id, const TCHAR* s) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000095 SetDlgItemText(handle, id, s);
96}
97
98
99void Dialog::enableItem(int id, bool state) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000100 EnableWindow(GetDlgItem(handle, id), state);
101}
102
103
104
105
DRCc75dc442010-05-20 07:44:49 +0000106INT_PTR CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000107 WPARAM wParam, LPARAM lParam)
108{
109 if (msg == WM_INITDIALOG)
DRCc75dc442010-05-20 07:44:49 +0000110 SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000111
DRCc75dc442010-05-20 07:44:49 +0000112 LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000113 if (!self) return FALSE;
114
115 return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam);
116}
117
118BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
119{
120 switch (msg) {
121
122 case WM_INITDIALOG:
123 handle = hwnd;
124 initDialog();
125 return TRUE;
126
127 case WM_COMMAND:
128 switch (LOWORD(wParam)) {
129 case IDOK:
130 if (onOk()) {
131 EndDialog(hwnd, 1);
132 return TRUE;
133 }
134 return FALSE;
135 case IDCANCEL:
136 EndDialog(hwnd, 0);
137 return TRUE;
138 default:
139 return onCommand(LOWORD(wParam), HIWORD(wParam));
140 };
141
142 case WM_HELP:
143 return onHelp(((HELPINFO*)lParam)->iCtrlId);
144
145 }
146
147 return FALSE;
148}
149
150
151PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) {
152 page.dwSize = sizeof(page);
153 page.dwFlags = 0; // PSP_USECALLBACK;
154 page.hInstance = inst;
155 page.pszTemplate = id;
156 page.pfnDlgProc = staticPageProc;
157 page.lParam = (LPARAM)this;
158 page.pfnCallback = 0; // staticPageProc;
159}
160
161PropSheetPage::~PropSheetPage() {
162}
163
164
DRCc75dc442010-05-20 07:44:49 +0000165INT_PTR CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000166 WPARAM wParam, LPARAM lParam)
167{
168 if (msg == WM_INITDIALOG)
DRCc75dc442010-05-20 07:44:49 +0000169 SetWindowLongPtr(hwnd, GWLP_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000170
DRCc75dc442010-05-20 07:44:49 +0000171 LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000172 if (!self) return FALSE;
173
174 return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam);
175}
176
177BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
178{
179 switch (msg) {
180
181 case WM_INITDIALOG:
182 handle = hwnd;
183 initDialog();
184 return TRUE;
185
186 case WM_NOTIFY:
187 switch (((NMHDR*)lParam)->code) {
188 case PSN_APPLY:
189 onOk();
190 return FALSE;
191 };
192 return FALSE;
193
194 case WM_COMMAND:
195 return onCommand(LOWORD(wParam), HIWORD(wParam));
196
197 case WM_HELP:
198 return onHelp(((HELPINFO*)lParam)->iCtrlId);
199
200 }
201
202 return FALSE;
203}
204
205
206PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list<PropSheetPage*> pages_, HICON icon_)
Peter Åstranddc30f8e2008-12-10 10:20:54 +0000207: icon(icon_), pages(pages_), inst(inst_), title(tstrDup(title_)), handle(0), alreadyShowing(0) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000208}
209
210PropSheet::~PropSheet() {
211}
212
213
214// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure...
215struct DLGTEMPLATEEX {
216 WORD dlgVer;
217 WORD signature;
218 DWORD helpID;
219 DWORD exStyle;
220 DWORD style;
221 WORD cDlgItems;
222 short x;
223 short y;
224 short cx;
225 short cy;
226};
227
228static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) {
229 if (message == PSCB_PRECREATE) {
230 // Remove the context-help style, to remove the titlebar ? button
231 // *** Nasty hack to cope with new & old dialog template formats...
232 if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff)
233 ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP;
234 else
235 ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP;
236 }
237 return TRUE;
238}
239
240
241bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
242 if (alreadyShowing) return false;
243 alreadyShowing = true;
244 int count = pages.size();
245
246 HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count];
247 try {
248 // Create the PropertSheet page GDI objects.
249 std::list<PropSheetPage*>::iterator pspi;
250 int i = 0;
251 for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
252 hpages[i] = CreatePropertySheetPage(&((*pspi)->page));
253 (*pspi)->setPropSheet(this);
254 i++;
255 }
256
257 // Initialise and create the PropertySheet itself
258 PROPSHEETHEADER header;
Peter Åstrandcf75be12008-12-09 10:50:40 +0000259 header.dwSize = sizeof(PROPSHEETHEADER); // Requires comctl32.dll 4.71 or greater, ie IE 4 or later
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000260 header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK);
261 header.pfnCallback = removeCtxtHelp;
262 header.hwndParent = owner;
263 header.hInstance = inst;
264 header.pszCaption = title.buf;
265 header.nPages = count;
266 header.nStartPage = 0;
267 header.phpage = hpages;
268 if (icon) {
269 header.hIcon = icon;
270 header.dwFlags |= PSH_USEHICON;
271 }
272
273 handle = (HWND)PropertySheet(&header);
274 if ((handle == 0) || (handle == (HWND)-1))
275 throw rdr::SystemException("PropertySheet failed", GetLastError());
276 centerWindow(handle, owner);
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100277 plog.info("created %p", handle);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000278
279#ifdef _DIALOG_CAPTURE
280 if (capture) {
281 plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
282 char* tmpdir = getenv("TEMP");
283 HDC dc = GetWindowDC(handle);
284 DeviceFrameBuffer fb(dc);
285 int i=0;
286 while (true) {
287 int id = PropSheet_IndexToId(handle, i);
288 if (!id) break;
289 PropSheet_SetCurSelByID(handle, id);
290 MSG msg;
291 while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) {
292 if (!PropSheet_IsDialogMessage(handle, &msg))
293 DispatchMessage(&msg);
294 }
295 fb.grabRect(fb.getRect());
296 TCHAR title[128];
297 if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title)))
298 _stprintf(title, _T("capture%d"), i);
299 CharArray pageTitle(strDup(title));
300 for (int j=0; j<strlen(pageTitle.buf); j++) {
301 if (pageTitle.buf[j] == '/' || pageTitle.buf[j] == '\\' || pageTitle.buf[j] == ':')
302 pageTitle.buf[j] = '-';
303 }
304 char filename[256];
305 sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf);
306 vlog.debug("writing to %s", filename);
307 saveBMP(filename, &fb);
308 i++;
309 }
310 ReleaseDC(handle, dc);
311 } else {
312#endif
313 try {
314 if (owner)
315 EnableWindow(owner, FALSE);
316 // Run the PropertySheet
317 MSG msg;
318 while (GetMessage(&msg, 0, 0, 0)) {
319 if (!PropSheet_IsDialogMessage(handle, &msg))
320 DispatchMessage(&msg);
321 if (!PropSheet_GetCurrentPageHwnd(handle))
322 break;
323 }
324 if (owner)
325 EnableWindow(owner, TRUE);
326 } catch (...) {
327 if (owner)
328 EnableWindow(owner, TRUE);
329 throw;
330 }
331#ifdef _DIALOG_CAPTURE
332 }
333#endif
334
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100335 plog.info("finished %p", handle);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000336
337 DestroyWindow(handle);
338 handle = 0;
339 alreadyShowing = false;
340
341 // Clear up the pages' GDI objects
342 for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
343 (*pspi)->setPropSheet(0);
344 delete [] hpages; hpages = 0;
345
346 return true;
Pierre Ossman8ee522a2018-05-29 15:50:08 +0200347 } catch (rdr::Exception&) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000348 alreadyShowing = false;
349
350 std::list<PropSheetPage*>::iterator pspi;
351 for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
352 (*pspi)->setPropSheet(0);
353 delete [] hpages; hpages = 0;
354
355 throw;
356 }
357}
358
359void PropSheet::reInitPages() {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000360 std::list<PropSheetPage*>::iterator pspi;
361 for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
362 if ((*pspi)->handle)
363 (*pspi)->initDialog();
364 }
365}
366
367bool PropSheet::commitPages() {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000368 bool result = true;
369 std::list<PropSheetPage*>::iterator pspi;
370 for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
371 if ((*pspi)->handle)
372 result = result && (*pspi)->onOk();
373 }
374 return result;
375}
376
377
378void PropSheetPage::setChanged(bool changed) {
379 if (propSheet) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000380 if (changed)
381 PropSheet_Changed(propSheet->handle, handle);
382 else
383 PropSheet_UnChanged(propSheet->handle, handle);
384 }
385}