blob: 76a624b2a47b8f9128bd7e12da961d1d0316b030 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19#include <vncviewer/CViewOptions.h>
20#include <rfb/Configuration.h>
21#include <rfb/encodings.h>
22#include <rfb/vncAuth.h>
23#include <rfb/LogWriter.h>
24#include <rfb_win32/Win32Util.h>
25#include <rfb_win32/Registry.h>
26#include <rdr/HexInStream.h>
27#include <rdr/HexOutStream.h>
28#include <stdlib.h>
29
30using namespace rfb;
31using namespace rfb::win32;
32
Peter Åstrand3e3c75e2005-01-03 13:32:28 +000033static StringParameter passwordFile("PasswordFile",
34 "Password file for VNC authentication", "");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000035
36static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true);
37static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true);
38
Peter Åstrandc81a6522004-12-30 11:32:08 +000039static BoolParameter fullColour("FullColor",
40 "Use full color", true);
41static AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour);
42
43static IntParameter lowColourLevel("LowColorLevel",
44 "Color level to use on slow connections. "
45 "0 = Very Low (8 colors), 1 = Low (64 colors), 2 = Medium (256 colors)",
Peter Åstranddd747d82004-12-21 15:54:44 +000046 2);
Peter Åstrandc81a6522004-12-30 11:32:08 +000047static AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel);
48
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000049static BoolParameter fullScreen("FullScreen",
50 "Use the whole display to show the remote desktop."
51 "(Press F8 to access the viewer menu)",
52 false);
53static StringParameter preferredEncoding("PreferredEncoding",
Peter Åstrand55855d52005-01-03 12:01:45 +000054 "Preferred encoding to use (Tight, ZRLE, Hextile or"
55 " Raw)", "Tight");
56static BoolParameter autoSelect("AutoSelect",
57 "Auto select pixel format and encoding. "
58 "Default if PreferredEncoding and FullColor are not specified.",
59 true);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000060static BoolParameter sharedConnection("Shared",
61 "Allow existing connections to the server to continue."
62 "(Default is to disconnect all other clients)",
63 false);
64
65static BoolParameter sendPtrEvents("SendPointerEvents",
66 "Send pointer (mouse) events to the server.", true);
67static BoolParameter sendKeyEvents("SendKeyEvents",
68 "Send key presses (and releases) to the server.", true);
Peter Åstrand57dcc452005-01-28 14:52:50 +000069static BoolParameter sendSysKeys("SendSysKeys",
70 "Send system keys (Alt combinations) to the server.", true);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000071
72static BoolParameter clientCutText("ClientCutText",
73 "Send clipboard changes to the server.", true);
74static BoolParameter serverCutText("ServerCutText",
75 "Accept clipboard changes from the server.", true);
76
77static BoolParameter protocol3_3("Protocol3.3",
78 "Only use protocol version 3.3", false);
79
80static IntParameter ptrEventInterval("PointerEventInterval",
81 "The interval to delay between sending one pointer event "
82 "and the next.", 0);
83static BoolParameter emulate3("Emulate3",
84 "Emulate middle mouse button when left and right buttons "
85 "are used simulatenously.", false);
86
87static BoolParameter acceptBell("AcceptBell",
88 "Produce a system beep when requested to by the server.",
89 true);
90
george82baa71192005-11-30 16:17:46 +000091static BoolParameter showToolbar("ShowToolbar", "Show toolbar by default.", true);
92
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000093static StringParameter monitor("Monitor", "The monitor to open the VNC Viewer window on, if available.", "");
94static StringParameter menuKey("MenuKey", "The key which brings up the popup menu", "F8");
95
Peter Åstrand365427a2004-12-29 10:59:03 +000096static BoolParameter customCompressLevel("CustomCompressLevel",
Peter Åstrand55855d52005-01-03 12:01:45 +000097 "Use custom compression level. "
98 "Default if CompressLevel is specified.", false);
Peter Åstrand365427a2004-12-29 10:59:03 +000099
100static IntParameter compressLevel("CompressLevel",
101 "Use specified compression level"
102 "0 = Low, 9 = High",
103 6);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000104
Peter Åstrand0b870262004-12-28 15:55:46 +0000105static BoolParameter noJpeg("NoJPEG",
106 "Disable lossy JPEG compression in Tight encoding.",
107 false);
108
Peter Åstrand365427a2004-12-29 10:59:03 +0000109static IntParameter qualityLevel("QualityLevel",
110 "JPEG quality level. "
111 "0 = Low, 9 = High",
112 6);
113
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000114CViewOptions::CViewOptions()
115: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize),
116autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen),
Peter Åstrand57dcc452005-01-28 14:52:50 +0000117shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents), sendSysKeys(::sendSysKeys),
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000118preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText),
george82baa71192005-11-30 16:17:46 +0000119protocol3_3(::protocol3_3), acceptBell(::acceptBell), showToolbar(::showToolbar), lowColourLevel(::lowColourLevel),
Peter Åstranded9d4ae2004-12-07 11:59:14 +0000120pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData()),
Peter Åstrand365427a2004-12-29 10:59:03 +0000121customCompressLevel(::customCompressLevel), compressLevel(::compressLevel),
Peter Åstrand3e3c75e2005-01-03 13:32:28 +0000122noJpeg(::noJpeg), qualityLevel(::qualityLevel), passwordFile(::passwordFile.getData())
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000123{
124 CharArray encodingName(::preferredEncoding.getData());
125 preferredEncoding = encodingNum(encodingName.buf);
126 setMenuKey(CharArray(::menuKey.getData()).buf);
Peter Åstrand55855d52005-01-03 12:01:45 +0000127
128 if (!::autoSelect.hasBeenSet()) {
129 // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
130 autoSelect = (!::preferredEncoding.hasBeenSet()
131 && !::fullColour.hasBeenSet()
132 && !::fullColourAlias.hasBeenSet());
133 }
134 if (!::customCompressLevel.hasBeenSet()) {
135 // Default to CustomCompressLevel=1 if CompressLevel is used.
136 customCompressLevel = ::compressLevel.hasBeenSet();
137 }
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000138}
139
140
141void CViewOptions::readFromFile(const char* filename) {
142 FILE* f = fopen(filename, "r");
143 if (!f)
144 throw rdr::Exception("Failed to read configuration file");
145
146 try {
147 char line[4096];
148 CharArray section;
149
150 CharArray hostTmp;
151 int portTmp = 0;
152
153 while (!feof(f)) {
154 // Read the next line
155 if (!fgets(line, sizeof(line), f)) {
156 if (feof(f))
157 break;
158 throw rdr::SystemException("fgets", ferror(f));
159 }
160 int len=strlen(line);
161 if (line[len-1] == '\n') {
162 line[len-1] = 0;
163 len--;
164 }
165
166 // Process the line
167 if (line[0] == ';') {
168 // Comment
169 } else if (line[0] == '[') {
170 // Entering a new section
171 if (!strSplit(&line[1], ']', &section.buf, 0))
172 throw rdr::Exception("bad Section");
173 } else {
174 // Reading an option
175 CharArray name;
176 CharArray value;
177 if (!strSplit(line, '=', &name.buf, &value.buf))
178 throw rdr::Exception("bad Name/Value pair");
179
180 if (stricmp(section.buf, "Connection") == 0) {
181 if (stricmp(name.buf, "Host") == 0) {
182 hostTmp.replaceBuf(value.takeBuf());
183 } else if (stricmp(name.buf, "Port") == 0) {
184 portTmp = atoi(value.buf);
185 } else if (stricmp(name.buf, "UserName") == 0) {
186 userName.replaceBuf(value.takeBuf());
187 } else if (stricmp(name.buf, "Password") == 0) {
188 int len = 0;
189 CharArray obfuscated;
190 rdr::HexInStream::hexStrToBin(value.buf, &obfuscated.buf, &len);
191 if (len == 8) {
192 password.replaceBuf(new char[9]);
193 memcpy(password.buf, obfuscated.buf, 8);
194 vncAuthUnobfuscatePasswd(password.buf);
195 password.buf[8] = 0;
196 }
197 }
198 } else if (stricmp(section.buf, "Options") == 0) {
199 // V4 options
200 if (stricmp(name.buf, "UseLocalCursor") == 0) {
201 useLocalCursor = atoi(value.buf);
202 } else if (stricmp(name.buf, "UseDesktopResize") == 0) {
203 useDesktopResize = atoi(value.buf);
204 } else if (stricmp(name.buf, "FullScreen") == 0) {
205 fullScreen = atoi(value.buf);
206 } else if (stricmp(name.buf, "FullColour") == 0) {
207 fullColour = atoi(value.buf);
208 } else if (stricmp(name.buf, "LowColourLevel") == 0) {
209 lowColourLevel = atoi(value.buf);
210 } else if (stricmp(name.buf, "PreferredEncoding") == 0) {
211 preferredEncoding = encodingNum(value.buf);
212 } else if ((stricmp(name.buf, "AutoDetect") == 0) ||
213 (stricmp(name.buf, "AutoSelect") == 0)) {
214 autoSelect = atoi(value.buf);
215 } else if (stricmp(name.buf, "Shared") == 0) {
216 shared = atoi(value.buf);
217 } else if (stricmp(name.buf, "SendPtrEvents") == 0) {
218 sendPtrEvents = atoi(value.buf);
219 } else if (stricmp(name.buf, "SendKeyEvents") == 0) {
220 sendKeyEvents = atoi(value.buf);
Peter Åstrand57dcc452005-01-28 14:52:50 +0000221 } else if (stricmp(name.buf, "SendSysKeys") == 0) {
222 sendSysKeys = atoi(value.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000223 } else if (stricmp(name.buf, "SendCutText") == 0) {
224 clientCutText = atoi(value.buf);
225 } else if (stricmp(name.buf, "AcceptCutText") == 0) {
226 serverCutText = atoi(value.buf);
george82985da922005-12-03 06:08:30 +0000227 } else if (stricmp(name.buf, "AcceptBell") == 0) {
228 acceptBell = atoi(value.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000229 } else if (stricmp(name.buf, "Emulate3") == 0) {
230 emulate3 = atoi(value.buf);
george82baa71192005-11-30 16:17:46 +0000231 } else if (stricmp(name.buf, "ShowToolbar") == 0) {
232 showToolbar = atoi(value.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000233 } else if (stricmp(name.buf, "PointerEventInterval") == 0) {
234 pointerEventInterval = atoi(value.buf);
235 } else if (stricmp(name.buf, "Monitor") == 0) {
236 monitor.replaceBuf(value.takeBuf());
237 } else if (stricmp(name.buf, "MenuKey") == 0) {
238 setMenuKey(value.buf);
Peter Åstrand365427a2004-12-29 10:59:03 +0000239 } else if (stricmp(name.buf, "CustomCompressLevel") == 0) {
240 customCompressLevel = atoi(value.buf);
241 } else if (stricmp(name.buf, "CompressLevel") == 0) {
242 compressLevel = atoi(value.buf);
Peter Åstrand0b870262004-12-28 15:55:46 +0000243 } else if (stricmp(name.buf, "NoJPEG") == 0) {
244 noJpeg = atoi(value.buf);
Peter Åstrand365427a2004-12-29 10:59:03 +0000245 } else if (stricmp(name.buf, "QualityLevel") == 0) {
246 qualityLevel = atoi(value.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000247 // Legacy options
248 } else if (stricmp(name.buf, "Preferred_Encoding") == 0) {
249 preferredEncoding = atoi(value.buf);
250 } else if (stricmp(name.buf, "8bit") == 0) {
251 fullColour = !atoi(value.buf);
252 } else if (stricmp(name.buf, "FullScreen") == 0) {
253 fullScreen = atoi(value.buf);
254 } else if (stricmp(name.buf, "ViewOnly") == 0) {
255 sendPtrEvents = sendKeyEvents = !atoi(value.buf);
256 } else if (stricmp(name.buf, "DisableClipboard") == 0) {
257 clientCutText = serverCutText = !atoi(value.buf);
258 }
259 }
260 }
261 }
262 fclose(f); f=0;
263
264 // Process the Host and Port
265 if (hostTmp.buf) {
266 int hostLen = strlen(hostTmp.buf) + 2 + 17;
267 host.replaceBuf(new char[hostLen]);
268 strCopy(host.buf, hostTmp.buf, hostLen);
269 if (portTmp) {
270 strncat(host.buf, "::", hostLen-1);
271 char tmp[16];
272 sprintf(tmp, "%d", portTmp);
273 strncat(host.buf, tmp, hostLen-1);
274 }
275 }
276
277 setConfigFileName(filename);
278 } catch (rdr::Exception&) {
279 if (f) fclose(f);
280 throw;
281 }
282}
283
284void CViewOptions::writeToFile(const char* filename) {
285 FILE* f = fopen(filename, "w");
286 if (!f)
287 throw rdr::Exception("Failed to write configuration file");
288
289 try {
290 // - Split server into host and port and save
291 fprintf(f, "[Connection]\n");
292
293 fprintf(f, "Host=%s\n", host.buf);
294 if (userName.buf)
295 fprintf(f, "UserName=%s\n", userName.buf);
296 if (password.buf) {
297 // - Warn the user before saving the password
298 if (MsgBox(0, _T("Do you want to include the VNC Password in this configuration file?\n")
299 _T("Storing the password is more convenient but poses a security risk."),
300 MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) == IDYES) {
301 char obfuscated[9];
302 memset(obfuscated, 0, sizeof(obfuscated));
303 strCopy(obfuscated, password.buf, sizeof(obfuscated));
304 vncAuthObfuscatePasswd(obfuscated);
305 CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfuscated, 8);
306 fprintf(f, "Password=%s\n", obfuscatedHex.buf);
307 }
308 }
309
310 // - Save the other options
311 fprintf(f, "[Options]\n");
312
313 fprintf(f, "UseLocalCursor=%d\n", (int)useLocalCursor);
314 fprintf(f, "UseDesktopResize=%d\n", (int)useDesktopResize);
315 fprintf(f, "FullScreen=%d\n", (int)fullScreen);
316 fprintf(f, "FullColour=%d\n", (int)fullColour);
317 fprintf(f, "LowColourLevel=%d\n", lowColourLevel);
318 fprintf(f, "PreferredEncoding=%s\n", encodingName(preferredEncoding));
319 fprintf(f, "AutoSelect=%d\n", (int)autoSelect);
320 fprintf(f, "Shared=%d\n", (int)shared);
321 fprintf(f, "SendPtrEvents=%d\n", (int)sendPtrEvents);
322 fprintf(f, "SendKeyEvents=%d\n", (int)sendKeyEvents);
Peter Åstrand57dcc452005-01-28 14:52:50 +0000323 fprintf(f, "SendSysKeys=%d\n", (int)sendSysKeys);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000324 fprintf(f, "SendCutText=%d\n", (int)clientCutText);
325 fprintf(f, "AcceptCutText=%d\n", (int)serverCutText);
george82985da922005-12-03 06:08:30 +0000326 fprintf(f, "AcceptBell=%d\n", (int)acceptBell);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000327 fprintf(f, "Emulate3=%d\n", (int)emulate3);
george82baa71192005-11-30 16:17:46 +0000328 fprintf(f, "ShowToolbar=%d\n", (int)showToolbar);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000329 fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval);
330 if (monitor.buf)
331 fprintf(f, "Monitor=%s\n", monitor.buf);
332 fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf);
Peter Åstrand365427a2004-12-29 10:59:03 +0000333 fprintf(f, "CustomCompressLevel=%d\n", customCompressLevel);
334 fprintf(f, "CompressLevel=%d\n", compressLevel);
Peter Åstrand0b870262004-12-28 15:55:46 +0000335 fprintf(f, "NoJPEG=%d\n", noJpeg);
Peter Åstrand365427a2004-12-29 10:59:03 +0000336 fprintf(f, "QualityLevel=%d\n", qualityLevel);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000337 fclose(f); f=0;
338
339 setConfigFileName(filename);
340 } catch (rdr::Exception&) {
341 if (f) fclose(f);
342 throw;
343 }
344}
345
346
347void CViewOptions::writeDefaults() {
348 RegKey key;
Peter Åstrand9fb4e0e2004-12-30 10:03:00 +0000349 key.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCviewer4"));
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000350 key.setBool(_T("UseLocalCursor"), useLocalCursor);
351 key.setBool(_T("UseDesktopResize"), useDesktopResize);
352 key.setBool(_T("FullScreen"), fullScreen);
353 key.setBool(_T("FullColour"), fullColour);
354 key.setInt(_T("LowColourLevel"), lowColourLevel);
355 key.setString(_T("PreferredEncoding"), TStr(encodingName(preferredEncoding)));
356 key.setBool(_T("AutoSelect"), autoSelect);
357 key.setBool(_T("Shared"), shared);
358 key.setBool(_T("SendPointerEvents"), sendPtrEvents);
359 key.setBool(_T("SendKeyEvents"), sendKeyEvents);
Peter Åstrand57dcc452005-01-28 14:52:50 +0000360 key.setBool(_T("SendSysKeys"), sendSysKeys);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000361 key.setBool(_T("ClientCutText"), clientCutText);
362 key.setBool(_T("ServerCutText"), serverCutText);
363 key.setBool(_T("Protocol3.3"), protocol3_3);
364 key.setBool(_T("AcceptBell"), acceptBell);
george82baa71192005-11-30 16:17:46 +0000365 key.setBool(_T("ShowToolbar"), showToolbar);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000366 key.setBool(_T("Emulate3"), emulate3);
367 key.setInt(_T("PointerEventInterval"), pointerEventInterval);
368 if (monitor.buf)
369 key.setString(_T("Monitor"), TStr(monitor.buf));
370 key.setString(_T("MenuKey"), TCharArray(menuKeyName()).buf);
Peter Åstrand365427a2004-12-29 10:59:03 +0000371 key.setInt(_T("CustomCompressLevel"), customCompressLevel);
372 key.setInt(_T("CompressLevel"), compressLevel);
Peter Åstrand0b870262004-12-28 15:55:46 +0000373 key.setInt(_T("NoJPEG"), noJpeg);
Peter Åstrand365427a2004-12-29 10:59:03 +0000374 key.setInt(_T("QualityLevel"), qualityLevel);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000375}
376
377
378void CViewOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));}
379void CViewOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));}
380void CViewOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));}
381void CViewOptions::setHost(const char* h) {host.replaceBuf(strDup(h));}
382void CViewOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));}
383
384void CViewOptions::setMenuKey(const char* keyName) {
385 if (!keyName[0]) {
386 menuKey = 0;
387 } else {
388 menuKey = VK_F8;
389 if (keyName[0] == 'F') {
390 UINT fKey = atoi(&keyName[1]);
391 if (fKey >= 1 && fKey <= 12)
392 menuKey = fKey-1 + VK_F1;
393 }
394 }
395}
396char* CViewOptions::menuKeyName() {
397 int fNum = (menuKey-VK_F1)+1;
398 if (fNum<1 || fNum>12)
399 return strDup("");
400 CharArray menuKeyStr(4);
401 sprintf(menuKeyStr.buf, "F%d", fNum);
402 return menuKeyStr.takeBuf();
403}
404
405
406CViewOptions& CViewOptions::operator=(const CViewOptions& o) {
407 useLocalCursor = o.useLocalCursor;
408 useDesktopResize = o.useDesktopResize;
409 fullScreen = o.fullScreen;
410 fullColour = o.fullColour;
411 lowColourLevel = o.lowColourLevel;
412 preferredEncoding = o.preferredEncoding;
413 autoSelect = o.autoSelect;
414 shared = o.shared;
415 sendPtrEvents = o.sendPtrEvents;
416 sendKeyEvents = o.sendKeyEvents;
Peter Åstrand57dcc452005-01-28 14:52:50 +0000417 sendSysKeys = o.sendSysKeys;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000418 clientCutText = o.clientCutText;
419 serverCutText = o.serverCutText;
420 emulate3 = o.emulate3;
421 pointerEventInterval = o.pointerEventInterval;
422 protocol3_3 = o.protocol3_3;
423 acceptBell = o.acceptBell;
george82baa71192005-11-30 16:17:46 +0000424 showToolbar = o.showToolbar;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000425 setUserName(o.userName.buf);
426 setPassword(o.password.buf);
427 setConfigFileName(o.configFileName.buf);
428 setHost(o.host.buf);
429 setMonitor(o.monitor.buf);
430 menuKey = o.menuKey;
Peter Åstrand365427a2004-12-29 10:59:03 +0000431 customCompressLevel = o.customCompressLevel;
432 compressLevel = o.compressLevel;
Peter Åstrand0b870262004-12-28 15:55:46 +0000433 noJpeg = o.noJpeg;
Peter Åstrand365427a2004-12-29 10:59:03 +0000434 qualityLevel = o.qualityLevel;
435
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000436 return *this;
Peter Åstranded9d4ae2004-12-07 11:59:14 +0000437}