blob: 70a5fb5694be8542e016bc7f7c92e194cc982440 [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) {
89 dlog.debug("bool[%d]=%d", id, (int)state);
90 SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0);
91}
92void Dialog::setItemInt(int id, int value) {
93 dlog.debug("int[%d]=%d", id, value);
94 SetDlgItemInt(handle, id, value, TRUE);
95}
96void Dialog::setItemString(int id, const TCHAR* s) {
97 dlog.debug("string[%d]=%s", id, (const char*)CStr(s));
98 SetDlgItemText(handle, id, s);
99}
100
101
102void Dialog::enableItem(int id, bool state) {
103 dlog.debug("enable[%d]=%d", id, (int)state);
104 EnableWindow(GetDlgItem(handle, id), state);
105}
106
107
108
109
DRCc75dc442010-05-20 07:44:49 +0000110INT_PTR CALLBACK Dialog::staticDialogProc(HWND hwnd, UINT msg,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000111 WPARAM wParam, LPARAM lParam)
112{
113 if (msg == WM_INITDIALOG)
DRCc75dc442010-05-20 07:44:49 +0000114 SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000115
DRCc75dc442010-05-20 07:44:49 +0000116 LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000117 if (!self) return FALSE;
118
119 return ((Dialog*)self)->dialogProc(hwnd, msg, wParam, lParam);
120}
121
122BOOL Dialog::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
123{
124 switch (msg) {
125
126 case WM_INITDIALOG:
127 handle = hwnd;
128 initDialog();
129 return TRUE;
130
131 case WM_COMMAND:
132 switch (LOWORD(wParam)) {
133 case IDOK:
134 if (onOk()) {
135 EndDialog(hwnd, 1);
136 return TRUE;
137 }
138 return FALSE;
139 case IDCANCEL:
140 EndDialog(hwnd, 0);
141 return TRUE;
142 default:
143 return onCommand(LOWORD(wParam), HIWORD(wParam));
144 };
145
146 case WM_HELP:
147 return onHelp(((HELPINFO*)lParam)->iCtrlId);
148
149 }
150
151 return FALSE;
152}
153
154
155PropSheetPage::PropSheetPage(HINSTANCE inst, const TCHAR* id) : Dialog(inst), propSheet(0) {
156 page.dwSize = sizeof(page);
157 page.dwFlags = 0; // PSP_USECALLBACK;
158 page.hInstance = inst;
159 page.pszTemplate = id;
160 page.pfnDlgProc = staticPageProc;
161 page.lParam = (LPARAM)this;
162 page.pfnCallback = 0; // staticPageProc;
163}
164
165PropSheetPage::~PropSheetPage() {
166}
167
168
DRCc75dc442010-05-20 07:44:49 +0000169INT_PTR CALLBACK PropSheetPage::staticPageProc(HWND hwnd, UINT msg,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000170 WPARAM wParam, LPARAM lParam)
171{
172 if (msg == WM_INITDIALOG)
DRCc75dc442010-05-20 07:44:49 +0000173 SetWindowLongPtr(hwnd, GWLP_USERDATA, ((PROPSHEETPAGE*)lParam)->lParam);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000174
DRCc75dc442010-05-20 07:44:49 +0000175 LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000176 if (!self) return FALSE;
177
178 return ((PropSheetPage*)self)->dialogProc(hwnd, msg, wParam, lParam);
179}
180
181BOOL PropSheetPage::dialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
182{
183 switch (msg) {
184
185 case WM_INITDIALOG:
186 handle = hwnd;
187 initDialog();
188 return TRUE;
189
190 case WM_NOTIFY:
191 switch (((NMHDR*)lParam)->code) {
192 case PSN_APPLY:
193 onOk();
194 return FALSE;
195 };
196 return FALSE;
197
198 case WM_COMMAND:
199 return onCommand(LOWORD(wParam), HIWORD(wParam));
200
201 case WM_HELP:
202 return onHelp(((HELPINFO*)lParam)->iCtrlId);
203
204 }
205
206 return FALSE;
207}
208
209
210PropSheet::PropSheet(HINSTANCE inst_, const TCHAR* title_, std::list<PropSheetPage*> pages_, HICON icon_)
Peter Åstranddc30f8e2008-12-10 10:20:54 +0000211: icon(icon_), pages(pages_), inst(inst_), title(tstrDup(title_)), handle(0), alreadyShowing(0) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000212}
213
214PropSheet::~PropSheet() {
215}
216
217
218// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure...
219struct DLGTEMPLATEEX {
220 WORD dlgVer;
221 WORD signature;
222 DWORD helpID;
223 DWORD exStyle;
224 DWORD style;
225 WORD cDlgItems;
226 short x;
227 short y;
228 short cx;
229 short cy;
230};
231
232static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) {
233 if (message == PSCB_PRECREATE) {
234 // Remove the context-help style, to remove the titlebar ? button
235 // *** Nasty hack to cope with new & old dialog template formats...
236 if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff)
237 ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP;
238 else
239 ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP;
240 }
241 return TRUE;
242}
243
244
245bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
246 if (alreadyShowing) return false;
247 alreadyShowing = true;
248 int count = pages.size();
249
250 HPROPSHEETPAGE* hpages = new HPROPSHEETPAGE[count];
251 try {
252 // Create the PropertSheet page GDI objects.
253 std::list<PropSheetPage*>::iterator pspi;
254 int i = 0;
255 for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
256 hpages[i] = CreatePropertySheetPage(&((*pspi)->page));
257 (*pspi)->setPropSheet(this);
258 i++;
259 }
260
261 // Initialise and create the PropertySheet itself
262 PROPSHEETHEADER header;
Peter Åstrandcf75be12008-12-09 10:50:40 +0000263 header.dwSize = sizeof(PROPSHEETHEADER); // Requires comctl32.dll 4.71 or greater, ie IE 4 or later
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000264 header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK);
265 header.pfnCallback = removeCtxtHelp;
266 header.hwndParent = owner;
267 header.hInstance = inst;
268 header.pszCaption = title.buf;
269 header.nPages = count;
270 header.nStartPage = 0;
271 header.phpage = hpages;
272 if (icon) {
273 header.hIcon = icon;
274 header.dwFlags |= PSH_USEHICON;
275 }
276
277 handle = (HWND)PropertySheet(&header);
278 if ((handle == 0) || (handle == (HWND)-1))
279 throw rdr::SystemException("PropertySheet failed", GetLastError());
280 centerWindow(handle, owner);
281 plog.info("created %lx", handle);
282
283#ifdef _DIALOG_CAPTURE
284 if (capture) {
285 plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
286 char* tmpdir = getenv("TEMP");
287 HDC dc = GetWindowDC(handle);
288 DeviceFrameBuffer fb(dc);
289 int i=0;
290 while (true) {
291 int id = PropSheet_IndexToId(handle, i);
292 if (!id) break;
293 PropSheet_SetCurSelByID(handle, id);
294 MSG msg;
295 while (PeekMessage(&msg, handle, 0, 0, PM_REMOVE)) {
296 if (!PropSheet_IsDialogMessage(handle, &msg))
297 DispatchMessage(&msg);
298 }
299 fb.grabRect(fb.getRect());
300 TCHAR title[128];
301 if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title)))
302 _stprintf(title, _T("capture%d"), i);
303 CharArray pageTitle(strDup(title));
304 for (int j=0; j<strlen(pageTitle.buf); j++) {
305 if (pageTitle.buf[j] == '/' || pageTitle.buf[j] == '\\' || pageTitle.buf[j] == ':')
306 pageTitle.buf[j] = '-';
307 }
308 char filename[256];
309 sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf);
310 vlog.debug("writing to %s", filename);
311 saveBMP(filename, &fb);
312 i++;
313 }
314 ReleaseDC(handle, dc);
315 } else {
316#endif
317 try {
318 if (owner)
319 EnableWindow(owner, FALSE);
320 // Run the PropertySheet
321 MSG msg;
322 while (GetMessage(&msg, 0, 0, 0)) {
323 if (!PropSheet_IsDialogMessage(handle, &msg))
324 DispatchMessage(&msg);
325 if (!PropSheet_GetCurrentPageHwnd(handle))
326 break;
327 }
328 if (owner)
329 EnableWindow(owner, TRUE);
330 } catch (...) {
331 if (owner)
332 EnableWindow(owner, TRUE);
333 throw;
334 }
335#ifdef _DIALOG_CAPTURE
336 }
337#endif
338
339 plog.info("finished %lx", handle);
340
341 DestroyWindow(handle);
342 handle = 0;
343 alreadyShowing = false;
344
345 // Clear up the pages' GDI objects
346 for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
347 (*pspi)->setPropSheet(0);
348 delete [] hpages; hpages = 0;
349
350 return true;
351 } catch (rdr::Exception) {
352 alreadyShowing = false;
353
354 std::list<PropSheetPage*>::iterator pspi;
355 for (pspi=pages.begin(); pspi!=pages.end(); pspi++)
356 (*pspi)->setPropSheet(0);
357 delete [] hpages; hpages = 0;
358
359 throw;
360 }
361}
362
363void PropSheet::reInitPages() {
364 plog.debug("reInitPages %lx", handle);
365 std::list<PropSheetPage*>::iterator pspi;
366 for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
367 if ((*pspi)->handle)
368 (*pspi)->initDialog();
369 }
370}
371
372bool PropSheet::commitPages() {
373 plog.debug("commitPages %lx", handle);
374 bool result = true;
375 std::list<PropSheetPage*>::iterator pspi;
376 for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
377 if ((*pspi)->handle)
378 result = result && (*pspi)->onOk();
379 }
380 return result;
381}
382
383
384void PropSheetPage::setChanged(bool changed) {
385 if (propSheet) {
386 plog.debug("setChanged[%lx(%lx)]=%d", handle, propSheet->handle, (int)changed);
387 if (changed)
388 PropSheet_Changed(propSheet->handle, handle);
389 else
390 PropSheet_UnChanged(propSheet->handle, handle);
391 }
392}