]> git.saurik.com Git - wxWidgets.git/blame - utils/wxPython/src/helpers.h
Lots more support for event-less callbacks
[wxWidgets.git] / utils / wxPython / src / helpers.h
CommitLineData
7bf85405
RD
1/////////////////////////////////////////////////////////////////////////////
2// Name: helpers.h
3// Purpose: Helper functions/classes for the wxPython extenaion module
4//
5// Author: Robin Dunn
6//
7// Created: 7/1/97
8// RCS-ID: $Id$
9// Copyright: (c) 1998 by Total Control Software
10// Licence: wxWindows license
11/////////////////////////////////////////////////////////////////////////////
12
13#ifndef __wxp_helpers__
14#define __wxp_helpers__
15
16#include <wx/wx.h>
17
18
cf694132
RD
19//----------------------------------------------------------------------
20
21// if we want to handle threads and Python threads are available...
22#if defined(WXP_USE_THREAD) && defined(WITH_THREAD)
23
24#define WXP_WITH_THREAD
25#define wxPy_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
26#define wxPy_END_ALLOW_THREADS Py_END_ALLOW_THREADS
27
28#else // no Python threads...
29#undef WXP_WITH_THREAD
30#define wxPy_BEGIN_ALLOW_THREADS
31#define wxPy_END_ALLOW_THREADS
32#endif
33
34
efc5f224
RD
35//---------------------------------------------------------------------------
36
37#if defined(__WXMSW__)
38# define HELPEREXPORT __declspec(dllexport)
39#else
40# define HELPEREXPORT
41#endif
42
7bf85405
RD
43//----------------------------------------------------------------------
44
45class wxPyApp: public wxApp
46{
47public:
cf694132
RD
48 wxPyApp();
49 ~wxPyApp();
7bf85405
RD
50 int MainLoop(void);
51 bool OnInit(void);
8bf5d46e 52//# void AfterMainLoop(void);
7bf85405
RD
53};
54
55extern wxPyApp *wxPythonApp;
56
57//----------------------------------------------------------------------
58
0d6f9504 59void __wxPreStart();
7bf85405
RD
60PyObject* __wxStart(PyObject*, PyObject* args);
61
62extern PyObject* wxPython_dict;
63PyObject* __wxSetDictionary(PyObject*, PyObject* args);
64
7bf85405 65void wxPyEventThunker(wxObject*, wxEvent& event);
efc5f224
RD
66
67HELPEREXPORT PyObject* wxPyConstructObject(void* ptr, char* className);
d559219f
RD
68HELPEREXPORT bool wxPyRestoreThread();
69HELPEREXPORT void wxPySaveThread(bool doSave);
7bf85405
RD
70
71//----------------------------------------------------------------------
72
73
74#ifndef SWIGCODE
75extern "C" void SWIG_MakePtr(char *, void *, char *);
76extern "C" char *SWIG_GetPtr(char *, void **, char *);
d559219f 77extern "C" char *SWIG_GetPtrObj(PyObject *obj, void **ptr, char *type);
7bf85405
RD
78#endif
79
80
81#ifdef _MSC_VER
82# pragma warning(disable:4800)
83#endif
84
b639c3c5
RD
85typedef unsigned char byte;
86
7bf85405
RD
87
88// Non-const versions to keep SWIG happy.
89extern wxPoint wxPyDefaultPosition;
90extern wxSize wxPyDefaultSize;
7bf85405
RD
91extern wxString wxPyEmptyStr;
92
93//----------------------------------------------------------------------
94
95class wxPyCallback : public wxObject {
96public:
cf694132
RD
97 wxPyCallback(PyObject* func);
98 ~wxPyCallback();
7bf85405
RD
99
100 void EventThunker(wxEvent& event);
101
102 PyObject* m_func;
103};
104
105//---------------------------------------------------------------------------
106
8bf5d46e
RD
107// class wxPyMenu : public wxMenu {
108// public:
109// wxPyMenu(const wxString& title = "", PyObject* func=NULL);
110// ~wxPyMenu();
7bf85405 111
8bf5d46e
RD
112// private:
113// static void MenuCallback(wxMenu& menu, wxCommandEvent& evt);
114// PyObject* func;
115// };
714e6a9e 116
7bf85405
RD
117
118//---------------------------------------------------------------------------
119
120class wxPyTimer : public wxTimer {
121public:
122 wxPyTimer(PyObject* callback);
123 ~wxPyTimer();
124
125 void Notify();
126
127private:
128 PyObject* func;
129};
130
cf694132
RD
131//---------------------------------------------------------------------------
132
133class wxPyEvent : public wxCommandEvent {
134 DECLARE_DYNAMIC_CLASS(wxPyEvent)
135public:
136 wxPyEvent(wxEventType commandType = wxEVT_NULL, PyObject* userData = Py_None);
137 ~wxPyEvent();
138
139 void SetUserData(PyObject* userData);
140 PyObject* GetUserData();
141
142private:
143 PyObject* m_userData;
144};
145
bb0054cd
RD
146
147
148
149
150//---------------------------------------------------------------------------
151// This class holds an instance of a Python Shadow Class object and assists
152// with looking up and invoking Python callback methods from C++ virtual
153// method redirections. For all classes which have virtuals which should be
154// overridable in wxPython, a new subclass is created that contains a
a08cbc01 155// wxPyCallbackHelper.
d559219f
RD
156//
157// **** This class should be combined with wxPyCallback defined above.
158//
bb0054cd
RD
159//---------------------------------------------------------------------------
160
efc5f224 161class HELPEREXPORT wxPyCallbackHelper {
bb0054cd
RD
162public:
163 wxPyCallbackHelper();
164 ~wxPyCallbackHelper();
165
166 void setSelf(PyObject* self);
167
168 bool findCallback(const wxString& name);
169 int callCallback(PyObject* argTuple);
170 PyObject* callCallbackObj(PyObject* argTuple);
171
172private:
173 PyObject* m_self;
174 PyObject* m_lastFound;
175};
176
177
178
179//---------------------------------------------------------------------------
180// These macros are used to implement the virtual methods that should
181// redirect to a Python method if one exists. The names designate the
182// return type, if any as well as any parameter types.
183//---------------------------------------------------------------------------
184
efc5f224
RD
185#define PYPRIVATE \
186 void _setSelf(PyObject* self) { \
187 m_myInst.setSelf(self); \
188 } \
189 private: wxPyCallbackHelper m_myInst;
190
191//---------------------------------------------------------------------------
192
d559219f
RD
193#define DEC_PYCALLBACK__(CBNAME) \
194 void CBNAME(); \
195 void base_##CBNAME();
196
197
198#define IMP_PYCALLBACK__(CLASS, PCLASS, CBNAME) \
199 void CLASS::CBNAME() { \
200 bool doSave = wxPyRestoreThread(); \
201 if (m_myInst.findCallback(#CBNAME)) \
202 m_myInst.callCallback(Py_BuildValue("()")); \
203 else \
204 PCLASS::CBNAME(); \
205 wxPySaveThread(doSave); \
206 } \
207 void CLASS::base_##CBNAME() { \
208 PCLASS::CBNAME(); \
209 }
210
211//---------------------------------------------------------------------------
212
213#define DEC_PYCALLBACK_BOOL_INTINT(CBNAME) \
214 bool CBNAME(int a, int b); \
215 bool base_##CBNAME(int a, int b);
216
217
218#define IMP_PYCALLBACK_BOOL_INTINT(CLASS, PCLASS, CBNAME) \
219 bool CLASS::CBNAME(int a, int b) { \
220 bool rval; \
221 bool doSave = wxPyRestoreThread(); \
bb0054cd 222 if (m_myInst.findCallback(#CBNAME)) \
d559219f 223 rval = m_myInst.callCallback(Py_BuildValue("(ii)",a,b)); \
bb0054cd 224 else \
d559219f
RD
225 rval = PCLASS::CBNAME(a,b); \
226 wxPySaveThread(doSave); \
227 return rval; \
bb0054cd 228 } \
d559219f 229 bool CLASS::base_##CBNAME(int a, int b) { \
bb0054cd
RD
230 return PCLASS::CBNAME(a,b); \
231 }
232
233//---------------------------------------------------------------------------
234
d559219f
RD
235#define DEC_PYCALLBACK_BOOL_INT(CBNAME) \
236 bool CBNAME(int a); \
237 bool base_##CBNAME(int a);
238
239
240#define IMP_PYCALLBACK_BOOL_INT(CLASS, PCLASS, CBNAME) \
241 bool CLASS::CBNAME(int a) { \
242 bool rval; \
243 bool doSave = wxPyRestoreThread(); \
bb0054cd 244 if (m_myInst.findCallback(#CBNAME)) \
d559219f 245 rval = m_myInst.callCallback(Py_BuildValue("(i)",a)); \
bb0054cd 246 else \
d559219f
RD
247 rval = PCLASS::CBNAME(a); \
248 wxPySaveThread(doSave); \
249 return rval; \
bb0054cd 250 } \
d559219f 251 bool CLASS::base_##CBNAME(int a) { \
bb0054cd
RD
252 return PCLASS::CBNAME(a); \
253 }
254
efc5f224
RD
255//---------------------------------------------------------------------------
256
d559219f
RD
257#define DEC_PYCALLBACK_BOOL_INT_pure(CBNAME) \
258 bool CBNAME(int a);
259
260
261#define IMP_PYCALLBACK_BOOL_INT_pure(CLASS, PCLASS, CBNAME) \
262 bool CLASS::CBNAME(int a) { \
263 bool rval; \
264 bool doSave = wxPyRestoreThread(); \
bb0054cd 265 if (m_myInst.findCallback(#CBNAME)) \
d559219f
RD
266 rval = m_myInst.callCallback(Py_BuildValue("(i)",a)); \
267 else rval = false; \
268 wxPySaveThread(doSave); \
269 return rval; \
bb0054cd
RD
270 }
271
272
273//---------------------------------------------------------------------------
274
d559219f
RD
275#define DEC_PYCALLBACK__DC(CBNAME) \
276 void CBNAME(wxDC& a); \
277 void base_##CBNAME(wxDC& a);
278
279
280#define IMP_PYCALLBACK__DC(CLASS, PCLASS, CBNAME) \
281 void CLASS::CBNAME(wxDC& a) { \
282 bool doSave = wxPyRestoreThread(); \
283 if (m_myInst.findCallback(#CBNAME)) \
284 m_myInst.callCallback(Py_BuildValue("(O)", \
285 wxPyConstructObject(&a, "wxDC"))); \
286 else \
287 PCLASS::CBNAME(a); \
288 wxPySaveThread(doSave); \
289 } \
290 void CLASS::base_##CBNAME(wxDC& a) { \
291 PCLASS::CBNAME(a); \
bb0054cd
RD
292 }
293
efc5f224
RD
294
295
bb0054cd
RD
296//---------------------------------------------------------------------------
297
d559219f
RD
298#define DEC_PYCALLBACK__DCBOOL(CBNAME) \
299 void CBNAME(wxDC& a, bool b); \
300 void base_##CBNAME(wxDC& a, bool b);
301
302
303#define IMP_PYCALLBACK__DCBOOL(CLASS, PCLASS, CBNAME) \
304 void CLASS::CBNAME(wxDC& a, bool b) { \
305 bool doSave = wxPyRestoreThread(); \
efc5f224
RD
306 if (m_myInst.findCallback(#CBNAME)) \
307 m_myInst.callCallback(Py_BuildValue("(Oi)", \
d559219f 308 wxPyConstructObject(&a, "wxDC"), (int)b)); \
efc5f224
RD
309 else \
310 PCLASS::CBNAME(a, b); \
d559219f 311 wxPySaveThread(doSave); \
efc5f224 312 } \
d559219f 313 void CLASS::base_##CBNAME(wxDC& a, bool b) { \
efc5f224
RD
314 PCLASS::CBNAME(a, b); \
315 }
316
317//---------------------------------------------------------------------------
318
d559219f
RD
319#define DEC_PYCALLBACK__DCBOOL(CBNAME) \
320 void CBNAME(wxDC& a, bool b); \
321 void base_##CBNAME(wxDC& a, bool b);
322
323
324#define IMP_PYCALLBACK__DCBOOL(CLASS, PCLASS, CBNAME) \
325 void CLASS::CBNAME(wxDC& a, bool b) { \
326 bool doSave = wxPyRestoreThread(); \
efc5f224
RD
327 if (m_myInst.findCallback(#CBNAME)) \
328 m_myInst.callCallback(Py_BuildValue("(Oi)", \
d559219f 329 wxPyConstructObject(&a, "wxDC"), (int)b)); \
efc5f224
RD
330 else \
331 PCLASS::CBNAME(a, b); \
d559219f 332 wxPySaveThread(doSave); \
efc5f224 333 } \
d559219f 334 void CLASS::base_##CBNAME(wxDC& a, bool b) { \
efc5f224
RD
335 PCLASS::CBNAME(a, b); \
336 }
337
338//---------------------------------------------------------------------------
339
d559219f
RD
340#define DEC_PYCALLBACK__2DBL(CBNAME) \
341 void CBNAME(double a, double b); \
342 void base_##CBNAME(double a, double b);
343
344
345#define IMP_PYCALLBACK__2DBL(CLASS, PCLASS, CBNAME) \
346 void CLASS::CBNAME(double a, double b) { \
347 bool doSave = wxPyRestoreThread(); \
348 if (m_myInst.findCallback(#CBNAME)) \
349 m_myInst.callCallback(Py_BuildValue("(dd)",a,b)); \
350 else \
351 PCLASS::CBNAME(a, b); \
352 wxPySaveThread(doSave); \
353 } \
354 void CLASS::base_##CBNAME(double a, double b) { \
355 PCLASS::CBNAME(a, b); \
efc5f224
RD
356 }
357
358//---------------------------------------------------------------------------
359
d559219f
RD
360#define DEC_PYCALLBACK__2DBL2INT(CBNAME) \
361 void CBNAME(double a, double b, int c, int d); \
362 void base_##CBNAME(double a, double b, int c, int d);
363
364
365#define IMP_PYCALLBACK__2DBL2INT(CLASS, PCLASS, CBNAME) \
366 void CLASS::CBNAME(double a, double b, int c, int d) { \
367 bool doSave = wxPyRestoreThread(); \
efc5f224
RD
368 if (m_myInst.findCallback(#CBNAME)) \
369 m_myInst.callCallback(Py_BuildValue("(ddii)", \
370 a,b,c,d)); \
371 else \
372 PCLASS::CBNAME(a, b, c, d); \
d559219f 373 wxPySaveThread(doSave); \
efc5f224 374 } \
d559219f 375 void CLASS::base_##CBNAME(double a, double b, int c, int d) { \
efc5f224
RD
376 PCLASS::CBNAME(a, b, c, d); \
377 }
378
379//---------------------------------------------------------------------------
380
d559219f
RD
381#define DEC_PYCALLBACK__DC4DBLBOOL(CBNAME) \
382 void CBNAME(wxDC& a, double b, double c, double d, double e, bool f); \
383 void base_##CBNAME(wxDC& a, double b, double c, double d, double e, bool f);
384
385
386#define IMP_PYCALLBACK__DC4DBLBOOL(CLASS, PCLASS, CBNAME) \
387 void CLASS::CBNAME(wxDC& a, double b, double c, double d, double e, bool f) { \
388 bool doSave = wxPyRestoreThread(); \
389 if (m_myInst.findCallback(#CBNAME)) \
390 m_myInst.callCallback(Py_BuildValue("(Oddddi)", \
391 wxPyConstructObject(&a, "wxDC"), \
392 b, c, d, e, (int)f)); \
393 else \
394 PCLASS::CBNAME(a, b, c, d, e, f); \
395 wxPySaveThread(doSave); \
396 } \
397 void CLASS::base_##CBNAME(wxDC& a, double b, double c, double d, double e, bool f) {\
398 PCLASS::CBNAME(a, b, c, d, e, f); \
efc5f224
RD
399 }
400
401//---------------------------------------------------------------------------
402
d559219f
RD
403#define DEC_PYCALLBACK_BOOL_DC4DBLBOOL(CBNAME) \
404 bool CBNAME(wxDC& a, double b, double c, double d, double e, bool f); \
405 bool base_##CBNAME(wxDC& a, double b, double c, double d, double e, bool f);
406
407
408#define IMP_PYCALLBACK_BOOL_DC4DBLBOOL(CLASS, PCLASS, CBNAME) \
409 bool CLASS::CBNAME(wxDC& a, double b, double c, double d, double e, bool f) { \
410 bool doSave = wxPyRestoreThread(); \
411 if (m_myInst.findCallback(#CBNAME)) \
412 return m_myInst.callCallback(Py_BuildValue("(Oddddi)", \
413 wxPyConstructObject(&a, "wxDC"), \
414 b, c, d, e, (int)f)); \
415 else \
416 return PCLASS::CBNAME(a, b, c, d, e, f); \
417 wxPySaveThread(doSave); \
418 } \
419 bool CLASS::base_##CBNAME(wxDC& a, double b, double c, double d, double e, bool f) {\
420 return PCLASS::CBNAME(a, b, c, d, e, f); \
efc5f224
RD
421 }
422
423//---------------------------------------------------------------------------
424
d559219f
RD
425#define DEC_PYCALLBACK__BOOL2DBL2INT(CBNAME) \
426 void CBNAME(bool a, double b, double c, int d, int e); \
427 void base_##CBNAME(bool a, double b, double c, int d, int e);
428
429
430#define IMP_PYCALLBACK__BOOL2DBL2INT(CLASS, PCLASS, CBNAME) \
431 void CLASS::CBNAME(bool a, double b, double c, int d, int e) { \
432 bool doSave = wxPyRestoreThread(); \
433 if (m_myInst.findCallback(#CBNAME)) \
434 m_myInst.callCallback(Py_BuildValue("(idii)", \
435 (int)a,b,c,d,e)); \
436 else \
437 PCLASS::CBNAME(a, b, c, d, e); \
438 wxPySaveThread(doSave); \
439 } \
440 void CLASS::base_##CBNAME(bool a, double b, double c, int d, int e) { \
441 PCLASS::CBNAME(a, b, c, d, e); \
efc5f224
RD
442 }
443
444//---------------------------------------------------------------------------
445
d559219f
RD
446#define DEC_PYCALLBACK__DC4DBL(CBNAME) \
447 void CBNAME(wxDC& a, double b, double c, double d, double e); \
448 void base_##CBNAME(wxDC& a, double b, double c, double d, double e);
449
450
451#define IMP_PYCALLBACK__DC4DBL(CLASS, PCLASS, CBNAME) \
452 void CLASS::CBNAME(wxDC& a, double b, double c, double d, double e) { \
453 bool doSave = wxPyRestoreThread(); \
454 if (m_myInst.findCallback(#CBNAME)) \
455 m_myInst.callCallback(Py_BuildValue("(Odddd)", \
456 wxPyConstructObject(&a, "wxDC"), \
457 b, c, d, e)); \
458 else \
459 PCLASS::CBNAME(a, b, c, d, e); \
460 wxPySaveThread(doSave); \
461 } \
462 void CLASS::base_##CBNAME(wxDC& a, double b, double c, double d, double e) {\
463 PCLASS::CBNAME(a, b, c, d, e); \
efc5f224
RD
464 }
465
466//---------------------------------------------------------------------------
467
d559219f
RD
468#define DEC_PYCALLBACK__DCBOOL(CBNAME) \
469 void CBNAME(wxDC& a, bool b); \
470 void base_##CBNAME(wxDC& a, bool b);
471
472
473#define IMP_PYCALLBACK__DCBOOL(CLASS, PCLASS, CBNAME) \
474 void CLASS::CBNAME(wxDC& a, bool b) { \
475 bool doSave = wxPyRestoreThread(); \
efc5f224
RD
476 if (m_myInst.findCallback(#CBNAME)) \
477 m_myInst.callCallback(Py_BuildValue("(Oi)", \
d559219f 478 wxPyConstructObject(&a, "wxDC"), \
efc5f224
RD
479 (int)b)); \
480 else \
481 PCLASS::CBNAME(a, b); \
d559219f 482 wxPySaveThread(doSave); \
efc5f224 483 } \
d559219f 484 void CLASS::base_##CBNAME(wxDC& a, bool b) { \
efc5f224
RD
485 PCLASS::CBNAME(a, b); \
486 }
bb0054cd 487
7bf85405 488//---------------------------------------------------------------------------
7bf85405 489
d559219f
RD
490#define DEC_PYCALLBACK__WXCPBOOL2DBL2INT(CBNAME) \
491 void CBNAME(wxControlPoint* a, bool b, double c, double d, int e, int f); \
492 void base_##CBNAME(wxControlPoint* a, bool b, double c, double d, int e, int f);
493
494
495#define IMP_PYCALLBACK__WXCPBOOL2DBL2INT(CLASS, PCLASS, CBNAME) \
496 void CLASS::CBNAME(wxControlPoint* a, bool b, double c, double d, \
497 int e, int f) { \
498 bool doSave = wxPyRestoreThread(); \
499 if (m_myInst.findCallback(#CBNAME)) \
500 m_myInst.callCallback(Py_BuildValue("(Oiddii)", \
501 wxPyConstructObject(a, "wxControlPoint"), \
502 (int)b, c, d, e, f)); \
503 else \
504 PCLASS::CBNAME(a, b, c, d, e, f); \
505 wxPySaveThread(doSave); \
506 } \
507 void CLASS::base_##CBNAME(wxControlPoint* a, bool b, double c, double d, \
508 int e, int f) { \
509 PCLASS::CBNAME(a, b, c, d, e, f); \
efc5f224
RD
510 }
511
512//---------------------------------------------------------------------------
513
d559219f
RD
514#define DEC_PYCALLBACK__WXCP2DBL2INT(CBNAME) \
515 void CBNAME(wxControlPoint* a, double b, double c, int d, int e); \
516 void base_##CBNAME(wxControlPoint* a, double b, double c, int d, int e);
517
518
519#define IMP_PYCALLBACK__WXCP2DBL2INT(CLASS, PCLASS, CBNAME) \
520 void CLASS::CBNAME(wxControlPoint* a, double b, double c, int d, int e) { \
521 bool doSave = wxPyRestoreThread(); \
522 if (m_myInst.findCallback(#CBNAME)) \
523 m_myInst.callCallback(Py_BuildValue("(Oddii)", \
524 wxPyConstructObject(a, "wxControlPoint"), \
525 b, c, d, e)); \
526 else \
527 PCLASS::CBNAME(a, b, c, d, e); \
528 wxPySaveThread(doSave); \
529 } \
530 void CLASS::base_##CBNAME(wxControlPoint* a, double b, double c, \
531 int d, int e) { \
532 PCLASS::CBNAME(a, b, c, d, e); \
efc5f224
RD
533 }
534
535//---------------------------------------------------------------------------
536
d559219f
RD
537#define DEC_PYCALLBACK__2DBLINT(CBNAME) \
538 void CBNAME(double a, double b, int c); \
539 void base_##CBNAME(double a, double b, int c);
540
541
542#define IMP_PYCALLBACK__2DBLINT(CLASS, PCLASS, CBNAME) \
543 void CLASS::CBNAME(double a, double b, int c) { \
544 bool doSave = wxPyRestoreThread(); \
efc5f224
RD
545 if (m_myInst.findCallback(#CBNAME)) \
546 m_myInst.callCallback(Py_BuildValue("(ddi)", a,b,c)); \
547 else \
548 PCLASS::CBNAME(a, b, c); \
d559219f 549 wxPySaveThread(doSave); \
efc5f224 550 } \
d559219f 551 void CLASS::base_##CBNAME(double a, double b, int c) { \
efc5f224
RD
552 PCLASS::CBNAME(a, b, c); \
553 }
554
555//---------------------------------------------------------------------------
556
d559219f
RD
557#define DEC_PYCALLBACK__BOOL2DBLINT(CBNAME) \
558 void CBNAME(bool a, double b, double c, int d); \
559 void base_##CBNAME(bool a, double b, double c, int d);
560
561
562#define IMP_PYCALLBACK__BOOL2DBLINT(CLASS, PCLASS, CBNAME) \
563 void CLASS::CBNAME(bool a, double b, double c, int d) { \
564 bool doSave = wxPyRestoreThread(); \
565 if (m_myInst.findCallback(#CBNAME)) \
566 m_myInst.callCallback(Py_BuildValue("(iddi)", (int)a,b,c,d)); \
567 else \
568 PCLASS::CBNAME(a, b, c, d); \
569 wxPySaveThread(doSave); \
570 } \
571 void CLASS::base_##CBNAME(bool a, double b, double c, int d) { \
572 PCLASS::CBNAME(a, b, c, d); \
efc5f224
RD
573 }
574
575//---------------------------------------------------------------------------
576//---------------------------------------------------------------------------
577//---------------------------------------------------------------------------
578//---------------------------------------------------------------------------
579
7bf85405
RD
580#endif
581
efc5f224
RD
582
583