]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/app.cpp
1. wxApp::ProcessPendingEvents() is now common, added appcmn.cpp and
[wxWidgets.git] / src / gtk1 / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: app.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "app.h"
12 #endif
13
14 #include "wx/app.h"
15 #include "wx/gdicmn.h"
16 #include "wx/utils.h"
17 #include "wx/intl.h"
18 #include "wx/log.h"
19 #include "wx/memory.h"
20 #include "wx/font.h"
21 #include "wx/settings.h"
22 #include "wx/dialog.h"
23
24 #if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
26 #endif
27
28 #include "wx/module.h"
29 #include "wx/image.h"
30
31 #if wxUSE_THREADS
32 #include "wx/thread.h"
33 #endif
34
35 #include "unistd.h"
36
37 #include "glib.h"
38 #include "gdk/gdk.h"
39 #include "gtk/gtk.h"
40
41 #include "wx/gtk/win_gtk.h"
42
43 //-----------------------------------------------------------------------------
44 // global data
45 //-----------------------------------------------------------------------------
46
47 wxApp *wxTheApp = (wxApp *) NULL;
48 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50 extern wxResourceCache *wxTheResourceCache;
51 extern bool g_isIdle;
52
53 unsigned char g_palette[64*3] =
54 {
55 0x0, 0x0, 0x0,
56 0xff, 0xff, 0xff,
57 0xff, 0x0, 0x0,
58 0xff, 0xff, 0x0,
59 0x0, 0xff, 0x0,
60 0x0, 0x0, 0xff,
61 0x0, 0xff, 0xff,
62 0x99, 0x99, 0x99,
63 0xff, 0x88, 0x0,
64 0x88, 0x0, 0x0,
65 0x0, 0x88, 0x88,
66 0x88, 0x88, 0x0,
67 0xff, 0xcc, 0x97,
68 0xbb, 0xbb, 0xbb,
69 0x9f, 0x6b, 0x42,
70 0x55, 0x55, 0x55,
71 0xdd, 0xdd, 0xdd,
72 0x77, 0x77, 0x77,
73 0x33, 0x33, 0x33,
74 0xcc, 0x0, 0x0,
75 0xff, 0x44, 0x0,
76 0xff, 0xcc, 0x0,
77 0xcc, 0xcc, 0x0,
78 0x60, 0x60, 0x0,
79 0x0, 0x43, 0x0,
80 0x0, 0x7f, 0x0,
81 0x0, 0xcc, 0x0,
82 0x0, 0x44, 0x44,
83 0x0, 0x0, 0x44,
84 0x0, 0x0, 0x88,
85 0xef, 0xb1, 0x7b,
86 0xdf, 0x98, 0x5f,
87 0xbf, 0x87, 0x56,
88 0x7f, 0x57, 0x26,
89 0x5f, 0x39, 0xc,
90 0x3f, 0x1c, 0x0,
91 0x21, 0x0, 0x0,
92 0x0, 0x43, 0x87,
93 0x2d, 0x70, 0xaf,
94 0x5a, 0x9e, 0xd7,
95 0x87, 0xcc, 0xff,
96 0xff, 0xe0, 0xba,
97 0x21, 0x43, 0xf,
98 0x3d, 0x5d, 0x25,
99 0x59, 0x78, 0x3a,
100 0x75, 0x93, 0x4f,
101 0x91, 0xae, 0x64,
102 0xad, 0xc8, 0x7a,
103 0xf0, 0xa8, 0xef,
104 0xd0, 0x88, 0xd0,
105 0xaf, 0x66, 0xaf,
106 0x8e, 0x44, 0x8e,
107 0x6d, 0x22, 0x6d,
108 0x4b, 0x0, 0x4b,
109 0xff, 0xc0, 0xbc,
110 0xff, 0x93, 0x91,
111 0xff, 0x66, 0x67,
112 0xd8, 0xf2, 0xbf,
113 0xff, 0xc9, 0x68,
114 0xff, 0x96, 0x67,
115 0xa5, 0x60, 0xff,
116 0x51, 0xff, 0x99,
117 0x3f, 0xa5, 0x63,
118 0x98, 0x90, 0x67
119 };
120
121 //-----------------------------------------------------------------------------
122 // local functions
123 //-----------------------------------------------------------------------------
124
125 extern void wxFlushResources();
126
127 //-----------------------------------------------------------------------------
128 // global functions
129 //-----------------------------------------------------------------------------
130
131 void wxExit()
132 {
133 gtk_main_quit();
134 }
135
136 /* forward declaration */
137 gint wxapp_idle_callback( gpointer WXUNUSED(data) );
138
139 bool wxYield()
140 {
141 bool has_idle = (wxTheApp->m_idleTag != 0);
142
143 if (has_idle)
144 {
145 /* We need to temporarily remove idle callbacks or the loop will
146 never finish. */
147 gtk_idle_remove( wxTheApp->m_idleTag );
148 wxTheApp->m_idleTag = 0;
149 }
150
151 while (gtk_events_pending())
152 gtk_main_iteration();
153
154 /* it's necessary to call ProcessIdle() to update the frames sizes which
155 might have been changed (it also will update other things set from
156 OnUpdateUI() which is a nice (and desired) side effect) */
157 while (wxTheApp->ProcessIdle()) { }
158
159 if (has_idle)
160 {
161 /* re-add idle handler */
162 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
163 }
164
165 return TRUE;
166 }
167
168 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
169 {
170 if (!wxTheApp) return TRUE;
171
172 #if (GTK_MINOR_VERSION > 0)
173 /* when getting called from GDK's idle handler we
174 are no longer within GDK's grab on the GUI
175 thread so we must lock it here ourselves */
176 GDK_THREADS_ENTER ();
177 #endif
178
179 /* sent idle event to all who request them */
180 while (wxTheApp->ProcessIdle()) { }
181
182 /* we don't want any more idle events until the next event is
183 sent to wxGTK */
184 gtk_idle_remove( wxTheApp->m_idleTag );
185 wxTheApp->m_idleTag = 0;
186
187 /* indicate that we are now in idle mode - even so deeply
188 in idle mode that we don't get any idle events anymore.
189 this is like wxMSW where an idle event is sent only
190 once each time after the event queue has been completely
191 emptied */
192 g_isIdle = TRUE;
193
194 #if (GTK_MINOR_VERSION > 0)
195 /* release lock again */
196 GDK_THREADS_LEAVE ();
197 #endif
198
199 return TRUE;
200 }
201
202 void wxapp_install_idle_handler()
203 {
204 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
205
206 /* this routine gets called by all event handlers
207 indicating that the idle is over. */
208
209 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
210
211 g_isIdle = FALSE;
212 }
213
214 #if wxUSE_THREADS
215
216 /* forward declaration */
217 static gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) );
218
219 void wxapp_install_thread_wakeup()
220 {
221 if (wxTheApp->m_wakeUpTimerTag) return;
222
223 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 100, wxapp_wakeup_timerout_callback, (gpointer) NULL );
224 }
225
226 void wxapp_uninstall_thread_wakeup()
227 {
228 if (!wxTheApp->m_wakeUpTimerTag) return;
229
230 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
231 wxTheApp->m_wakeUpTimerTag = 0;
232 }
233
234 static gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
235 {
236 wxapp_uninstall_thread_wakeup();
237
238 #if (GTK_MINOR_VERSION > 0)
239 // when getting called from GDK's time-out handler
240 // we are no longer within GDK's grab on the GUI
241 // thread so we must lock it here ourselves
242 GDK_THREADS_ENTER ();
243 #endif
244
245 // unblock other threads wishing to do some GUI things
246 wxMutexGuiLeave();
247
248 // wake up other threads
249 wxUsleep( 1 );
250
251 // block other thread again
252 wxMutexGuiEnter();
253
254 #if (GTK_MINOR_VERSION > 0)
255 // release lock again
256 GDK_THREADS_LEAVE ();
257 #endif
258
259 wxapp_install_thread_wakeup();
260
261 return TRUE;
262 }
263
264 #endif // wxUSE_THREADS
265
266 //-----------------------------------------------------------------------------
267 // wxApp
268 //-----------------------------------------------------------------------------
269
270 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
271
272 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
273 EVT_IDLE(wxApp::OnIdle)
274 END_EVENT_TABLE()
275
276 wxApp::wxApp()
277 {
278 wxTheApp = this;
279
280 m_topWindow = (wxWindow *) NULL;
281 m_exitOnFrameDelete = TRUE;
282
283 m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
284
285 #if wxUSE_THREADS
286 m_wakeUpTimerTag = 0;
287 wxapp_install_thread_wakeup();
288 #endif
289
290 m_colorCube = (unsigned char*) NULL;
291 }
292
293 wxApp::~wxApp()
294 {
295 if (m_idleTag) gtk_idle_remove( m_idleTag );
296
297 #if wxUSE_THREADS
298 wxapp_uninstall_thread_wakeup();
299 #endif
300
301 if (m_colorCube) free(m_colorCube);
302 }
303
304 bool wxApp::OnInitGui()
305 {
306 GdkVisual *visual = gdk_visual_get_system();
307
308 /* on some machines, the default visual is just 256 colours, so
309 we make sure we get the best. this can sometimes be wasteful,
310 of course, but what do these guys pay $30.000 for? */
311 /*
312 if (gdk_visual_get_best() != gdk_visual_get_system())
313 {
314 GdkVisual* vis = gdk_visual_get_best();
315 gtk_widget_set_default_visual( vis );
316
317 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
318 gtk_widget_set_default_colormap( colormap );
319
320 visual = vis;
321 }
322 */
323
324 /* Nothing to do for 15, 16, 24, 32 bit displays */
325 if (visual->depth > 8) return TRUE;
326
327 /* this initiates the standard palette as defined by GdkImlib
328 in the GNOME libraries. it ensures that all GNOME applications
329 use the same 64 colormap entries on 8-bit displays so you
330 can use several rather graphics-heavy applications at the
331 same time.
332 NOTE: this doesn't really seem to work this way... */
333
334 /*
335 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
336
337 for (int i = 0; i < 64; i++)
338 {
339 GdkColor col;
340 col.red = g_palette[i*3 + 0] << 8;
341 col.green = g_palette[i*3 + 1] << 8;
342 col.blue = g_palette[i*3 + 2] << 8;
343 col.pixel = 0;
344
345 gdk_color_alloc( cmap, &col );
346 }
347
348 gtk_widget_set_default_colormap( cmap );
349 */
350
351 /* initialize color cube for 8-bit color reduction dithering */
352
353 GdkColormap *cmap = gtk_widget_get_default_colormap();
354
355 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
356
357 for (int r = 0; r < 32; r++)
358 {
359 for (int g = 0; g < 32; g++)
360 {
361 for (int b = 0; b < 32; b++)
362 {
363 int rr = (r << 3) | (r >> 2);
364 int gg = (g << 3) | (g >> 2);
365 int bb = (b << 3) | (b >> 2);
366
367 int index = -1;
368
369 GdkColor *colors = cmap->colors;
370 if (colors)
371 {
372 int max = 3 * 65536;
373
374 for (int i = 0; i < cmap->size; i++)
375 {
376 int rdiff = ((rr << 8) - colors[i].red);
377 int gdiff = ((gg << 8) - colors[i].green);
378 int bdiff = ((bb << 8) - colors[i].blue);
379 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
380 if (sum < max)
381 {
382 index = i; max = sum;
383 }
384 }
385 }
386 else
387 {
388 #if (GTK_MINOR_VERSION > 0)
389 /* assume 8-bit true or static colors. this really
390 exists. */
391 GdkVisual* vis = gdk_colormap_get_visual( cmap );
392 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
393 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
394 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
395 #else
396 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
397 #endif
398 }
399 m_colorCube[ (r*1024) + (g*32) + b ] = index;
400 }
401 }
402 }
403
404 return TRUE;
405 }
406
407 bool wxApp::ProcessIdle()
408 {
409 wxIdleEvent event;
410 event.SetEventObject( this );
411 ProcessEvent( event );
412
413 return event.MoreRequested();
414 }
415
416 void wxApp::OnIdle( wxIdleEvent &event )
417 {
418 static bool s_inOnIdle = FALSE;
419
420 /* Avoid recursion (via ProcessEvent default case) */
421 if (s_inOnIdle)
422 return;
423
424 s_inOnIdle = TRUE;
425
426 /* Resend in the main thread events which have been prepared in other
427 threads */
428 ProcessPendingEvents();
429
430 /* 'Garbage' collection of windows deleted with Close(). */
431 DeletePendingObjects();
432
433 /* flush the logged messages if any */
434 #if wxUSE_LOG
435 wxLog *log = wxLog::GetActiveTarget();
436 if (log != NULL && log->HasPendingMessages())
437 log->Flush();
438 #endif // wxUSE_LOG
439
440 /* Send OnIdle events to all windows */
441 bool needMore = SendIdleEvents();
442
443 if (needMore)
444 event.RequestMore(TRUE);
445
446 s_inOnIdle = FALSE;
447 }
448
449 bool wxApp::SendIdleEvents()
450 {
451 bool needMore = FALSE;
452
453 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
454 while (node)
455 {
456 wxWindow* win = node->GetData();
457 if (SendIdleEvents(win))
458 needMore = TRUE;
459 node = node->GetNext();
460 }
461
462 return needMore;
463 }
464
465 bool wxApp::SendIdleEvents( wxWindow* win )
466 {
467 bool needMore = FALSE;
468
469 wxIdleEvent event;
470 event.SetEventObject(win);
471
472 win->ProcessEvent(event);
473
474 win->OnInternalIdle();
475
476 if (event.MoreRequested())
477 needMore = TRUE;
478
479 wxNode* node = win->GetChildren().First();
480 while (node)
481 {
482 wxWindow* win = (wxWindow*) node->Data();
483 if (SendIdleEvents(win))
484 needMore = TRUE;
485
486 node = node->Next();
487 }
488 return needMore ;
489 }
490
491 int wxApp::MainLoop()
492 {
493 gtk_main();
494 return 0;
495 }
496
497 void wxApp::ExitMainLoop()
498 {
499 gtk_main_quit();
500 }
501
502 bool wxApp::Initialized()
503 {
504 return m_initialized;
505 }
506
507 bool wxApp::Pending()
508 {
509 return (gtk_events_pending() > 0);
510 }
511
512 void wxApp::Dispatch()
513 {
514 gtk_main_iteration();
515 }
516
517 void wxApp::DeletePendingObjects()
518 {
519 wxNode *node = wxPendingDelete.First();
520 while (node)
521 {
522 wxObject *obj = (wxObject *)node->Data();
523
524 delete obj;
525
526 if (wxPendingDelete.Find(obj))
527 delete node;
528
529 node = wxPendingDelete.First();
530 }
531 }
532
533 bool wxApp::Initialize()
534 {
535 wxBuffer = new wxChar[BUFSIZ + 512];
536
537 wxClassInfo::InitializeClasses();
538
539 wxSystemSettings::Init();
540
541 // GL: I'm annoyed ... I don't know where to put this and I don't want to
542 // create a module for that as it's part of the core.
543 #if wxUSE_THREADS
544 wxPendingEvents = new wxList();
545 wxPendingEventsLocker = new wxCriticalSection();
546 #endif
547
548 /*
549 wxTheFontNameDirectory = new wxFontNameDirectory;
550 wxTheFontNameDirectory->Initialize();
551 */
552
553 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
554 wxTheColourDatabase->Initialize();
555
556 wxInitializeStockLists();
557 wxInitializeStockObjects();
558
559 #if wxUSE_WX_RESOURCES
560 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
561
562 wxInitializeResourceSystem();
563 #endif
564
565 wxModule::RegisterModules();
566 if (!wxModule::InitializeModules()) return FALSE;
567
568 return TRUE;
569 }
570
571 void wxApp::CleanUp()
572 {
573 wxModule::CleanUpModules();
574
575 #if wxUSE_WX_RESOURCES
576 wxFlushResources();
577
578 if (wxTheResourceCache)
579 delete wxTheResourceCache;
580 wxTheResourceCache = (wxResourceCache*) NULL;
581
582 wxCleanUpResourceSystem();
583 #endif
584
585 if (wxTheColourDatabase)
586 delete wxTheColourDatabase;
587 wxTheColourDatabase = (wxColourDatabase*) NULL;
588
589 /*
590 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
591 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
592 */
593
594 wxDeleteStockObjects();
595
596 wxDeleteStockLists();
597
598 delete wxTheApp;
599 wxTheApp = (wxApp*) NULL;
600
601 // GL: I'm annoyed ... I don't know where to put this and I don't want to
602 // create a module for that as it's part of the core.
603 #if wxUSE_THREADS
604 delete wxPendingEvents;
605 delete wxPendingEventsLocker;
606 #endif
607
608 wxSystemSettings::Done();
609
610 delete[] wxBuffer;
611
612 wxClassInfo::CleanUpClasses();
613
614 // check for memory leaks
615 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
616 if (wxDebugContext::CountObjectsLeft() > 0)
617 {
618 wxLogDebug(wxT("There were memory leaks.\n"));
619 wxDebugContext::Dump();
620 wxDebugContext::PrintStatistics();
621 }
622 #endif // Debug
623
624 #if wxUSE_LOG
625 // do this as the very last thing because everything else can log messages
626 wxLog::DontCreateOnDemand();
627
628 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
629 if (oldLog)
630 delete oldLog;
631 #endif // wxUSE_LOG
632 }
633
634 //-----------------------------------------------------------------------------
635 // wxEntry
636 //-----------------------------------------------------------------------------
637
638 int wxEntry( int argc, char *argv[] )
639 {
640 gtk_set_locale();
641
642 #if wxUSE_WCHAR_T
643 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
644 #else
645 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
646 #endif
647
648 gtk_init( &argc, &argv );
649
650 wxSetDetectableAutoRepeat( TRUE );
651
652 if (!wxApp::Initialize())
653 return -1;
654
655 if (!wxTheApp)
656 {
657 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
658 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
659
660 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
661
662 wxObject *test_app = app_ini();
663
664 wxTheApp = (wxApp*) test_app;
665 }
666
667 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
668
669 wxTheApp->argc = argc;
670 #if wxUSE_UNICODE
671 wxTheApp->argv = new wxChar*[argc+1];
672 int mb_argc = 0;
673 while (mb_argc < argc) {
674 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
675 mb_argc++;
676 }
677 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
678 #else
679 wxTheApp->argv = argv;
680 #endif
681
682 wxString name(wxFileNameFromPath(argv[0]));
683 wxStripExtension( name );
684 wxTheApp->SetAppName( name );
685
686 int retValue = 0;
687
688 if ( !wxTheApp->OnInitGui() )
689 retValue = -1;
690
691 // Here frames insert themselves automatically into wxTopLevelWindows by
692 // getting created in OnInit().
693 if ( retValue == 0 )
694 {
695 if ( !wxTheApp->OnInit() )
696 retValue = -1;
697 }
698
699 if ( retValue == 0 )
700 {
701 /* delete pending toplevel windows (typically a single
702 dialog) so that, if there isn't any left, we don't
703 call OnRun() */
704 wxTheApp->DeletePendingObjects();
705
706 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
707
708 if (wxTheApp->Initialized())
709 {
710 retValue = wxTheApp->OnRun();
711
712 wxWindow *topWindow = wxTheApp->GetTopWindow();
713 if (topWindow)
714 {
715 /* Forcibly delete the window. */
716 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
717 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
718 {
719 topWindow->Close( TRUE );
720 wxTheApp->DeletePendingObjects();
721 }
722 else
723 {
724 delete topWindow;
725 wxTheApp->SetTopWindow( (wxWindow*) NULL );
726 }
727 }
728 wxTheApp->OnExit();
729 }
730 }
731
732 #if wxUSE_LOG
733 // flush the logged messages if any
734 wxLog *log = wxLog::GetActiveTarget();
735 if (log != NULL && log->HasPendingMessages())
736 log->Flush();
737
738 // continuing to use user defined log target is unsafe from now on because
739 // some resources may be already unavailable, so replace it by something
740 // more safe
741 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
742 if ( oldlog )
743 delete oldlog;
744 #endif // wxUSE_LOG
745
746 wxApp::CleanUp();
747
748 return retValue;
749 }
750
751 #include "wx/gtk/info.xpm"
752 #include "wx/gtk/error.xpm"
753 #include "wx/gtk/question.xpm"
754 #include "wx/gtk/warning.xpm"
755
756 wxIcon
757 wxApp::GetStdIcon(int which) const
758 {
759 switch(which)
760 {
761 case wxICON_INFORMATION:
762 return wxIcon(info_xpm);
763
764 case wxICON_QUESTION:
765 return wxIcon(question_xpm);
766
767 case wxICON_EXCLAMATION:
768 return wxIcon(warning_xpm);
769
770 default:
771 wxFAIL_MSG(wxT("requested non existent standard icon"));
772 // still fall through
773
774 case wxICON_HAND:
775 return wxIcon(error_xpm);
776 }
777 }