Motif compilation fixes (now ok)
[wxWidgets.git] / src / motif / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: app.cpp
3 // Purpose: wxApp
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "app.h"
14 #endif
15
16 #include "wx/frame.h"
17 #include "wx/app.h"
18 #include "wx/utils.h"
19 #include "wx/gdicmn.h"
20 #include "wx/pen.h"
21 #include "wx/brush.h"
22 #include "wx/cursor.h"
23 #include "wx/icon.h"
24 #include "wx/palette.h"
25 #include "wx/dc.h"
26 #include "wx/dialog.h"
27 #include "wx/msgdlg.h"
28 #include "wx/log.h"
29 #include "wx/module.h"
30 #include "wx/memory.h"
31
32 #if wxUSE_THREADS
33 #include "wx/thread.h"
34 #endif
35
36 #if wxUSE_WX_RESOURCES
37 #include "wx/resource.h"
38 #endif
39
40 #include <Xm/Xm.h>
41 #include <X11/Xlib.h>
42 #include <X11/Xutil.h>
43 #include <X11/Xresource.h>
44 #include <X11/Xatom.h>
45
46 #include "wx/motif/private.h"
47
48 #include <string.h>
49
50 extern char *wxBuffer;
51 extern wxList wxPendingDelete;
52
53 #if wxUSE_THREADS
54 extern wxList wxPendingEvents;
55 extern wxCriticalSection wxPendingEventsLocker;
56 #endif // wxUSE_THREADS
57
58 wxApp *wxTheApp = NULL;
59
60 wxHashTable *wxWidgetHashTable = NULL;
61
62 #if !USE_SHARED_LIBRARY
63 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
64
65 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
66 EVT_IDLE(wxApp::OnIdle)
67 END_EVENT_TABLE()
68 #endif
69
70 long wxApp::sm_lastMessageTime = 0;
71
72 bool wxApp::Initialize()
73 {
74 wxBuffer = new char[BUFSIZ + 512];
75
76 wxClassInfo::InitializeClasses();
77
78 wxTheColourDatabase = new wxColourDatabase(wxKEY_STRING);
79 wxTheColourDatabase->Initialize();
80
81 wxInitializeStockLists();
82 wxInitializeStockObjects();
83
84 #if wxUSE_WX_RESOURCES
85 wxInitializeResourceSystem();
86 #endif
87
88 // For PostScript printing
89 #if wxUSE_POSTSCRIPT
90 /* Done using wxModule now
91 wxInitializePrintSetupData();
92 wxThePrintPaperDatabase = new wxPrintPaperDatabase;
93 wxThePrintPaperDatabase->CreateDatabase();
94 */
95 #endif
96
97 wxBitmap::InitStandardHandlers();
98
99 wxWidgetHashTable = new wxHashTable(wxKEY_INTEGER);
100
101 wxModule::RegisterModules();
102 if (!wxModule::InitializeModules()) return FALSE;
103
104 return TRUE;
105 }
106
107 void wxApp::CleanUp()
108 {
109 delete wxWidgetHashTable;
110 wxWidgetHashTable = NULL;
111
112 wxModule::CleanUpModules();
113
114 #if wxUSE_WX_RESOURCES
115 wxCleanUpResourceSystem();
116 #endif
117
118 wxDeleteStockObjects() ;
119
120 // Destroy all GDI lists, etc.
121
122 delete wxTheBrushList;
123 wxTheBrushList = NULL;
124
125 delete wxThePenList;
126 wxThePenList = NULL;
127
128 delete wxTheFontList;
129 wxTheFontList = NULL;
130
131 delete wxTheBitmapList;
132 wxTheBitmapList = NULL;
133
134 delete wxTheColourDatabase;
135 wxTheColourDatabase = NULL;
136
137 #if wxUSE_POSTSCRIPT
138 /* Done using wxModule now
139 wxInitializePrintSetupData(FALSE);
140 delete wxThePrintPaperDatabase;
141 wxThePrintPaperDatabase = NULL;
142 */
143 #endif
144
145 wxBitmap::CleanUpHandlers();
146
147 delete[] wxBuffer;
148 wxBuffer = NULL;
149
150 wxClassInfo::CleanUpClasses();
151
152 delete wxTheApp;
153 wxTheApp = NULL;
154
155 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
156 // At this point we want to check if there are any memory
157 // blocks that aren't part of the wxDebugContext itself,
158 // as a special case. Then when dumping we need to ignore
159 // wxDebugContext, too.
160 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
161 {
162 wxLogDebug("There were memory leaks.\n");
163 wxDebugContext::Dump();
164 wxDebugContext::PrintStatistics();
165 }
166 #endif
167
168 // do it as the very last thing because everything else can log messages
169 wxLog::DontCreateOnDemand();
170 // do it as the very last thing because everything else can log messages
171 delete wxLog::SetActiveTarget(NULL);
172 }
173
174 int wxEntry( int argc, char *argv[] )
175 {
176 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
177 // This seems to be necessary since there are 'rogue'
178 // objects present at this point (perhaps global objects?)
179 // Setting a checkpoint will ignore them as far as the
180 // memory checking facility is concerned.
181 // Of course you may argue that memory allocated in globals should be
182 // checked, but this is a reasonable compromise.
183 wxDebugContext::SetCheckpoint();
184 #endif
185
186 if (!wxApp::Initialize())
187 return FALSE;
188
189 if (!wxTheApp)
190 {
191 if (!wxApp::GetInitializerFunction())
192 {
193 printf( "wxWindows error: No initializer - use IMPLEMENT_APP macro.\n" );
194 return 0;
195 };
196
197 wxTheApp = (wxApp*) (* wxApp::GetInitializerFunction()) ();
198 };
199
200 if (!wxTheApp)
201 {
202 printf( "wxWindows error: wxTheApp == NULL\n" );
203 return 0;
204 };
205
206 wxTheApp->SetClassName(wxFileNameFromPath(argv[0]));
207 wxTheApp->SetAppName(wxFileNameFromPath(argv[0]));
208
209 wxTheApp->argc = argc;
210 wxTheApp->argv = argv;
211
212 // GUI-specific initialization, such as creating an app context.
213 wxTheApp->OnInitGui();
214
215 // Here frames insert themselves automatically into wxTopLevelWindows by
216 // getting created in OnInit().
217
218 int retValue = 0;
219 if (wxTheApp->OnInit())
220 {
221 if (wxTheApp->Initialized()) retValue = wxTheApp->OnRun();
222 }
223
224 // flush the logged messages if any
225 wxLog *pLog = wxLog::GetActiveTarget();
226 if ( pLog != NULL && pLog->HasPendingMessages() )
227 pLog->Flush();
228
229 delete wxLog::SetActiveTarget(new wxLogStderr); // So dialog boxes aren't used
230 // for further messages
231
232 if (wxTheApp->GetTopWindow())
233 {
234 delete wxTheApp->GetTopWindow();
235 wxTheApp->SetTopWindow(NULL);
236 }
237
238 wxTheApp->DeletePendingObjects();
239
240 wxTheApp->OnExit();
241
242 wxApp::CleanUp();
243
244 return retValue;
245 };
246
247 // Static member initialization
248 wxAppInitializerFunction wxApp::m_appInitFn = (wxAppInitializerFunction) NULL;
249
250 wxApp::wxApp()
251 {
252 m_topWindow = NULL;
253 wxTheApp = this;
254 m_className = "";
255 m_wantDebugOutput = TRUE ;
256 m_appName = "";
257 argc = 0;
258 argv = NULL;
259 m_printMode = wxPRINT_POSTSCRIPT;
260 m_exitOnFrameDelete = TRUE;
261 m_auto3D = TRUE;
262
263 m_mainColormap = (WXColormap) NULL;
264 m_appContext = (WXAppContext) NULL;
265 m_topLevelWidget = (WXWidget) NULL;
266 m_maxRequestSize = 0;
267 m_initialDisplay = (WXDisplay*) 0;
268 }
269
270 bool wxApp::Initialized()
271 {
272 if (GetTopWindow())
273 return TRUE;
274 else
275 return FALSE;
276 }
277
278 int wxApp::MainLoop()
279 {
280 m_keepGoing = TRUE;
281
282 /*
283 * Sit around forever waiting to process X-events. Property Change
284 * event are handled special, because they have to refer to
285 * the root window rather than to a widget. therefore we can't
286 * use an Xt-eventhandler.
287 */
288
289 XSelectInput(XtDisplay((Widget) wxTheApp->GetTopLevelWidget()),
290 XDefaultRootWindow(XtDisplay((Widget) wxTheApp->GetTopLevelWidget())),
291 PropertyChangeMask);
292
293 XEvent event;
294
295 // Use this flag to allow breaking the loop via wxApp::ExitMainLoop()
296 while (m_keepGoing)
297 {
298 XtAppNextEvent( (XtAppContext) wxTheApp->GetAppContext(), &event);
299
300 ProcessXEvent((WXEvent*) & event);
301
302 if (XtAppPending( (XtAppContext) wxTheApp->GetAppContext() ) == 0)
303 {
304 if (!ProcessIdle())
305 {
306 #if wxUSE_THREADS
307 // leave the main loop to give other threads a chance to
308 // perform their GUI work
309 wxMutexGuiLeave();
310 wxUsleep(20);
311 wxMutexGuiEnter();
312 #endif
313 }
314 }
315
316 }
317
318 return 0;
319 }
320
321 // Processes an X event.
322 void wxApp::ProcessXEvent(WXEvent* _event)
323 {
324 XEvent* event = (XEvent*) _event;
325
326 if ((event->type == KeyPress) && CheckForAccelerator(_event))
327 {
328 // Do nothing! We intercepted and processed the event as an accelerator.
329 return;
330 }
331 else if (event->type == PropertyNotify)
332 {
333 HandlePropertyChange(_event);
334 return;
335 }
336 else if (event->type == ResizeRequest)
337 {
338 /* Terry Gitnick <terryg@scientech.com> - 1/21/98
339 * If resize event, don't resize until the last resize event for this
340 * window is recieved. Prevents flicker as windows are resized.
341 */
342
343 Display *disp = XtDisplay((Widget) wxTheApp->GetTopLevelWidget());
344 Window win = event->xany.window;
345 XEvent report;
346
347 // to avoid flicker
348 report = * event;
349 while( XCheckTypedWindowEvent (disp, win, ResizeRequest, &report));
350
351 // TODO: when implementing refresh optimization, we can use
352 // XtAddExposureToRegion to expand the window's paint region.
353
354 XtDispatchEvent(event);
355 }
356 else
357 {
358 XtDispatchEvent(event);
359 }
360 }
361
362 // Returns TRUE if more time is needed.
363 bool wxApp::ProcessIdle()
364 {
365 wxIdleEvent event;
366 event.SetEventObject(this);
367 ProcessEvent(event);
368
369 return event.MoreRequested();
370 }
371
372 void wxApp::ExitMainLoop()
373 {
374 m_keepGoing = FALSE;
375 }
376
377 // Is a message/event pending?
378 bool wxApp::Pending()
379 {
380 XFlush(XtDisplay( (Widget) wxTheApp->GetTopLevelWidget() ));
381
382 // Fix by Doug from STI, to prevent a stall if non-X event
383 // is found.
384 return ((XtAppPending( (XtAppContext) GetAppContext() ) & XtIMXEvent) != 0) ;
385 }
386
387 // Dispatch a message.
388 void wxApp::Dispatch()
389 {
390 // XtAppProcessEvent( (XtAppContext) wxTheApp->GetAppContext(), XtIMAll);
391
392 XEvent event;
393 XtAppNextEvent((XtAppContext) GetAppContext(), &event);
394 ProcessXEvent((WXEvent*) & event);
395 }
396
397 // This should be redefined in a derived class for
398 // handling property change events for XAtom IPC.
399 void wxApp::HandlePropertyChange(WXEvent *event)
400 {
401 // by default do nothing special
402 XtDispatchEvent((XEvent*) event); /* let Motif do the work */
403 }
404
405 void wxApp::OnIdle(wxIdleEvent& event)
406 {
407 static bool inOnIdle = FALSE;
408
409 // Avoid recursion (via ProcessEvent default case)
410 if (inOnIdle)
411 return;
412
413 inOnIdle = TRUE;
414
415 // 'Garbage' collection of windows deleted with Close().
416 DeletePendingObjects();
417
418 #if wxUSE_THREADS
419 // Flush pending events.
420 ProcessPendingEvents();
421 #endif
422
423 // flush the logged messages if any
424 wxLog *pLog = wxLog::GetActiveTarget();
425 if ( pLog != NULL && pLog->HasPendingMessages() )
426 pLog->Flush();
427
428 // Send OnIdle events to all windows
429 bool needMore = SendIdleEvents();
430
431 if (needMore)
432 event.RequestMore(TRUE);
433
434 inOnIdle = FALSE;
435 }
436
437 // Send idle event to all top-level windows
438 bool wxApp::SendIdleEvents()
439 {
440 bool needMore = FALSE;
441
442 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
443 while (node)
444 {
445 wxWindow* win = node->GetData();
446 if (SendIdleEvents(win))
447 needMore = TRUE;
448 node = node->GetNext();
449 }
450
451 return needMore;
452 }
453
454 // Send idle event to window and all subwindows
455 bool wxApp::SendIdleEvents(wxWindow* win)
456 {
457 bool needMore = FALSE;
458
459 wxIdleEvent event;
460 event.SetEventObject(win);
461 win->ProcessEvent(event);
462
463 if (event.MoreRequested())
464 needMore = TRUE;
465
466 wxNode* node = win->GetChildren().First();
467 while (node)
468 {
469 wxWindow* win = (wxWindow*) node->Data();
470 if (SendIdleEvents(win))
471 needMore = TRUE;
472
473 node = node->Next();
474 }
475 return needMore ;
476 }
477
478 void wxApp::DeletePendingObjects()
479 {
480 wxNode *node = wxPendingDelete.First();
481 while (node)
482 {
483 wxObject *obj = (wxObject *)node->Data();
484
485 delete obj;
486
487 if (wxPendingDelete.Member(obj))
488 delete node;
489
490 // Deleting one object may have deleted other pending
491 // objects, so start from beginning of list again.
492 node = wxPendingDelete.First();
493 }
494 }
495
496 #if wxUSE_THREADS
497 void wxApp::ProcessPendingEvents()
498 {
499 wxNode *node = wxPendingEvents.First();
500 wxCriticalSectionLocker locker(wxPendingEventsLocker);
501
502 while (node)
503 {
504 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
505
506 handler->ProcessPendingEvents();
507
508 delete node;
509 node = wxPendingEvents.First();
510 }
511 }
512 #endif // wxUSE_THREADS
513
514 wxLog* wxApp::CreateLogTarget()
515 {
516 return new wxLogGui;
517 }
518
519 wxWindow* wxApp::GetTopWindow() const
520 {
521 if (m_topWindow)
522 return m_topWindow;
523 else if (wxTopLevelWindows.GetCount() > 0)
524 return wxTopLevelWindows.GetFirst()->GetData();
525 else
526 return NULL;
527 }
528
529 // Create an application context
530 bool wxApp::OnInitGui()
531 {
532 XtToolkitInitialize() ;
533 wxTheApp->m_appContext = (WXAppContext) XtCreateApplicationContext() ;
534 Display *dpy = XtOpenDisplay((XtAppContext) wxTheApp->m_appContext,(String)NULL,NULL,
535 (const char*) wxTheApp->GetClassName(), NULL, 0,
536 # if XtSpecificationRelease < 5
537 (Cardinal*) &argc,
538 # else
539 &argc,
540 # endif
541 argv);
542
543 if (!dpy) {
544 wxString className(wxTheApp->GetClassName());
545 wxLogError(_("wxWindows could not open display for '%s': exiting."),
546 (const char*) className);
547 exit(-1);
548 }
549 m_initialDisplay = (WXDisplay*) dpy;
550
551 wxTheApp->m_topLevelWidget = (WXWidget) XtAppCreateShell((String)NULL, (const char*) wxTheApp->GetClassName(),
552 applicationShellWidgetClass,dpy,
553 NULL,0) ;
554
555 // Add general resize proc
556 XtActionsRec rec;
557 rec.string = "resize";
558 rec.proc = (XtActionProc)wxWidgetResizeProc;
559 XtAppAddActions((XtAppContext) wxTheApp->m_appContext, &rec, 1);
560
561 GetMainColormap(dpy);
562 m_maxRequestSize = XMaxRequestSize((Display*) dpy);
563
564 return TRUE;
565 }
566
567 WXColormap wxApp::GetMainColormap(WXDisplay* display)
568 {
569 if (!display) /* Must be called first with non-NULL display */
570 return m_mainColormap;
571
572 int defaultScreen = DefaultScreen((Display*) display);
573 Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
574
575 Colormap c = DefaultColormapOfScreen(screen);
576
577 if (!m_mainColormap)
578 m_mainColormap = (WXColormap) c;
579
580 return (WXColormap) c;
581 }
582
583 // Returns TRUE if an accelerator has been processed
584 bool wxApp::CheckForAccelerator(WXEvent* event)
585 {
586 XEvent* xEvent = (XEvent*) event;
587 if (xEvent->xany.type == KeyPress)
588 {
589 // Find a wxWindow for this window
590 // TODO: should get display for the window, not the current display
591 Widget widget = XtWindowToWidget((Display*) wxGetDisplay(), xEvent->xany.window);
592 wxWindow* win = NULL;
593
594 // Find the first wxWindow that corresponds to this event window
595 while (widget && !(win = wxGetWindowFromTable(widget)))
596 widget = XtParent(widget);
597
598 if (!widget || !win)
599 return FALSE;
600
601 wxKeyEvent keyEvent(wxEVT_CHAR);
602 wxTranslateKeyEvent(keyEvent, win, (Widget) 0, xEvent);
603
604 // Now we have a wxKeyEvent and we have a wxWindow.
605 // Go up the hierarchy until we find a matching accelerator,
606 // or we get to the top.
607 while (win)
608 {
609 if (win->ProcessAccelerator(keyEvent))
610 return TRUE;
611 win = win->GetParent();
612 }
613 return FALSE;
614 }
615 return FALSE;
616 }
617
618 void wxExit()
619 {
620 int retValue = 0;
621 if (wxTheApp)
622 retValue = wxTheApp->OnExit();
623
624 wxApp::CleanUp();
625 /*
626 * Exit in some platform-specific way. Not recommended that the app calls this:
627 * only for emergencies.
628 */
629 exit(retValue);
630 }
631
632 // Yield to other processes
633 bool wxYield()
634 {
635 while (wxTheApp && wxTheApp->Pending())
636 wxTheApp->Dispatch();
637
638 // VZ: is it the same as this (taken from old wxExecute)?
639 #if 0
640 XtAppProcessEvent((XtAppContext) wxTheApp->GetAppContext(), XtIMAll);
641 #endif
642
643 return TRUE;
644 }
645