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