]> git.saurik.com Git - wxWidgets.git/blame - src/os2/app.cpp
Misc XRC format docs corrections.
[wxWidgets.git] / src / os2 / app.cpp
CommitLineData
0e320a79 1/////////////////////////////////////////////////////////////////////////////
ad9835c9 2// Name: src/os2/app.cpp
0e320a79 3// Purpose: wxApp
d88de032 4// Author: David Webster
0e320a79 5// Modified by:
d88de032 6// Created: 10/13/99
d88de032 7// Copyright: (c) David Webster
65571936 8// Licence: wxWindows licence
0e320a79
DW
9/////////////////////////////////////////////////////////////////////////////
10
d88de032
DW
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
e4db172a
WS
14#include "wx/app.h"
15
d88de032 16#ifndef WX_PRECOMP
ad9835c9 17 #include "wx/dynarray.h"
d88de032 18 #include "wx/frame.h"
d88de032
DW
19 #include "wx/utils.h"
20 #include "wx/gdicmn.h"
21 #include "wx/pen.h"
22 #include "wx/brush.h"
23 #include "wx/cursor.h"
24 #include "wx/icon.h"
25 #include "wx/palette.h"
26 #include "wx/dc.h"
27 #include "wx/dialog.h"
28 #include "wx/msgdlg.h"
29 #include "wx/intl.h"
3a3dde0d 30 #include "wx/crt.h"
e4db172a 31 #include "wx/log.h"
02761f6c 32 #include "wx/module.h"
0e320a79
DW
33#endif
34
f18ad9c6
WS
35#include "wx/stdpaths.h"
36#include "wx/filename.h"
32ba0160 37#include "wx/evtloop.h"
f18ad9c6 38
d88de032
DW
39#include "wx/os2/private.h"
40
29d83fc1
DW
41#ifdef __EMX__
42
19193a2c
KB
43#include <sys/ioctl.h>
44#include <sys/select.h>
29d83fc1
DW
45
46#else
47
65b851bb 48#include <nerrno.h>
19193a2c
KB
49#include <sys/ioctl.h>
50#include <sys/select.h>
51#include <sys/time.h>
29d83fc1 52
19193a2c 53#endif //
29d83fc1 54
6670f564
WS
55#if defined(__WATCOMC__)
56
57#include <tcpustd.h>
58
59#elif !defined(__EMX__)
29d83fc1 60
3958ae62 61#define select(a,b,c,d,e) bsdselect(a,b,c,d,e)
19193a2c
KB
62extern "C" int _System bsdselect(int,
63 struct fd_set *,
64 struct fd_set *,
65 struct fd_set *,
66 struct timeval *);
3958ae62
SN
67#endif
68
d88de032
DW
69#if wxUSE_THREADS
70 #include "wx/thread.h"
d88de032 71#endif // wxUSE_THREADS
0e320a79 72
8df85a61
DW
73#if wxUSE_TOOLTIPS
74 #include "wx/tooltip.h"
75#endif // wxUSE_TOOLTIPS
76
0e320a79 77#include <string.h>
d88de032
DW
78#include <ctype.h>
79
80// ---------------------------------------------------------------------------
81// global variables
82// ---------------------------------------------------------------------------
83
e7445ff8 84WXDLLEXPORT_DATA(wxChar*) wxBuffer;
9ed0fac8 85extern wxCursor* g_globalCursor;
0e320a79 86
8df85a61 87HAB vHabmain = NULLHANDLE;
d88de032 88
d88de032 89
9ed0fac8
DW
90HICON wxSTD_FRAME_ICON = (HICON) NULL;
91HICON wxSTD_MDICHILDFRAME_ICON = (HICON) NULL;
d88de032
DW
92HICON wxSTD_MDIPARENTFRAME_ICON = (HICON) NULL;
93
9ed0fac8
DW
94HICON wxDEFAULT_FRAME_ICON = (HICON) NULL;
95HICON wxDEFAULT_MDICHILDFRAME_ICON = (HICON) NULL;
96HICON wxDEFAULT_MDIPARENTFRAME_ICON = (HICON) NULL;
d88de032
DW
97
98HBRUSH wxDisableButtonBrush = (HBRUSH) 0;
99
51c1d535
DW
100MRESULT EXPENTRY wxWndProc( HWND hWnd,ULONG message,MPARAM mp1,MPARAM mp2);
101MRESULT EXPENTRY wxFrameWndProc( HWND hWnd,ULONG message,MPARAM mp1,MPARAM mp2);
102
d88de032
DW
103// ===========================================================================
104// implementation
105// ===========================================================================
106
3958ae62
SN
107// ---------------------------------------------------------------------------
108// helper struct and functions for socket handling
109// ---------------------------------------------------------------------------
110
111struct GsocketCallbackInfo{
112 void (*proc)(void *);
113 int type;
114 int handle;
115 void* gsock;
116};
117
621b4574 118// These defines are used here and in gsockpm.cpp
3958ae62
SN
119#define wxSockReadMask 0x01
120#define wxSockWriteMask 0x02
121
3958ae62
SN
122void wxApp::HandleSockets()
123{
ad9835c9 124 bool pendingEvent = false;
3958ae62
SN
125
126 // Check whether it's time for Gsocket operation
127 if (m_maxSocketHandles > 0 && m_maxSocketNr > 0)
128 {
129 fd_set readfds = m_readfds;
130 fd_set writefds = m_writefds;
131 struct timeval timeout;
132 int i;
133 struct GsocketCallbackInfo
134 *CallbackInfo = (struct GsocketCallbackInfo *)m_sockCallbackInfo;
3958ae62
SN
135 timeout.tv_sec = 0;
136 timeout.tv_usec = 0;
137 if ( select(m_maxSocketNr, &readfds, &writefds, 0, &timeout) > 0)
138 {
5c9ee6dd
SN
139 for (i = m_lastUsedHandle + 1; i != m_lastUsedHandle;
140 (i < m_maxSocketNr - 1) ? i++ : (i = 0))
3958ae62 141 {
3958ae62
SN
142 if (FD_ISSET(i, &readfds))
143 {
144 int r;
145 for (r = 0; r < m_maxSocketHandles; r++){
146 if(CallbackInfo[r].handle == i &&
147 CallbackInfo[r].type == wxSockReadMask)
148 break;
149 }
150 if (r < m_maxSocketHandles)
151 {
152 CallbackInfo[r].proc(CallbackInfo[r].gsock);
ad9835c9 153 pendingEvent = true;
3958ae62
SN
154 }
155 }
156 if (FD_ISSET(i, &writefds))
157 {
158 int r;
159 for (r = 0; r < m_maxSocketHandles; r++)
160 if(CallbackInfo[r].handle == i &&
161 CallbackInfo[r].type == wxSockWriteMask)
162 break;
163 if (r < m_maxSocketHandles)
164 {
165 CallbackInfo[r].proc(CallbackInfo[r].gsock);
ad9835c9 166 pendingEvent = true;
3958ae62
SN
167 }
168 }
169 }
170 m_lastUsedHandle = i;
171 }
172 if (pendingEvent)
5c9ee6dd 173 ProcessPendingEvents();
3958ae62
SN
174 }
175}
d88de032
DW
176// ---------------------------------------------------------------------------
177// wxApp
178// ---------------------------------------------------------------------------
179
d88de032 180 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
0e320a79 181
d88de032
DW
182 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
183 EVT_IDLE(wxApp::OnIdle)
184 EVT_END_SESSION(wxApp::OnEndSession)
185 EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
186 END_EVENT_TABLE()
0e320a79 187
8df85a61
DW
188//
189// Initialize
190//
05e2b077 191bool wxApp::Initialize(int& argc, wxChar **argv)
0e320a79 192{
94826170
VZ
193 if ( !wxAppBase::Initialize(argc, argv) )
194 return false;
195
7e99520b
DW
196#if defined(wxUSE_CONSOLEDEBUG)
197 #if wxUSE_CONSOLEDEBUG
198/***********************************************/
199/* Code for using stdout debug */
200/* To use it you mast link app as "Window" - EK*/
201/***********************************************/
202 {
203 PPIB pib;
204 PTIB tib;
205
206 printf("In console\n");
207
208 DosGetInfoBlocks(&tib, &pib);
209/* Try morphing into a PM application. */
210// if(pib->pib_ultype == 2) /* VIO */
211 pib->pib_ultype = 3;
212 }
213/**********************************************/
214/**********************************************/
215 #endif //wxUSE_CONSOLEDEBUG
216#endif
217
54ffa107
DW
218 //
219 // OS2 has to have an anchorblock
220 //
94826170 221 vHabmain = WinInitialize(0);
175bb578
SN
222 wxFileName GetPrefix(argv[0]);
223 GetPrefix.MakeAbsolute();
224 wxStandardPaths::SetInstallPrefix(GetPrefix.GetPath());
94826170
VZ
225 if (!vHabmain)
226 {
227 // TODO: at least give some error message here...
228 wxAppBase::CleanUp();
54ffa107 229
ad9835c9 230 return false;
94826170
VZ
231 }
232
233 wxBuffer = new wxChar[1500]; // FIXME; why?
54ffa107
DW
234
235 // Some people may wish to use this, but
236 // probably it shouldn't be here by default.
54ffa107 237 // wxRedirectIOToConsole();
54ffa107 238
2461cfa0 239 wxWinHandleHash = new wxWinHashTable(wxKEY_INTEGER, 100);
d88de032
DW
240
241 // This is to foil optimizations in Visual C++ that throw out dummy.obj.
242 // PLEASE DO NOT ALTER THIS.
1b3d5e55 243#if !defined(WXMAKINGDLL) && defined(__VISAGECPP__)
d88de032
DW
244 extern char wxDummyChar;
245 if (wxDummyChar) wxDummyChar++;
246#endif
247
61243a51 248 // wxSetKeyboardHook(TRUE);
d88de032 249
459073a9 250 RegisterWindowClasses(vHabmain);
94826170 251
ad9835c9 252 return true;
8df85a61 253} // end of wxApp::Initialize
d88de032 254
7e99520b 255const char* CANTREGISTERCLASS = " Can't register Class ";
d88de032
DW
256// ---------------------------------------------------------------------------
257// RegisterWindowClasses
258// ---------------------------------------------------------------------------
259
ad9835c9 260bool wxApp::RegisterWindowClasses( HAB vHab )
d88de032 261{
ad9835c9
WS
262 ERRORID vError = 0L;
263 wxString sError;
3b9e3455 264
f23208ca 265 if (!::WinRegisterClass( vHab
0fba44b4 266 ,(PSZ)wxFrameClassName
51c1d535 267 ,wxFrameWndProc
f9efbe3a 268 ,CS_SIZEREDRAW | CS_SYNCPAINT
a0606634 269 ,sizeof(ULONG)
f23208ca 270 ))
d88de032 271 {
914589c2
DW
272 vError = ::WinGetLastError(vHab);
273 sError = wxPMErrorToStr(vError);
9923c37d 274 wxLogLastError(sError.c_str());
ad9835c9 275 return false;
d88de032
DW
276 }
277
f23208ca 278 if (!::WinRegisterClass( vHab
0fba44b4 279 ,(PSZ)wxFrameClassNameNoRedraw
51c1d535 280 ,wxWndProc
f23208ca 281 ,0
51c1d535 282 ,sizeof(ULONG)
f23208ca 283 ))
d88de032 284 {
914589c2
DW
285 vError = ::WinGetLastError(vHab);
286 sError = wxPMErrorToStr(vError);
9923c37d 287 wxLogLastError(sError.c_str());
ad9835c9 288 return false;
d88de032
DW
289 }
290
f23208ca 291 if (!::WinRegisterClass( vHab
0fba44b4 292 ,(PSZ)wxMDIFrameClassName
51c1d535 293 ,wxWndProc
f6bcfd97 294 ,CS_SIZEREDRAW | CS_MOVENOTIFY | CS_SYNCPAINT
51c1d535 295 ,sizeof(ULONG)
f23208ca 296 ))
d88de032 297 {
914589c2
DW
298 vError = ::WinGetLastError(vHab);
299 sError = wxPMErrorToStr(vError);
9923c37d 300 wxLogLastError(sError.c_str());
ad9835c9 301 return false;
d88de032 302 }
0e320a79 303
f23208ca 304 if (!::WinRegisterClass( vHab
0fba44b4 305 ,(PSZ)wxMDIFrameClassNameNoRedraw
51c1d535 306 ,wxWndProc
f23208ca 307 ,0
51c1d535 308 ,sizeof(ULONG)
f23208ca 309 ))
d88de032 310 {
914589c2
DW
311 vError = ::WinGetLastError(vHab);
312 sError = wxPMErrorToStr(vError);
9923c37d 313 wxLogLastError(sError.c_str());
ad9835c9 314 return false;
d88de032
DW
315 }
316
f23208ca 317 if (!::WinRegisterClass( vHab
0fba44b4 318 ,(PSZ)wxMDIChildFrameClassName
51c1d535 319 ,wxWndProc
f23208ca 320 ,CS_MOVENOTIFY | CS_SIZEREDRAW | CS_SYNCPAINT | CS_HITTEST
51c1d535 321 ,sizeof(ULONG)
f23208ca 322 ))
d88de032 323 {
914589c2
DW
324 vError = ::WinGetLastError(vHab);
325 sError = wxPMErrorToStr(vError);
9923c37d 326 wxLogLastError(sError.c_str());
ad9835c9 327 return false;
d88de032
DW
328 }
329
f23208ca 330 if (!::WinRegisterClass( vHab
0fba44b4 331 ,(PSZ)wxMDIChildFrameClassNameNoRedraw
51c1d535 332 ,wxWndProc
f23208ca 333 ,CS_HITTEST
51c1d535 334 ,sizeof(ULONG)
f23208ca 335 ))
d88de032 336 {
914589c2
DW
337 vError = ::WinGetLastError(vHab);
338 sError = wxPMErrorToStr(vError);
9923c37d 339 wxLogLastError(sError.c_str());
ad9835c9 340 return false;
d88de032
DW
341 }
342
f23208ca 343 if (!::WinRegisterClass( vHab
0fba44b4 344 ,(PSZ)wxPanelClassName
51c1d535 345 ,wxWndProc
f23208ca 346 ,CS_MOVENOTIFY | CS_SIZEREDRAW | CS_HITTEST | CS_SAVEBITS | CS_SYNCPAINT
51c1d535 347 ,sizeof(ULONG)
f23208ca 348 ))
d88de032 349 {
914589c2
DW
350 vError = ::WinGetLastError(vHab);
351 sError = wxPMErrorToStr(vError);
9923c37d 352 wxLogLastError(sError.c_str());
ad9835c9 353 return false;
d88de032
DW
354 }
355
f23208ca 356 if (!::WinRegisterClass( vHab
0fba44b4 357 ,(PSZ)wxCanvasClassName
51c1d535 358 ,wxWndProc
54ffa107 359 ,CS_SIZEREDRAW | CS_HITTEST | CS_SYNCPAINT
51c1d535 360 ,sizeof(ULONG)
f23208ca 361 ))
d88de032 362 {
914589c2
DW
363 vError = ::WinGetLastError(vHab);
364 sError = wxPMErrorToStr(vError);
9923c37d 365 wxLogLastError(sError.c_str());
ad9835c9 366 return false;
d88de032 367 }
0256cfeb 368 if (!::WinRegisterClass( vHab
0fba44b4 369 ,(PSZ)wxCanvasClassNameNR
0256cfeb
DW
370 ,wxWndProc
371 ,CS_HITTEST | CS_SYNCPAINT
372 ,sizeof(ULONG)
373 ))
374 {
375 vError = ::WinGetLastError(vHab);
376 sError = wxPMErrorToStr(vError);
9923c37d 377 wxLogLastError(sError.c_str());
ad9835c9 378 return false;
0256cfeb 379 }
ad9835c9 380 return true;
8df85a61 381} // end of wxApp::RegisterWindowClasses
d88de032 382
8df85a61 383//
77ffb593 384// Cleans up any wxWidgets internal structures left lying around
8df85a61 385//
0e320a79
DW
386void wxApp::CleanUp()
387{
5276b0a5 388 wxDELETEA(wxBuffer);
0e320a79 389
8df85a61
DW
390 //
391 // PM-SPECIFIC CLEANUP
392 //
0e320a79 393
ad9835c9 394 // wxSetKeyboardHook(false);
9ed0fac8 395
d88de032 396 if (wxSTD_FRAME_ICON)
9ed0fac8 397 ::WinFreeFileIcon(wxSTD_FRAME_ICON);
d88de032 398 if (wxSTD_MDICHILDFRAME_ICON)
9ed0fac8 399 ::WinFreeFileIcon(wxSTD_MDICHILDFRAME_ICON);
d88de032 400 if (wxSTD_MDIPARENTFRAME_ICON)
9ed0fac8 401 ::WinFreeFileIcon(wxSTD_MDIPARENTFRAME_ICON);
d88de032
DW
402
403 if (wxDEFAULT_FRAME_ICON)
9ed0fac8 404 ::WinFreeFileIcon(wxDEFAULT_FRAME_ICON);
d88de032 405 if (wxDEFAULT_MDICHILDFRAME_ICON)
9ed0fac8 406 ::WinFreeFileIcon(wxDEFAULT_MDICHILDFRAME_ICON);
d88de032 407 if (wxDEFAULT_MDIPARENTFRAME_ICON)
9ed0fac8
DW
408 ::WinFreeFileIcon(wxDEFAULT_MDIPARENTFRAME_ICON);
409
d88de032
DW
410 if ( wxDisableButtonBrush )
411 {
412// TODO: ::DeleteObject( wxDisableButtonBrush );
413 }
414
5276b0a5 415 wxDELETE(wxWinHandleHash);
d88de032 416
468e327a
SN
417 // Delete Message queue
418 if (wxTheApp->m_hMq)
419 ::WinDestroyMsgQueue(wxTheApp->m_hMq);
420
94826170 421 wxAppBase::CleanUp();
8df85a61 422} // end of wxApp::CleanUp
0e320a79 423
9ed0fac8 424bool wxApp::OnInitGui()
d88de032 425{
ad9835c9
WS
426 ERRORID vError;
427 wxString sError;
77cd51c3 428
19193a2c 429 if (!wxAppBase::OnInitGui())
ad9835c9 430 return false;
19193a2c 431
f23208ca 432 m_hMq = ::WinCreateMsgQueue(vHabmain, 0);
914589c2
DW
433 if (!m_hMq)
434 {
435 vError = ::WinGetLastError(vHabmain);
436 sError = wxPMErrorToStr(vError);
437 wxLogDebug(sError);
ad9835c9 438 return false;
914589c2 439 }
19193a2c 440
ad9835c9 441 return true;
8df85a61 442} // end of wxApp::OnInitGui
0e320a79 443
0e320a79
DW
444wxApp::wxApp()
445{
9ed0fac8 446 m_nPrintMode = wxPRINT_WINDOWS;
468e327a 447 m_hMq = 0;
3958ae62
SN
448 m_maxSocketHandles = 0;
449 m_maxSocketNr = 0;
450 m_sockCallbackInfo = 0;
8df85a61 451} // end of wxApp::wxApp
d88de032
DW
452
453wxApp::~wxApp()
454{
8df85a61 455} // end of wxApp::~wxApp
0e320a79 456
ad9835c9 457bool gbInOnIdle = false;
2b5f62a0 458
4bbd8098 459void wxApp::OnIdle( wxIdleEvent& WXUNUSED(rEvent) )
d88de032 460{
9ed0fac8 461 //
d88de032 462 // Avoid recursion (via ProcessEvent default case)
9ed0fac8 463 //
2b5f62a0 464 if (gbInOnIdle)
d88de032 465 return;
0e320a79 466
6670f564
WS
467 gbInOnIdle = true;
468
893758d5
DW
469#if wxUSE_DC_CACHEING
470 // automated DC cache management: clear the cached DCs and bitmap
471 // if it's likely that the app has finished with them, that is, we
472 // get an idle event and we're not dragging anything.
19193a2c
KB
473 if (!::WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) &&
474 !::WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) &&
475 !::WinGetKeyState(HWND_DESKTOP, VK_BUTTON2))
893758d5
DW
476 wxDC::ClearCache();
477#endif // wxUSE_DC_CACHEING
478
ad9835c9 479 gbInOnIdle = false;
8df85a61 480} // end of wxApp::OnIdle
9779893b 481
9ed0fac8
DW
482void wxApp::OnEndSession(
483 wxCloseEvent& WXUNUSED(rEvent))
0e320a79 484{
d88de032 485 if (GetTopWindow())
ad9835c9 486 GetTopWindow()->Close(true);
8df85a61 487} // end of wxApp::OnEndSession
0e320a79 488
9ed0fac8 489//
d88de032
DW
490// Default behaviour: close the application with prompts. The
491// user can veto the close, and therefore the end session.
9ed0fac8 492//
ad9835c9 493void wxApp::OnQueryEndSession( wxCloseEvent& rEvent )
0e320a79 494{
d88de032
DW
495 if (GetTopWindow())
496 {
9ed0fac8 497 if (!GetTopWindow()->Close(!rEvent.CanVeto()))
ad9835c9 498 rEvent.Veto(true);
d88de032 499 }
8df85a61 500} // end of wxApp::OnQueryEndSession
0e320a79 501
3958ae62
SN
502int wxApp::AddSocketHandler(int handle, int mask,
503 void (*callback)(void*), void * gsock)
504{
505 int find;
506 struct GsocketCallbackInfo
507 *CallbackInfo = (struct GsocketCallbackInfo *)m_sockCallbackInfo;
508
509 for (find = 0; find < m_maxSocketHandles; find++)
510 if (CallbackInfo[find].handle == -1)
511 break;
512 if (find == m_maxSocketHandles)
513 {
514 // Allocate new memory
515 m_sockCallbackInfo = realloc(m_sockCallbackInfo,
516 (m_maxSocketHandles+=10)*
517 sizeof(struct GsocketCallbackInfo));
518 CallbackInfo = (struct GsocketCallbackInfo *)m_sockCallbackInfo;
519 for (find = m_maxSocketHandles - 10; find < m_maxSocketHandles; find++)
520 CallbackInfo[find].handle = -1;
521 find = m_maxSocketHandles - 10;
522 }
523 CallbackInfo[find].proc = callback;
524 CallbackInfo[find].type = mask;
525 CallbackInfo[find].handle = handle;
526 CallbackInfo[find].gsock = gsock;
527 if (mask & wxSockReadMask)
528 FD_SET(handle, &m_readfds);
529 if (mask & wxSockWriteMask)
530 FD_SET(handle, &m_writefds);
531 if (handle >= m_maxSocketNr)
532 m_maxSocketNr = handle + 1;
533 return find;
534}
535
536void wxApp::RemoveSocketHandler(int handle)
537{
538 struct GsocketCallbackInfo
539 *CallbackInfo = (struct GsocketCallbackInfo *)m_sockCallbackInfo;
540 if (handle < m_maxSocketHandles)
541 {
542 if (CallbackInfo[handle].type & wxSockReadMask)
543 FD_CLR(CallbackInfo[handle].handle, &m_readfds);
544 if (CallbackInfo[handle].type & wxSockWriteMask)
545 FD_CLR(CallbackInfo[handle].handle, &m_writefds);
546 CallbackInfo[handle].handle = -1;
547 }
548}
549
8df85a61
DW
550//-----------------------------------------------------------------------------
551// wxWakeUpIdle
552//-----------------------------------------------------------------------------
553
e2478fde 554void wxApp::WakeUpIdle()
8df85a61
DW
555{
556 //
557 // Send the top window a dummy message so idle handler processing will
558 // start up again. Doing it this way ensures that the idle handler
559 // wakes up in the right thread (see also wxWakeUpMainThread() which does
560 // the same for the main app thread only)
561 //
562 wxWindow* pTopWindow = wxTheApp->GetTopWindow();
563
564 if (pTopWindow)
565 {
566 if ( !::WinPostMsg(GetHwndOf(pTopWindow), WM_NULL, (MPARAM)0, (MPARAM)0))
567 {
568 //
569 // Should never happen
570 //
2173b18f 571 wxLogLastError(wxT("PostMessage(WM_NULL)"));
8df85a61
DW
572 }
573 }
574} // end of wxWakeUpIdle
d88de032 575
76990f63 576HAB wxGetInstance()
d88de032 577{
76990f63 578 return vHabmain;
d88de032
DW
579}
580
6670f564 581void wxSetInstance( HAB vHab )
d88de032 582{
76990f63 583 vHabmain = vHab;
0e320a79 584}