]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/app.cpp
1. wxAppBase class appers, wxApp in wxGTK and wxMSW modified accordingly
[wxWidgets.git] / src / gtk / 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 wxApp::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50 #if wxUSE_THREADS
51 extern wxList *wxPendingEvents;
52 extern wxCriticalSection *wxPendingEventsLocker;
53 #endif
54 extern wxResourceCache *wxTheResourceCache;
55 extern bool g_isIdle;
56
57 unsigned char g_palette[64*3] =
58 {
59 0x0, 0x0, 0x0,
60 0xff, 0xff, 0xff,
61 0xff, 0x0, 0x0,
62 0xff, 0xff, 0x0,
63 0x0, 0xff, 0x0,
64 0x0, 0x0, 0xff,
65 0x0, 0xff, 0xff,
66 0x99, 0x99, 0x99,
67 0xff, 0x88, 0x0,
68 0x88, 0x0, 0x0,
69 0x0, 0x88, 0x88,
70 0x88, 0x88, 0x0,
71 0xff, 0xcc, 0x97,
72 0xbb, 0xbb, 0xbb,
73 0x9f, 0x6b, 0x42,
74 0x55, 0x55, 0x55,
75 0xdd, 0xdd, 0xdd,
76 0x77, 0x77, 0x77,
77 0x33, 0x33, 0x33,
78 0xcc, 0x0, 0x0,
79 0xff, 0x44, 0x0,
80 0xff, 0xcc, 0x0,
81 0xcc, 0xcc, 0x0,
82 0x60, 0x60, 0x0,
83 0x0, 0x43, 0x0,
84 0x0, 0x7f, 0x0,
85 0x0, 0xcc, 0x0,
86 0x0, 0x44, 0x44,
87 0x0, 0x0, 0x44,
88 0x0, 0x0, 0x88,
89 0xef, 0xb1, 0x7b,
90 0xdf, 0x98, 0x5f,
91 0xbf, 0x87, 0x56,
92 0x7f, 0x57, 0x26,
93 0x5f, 0x39, 0xc,
94 0x3f, 0x1c, 0x0,
95 0x21, 0x0, 0x0,
96 0x0, 0x43, 0x87,
97 0x2d, 0x70, 0xaf,
98 0x5a, 0x9e, 0xd7,
99 0x87, 0xcc, 0xff,
100 0xff, 0xe0, 0xba,
101 0x21, 0x43, 0xf,
102 0x3d, 0x5d, 0x25,
103 0x59, 0x78, 0x3a,
104 0x75, 0x93, 0x4f,
105 0x91, 0xae, 0x64,
106 0xad, 0xc8, 0x7a,
107 0xf0, 0xa8, 0xef,
108 0xd0, 0x88, 0xd0,
109 0xaf, 0x66, 0xaf,
110 0x8e, 0x44, 0x8e,
111 0x6d, 0x22, 0x6d,
112 0x4b, 0x0, 0x4b,
113 0xff, 0xc0, 0xbc,
114 0xff, 0x93, 0x91,
115 0xff, 0x66, 0x67,
116 0xd8, 0xf2, 0xbf,
117 0xff, 0xc9, 0x68,
118 0xff, 0x96, 0x67,
119 0xa5, 0x60, 0xff,
120 0x51, 0xff, 0x99,
121 0x3f, 0xa5, 0x63,
122 0x98, 0x90, 0x67
123 };
124
125 //-----------------------------------------------------------------------------
126 // local functions
127 //-----------------------------------------------------------------------------
128
129 extern void wxFlushResources(void);
130
131 //-----------------------------------------------------------------------------
132 // global functions
133 //-----------------------------------------------------------------------------
134
135 void wxExit()
136 {
137 gtk_main_quit();
138 }
139
140 /* forward declaration */
141 gint wxapp_idle_callback( gpointer WXUNUSED(data) );
142
143 bool wxYield()
144 {
145 /* it's necessary to call ProcessIdle() to update the frames sizes which
146 might have been changed (it also will update other things set from
147 OnUpdateUI() which is a nice (and desired) side effect) */
148 while (wxTheApp->ProcessIdle()) { }
149
150 #if 0
151 for ( wxWindowList::Node *node = wxTopLevelWindows.GetFirst();
152 node;
153 node = node->GetNext() )
154 {
155 wxWindow *win = node->GetData();
156 win->OnInternalIdle();
157 }
158 #endif
159
160 if (wxTheApp->m_idleTag)
161 {
162 /* We need to temporarily remove idle callbacks or the loop will
163 never finish. */
164 gtk_idle_remove( wxTheApp->m_idleTag );
165 wxTheApp->m_idleTag = 0;
166
167 while (gtk_events_pending())
168 gtk_main_iteration();
169
170 /* re-add idle handler */
171 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
172 }
173 else
174 {
175 while (gtk_events_pending())
176 gtk_main_iteration();
177 }
178
179 return TRUE;
180 }
181
182 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
183 {
184 if (!wxTheApp) return TRUE;
185
186 #if (GTK_MINOR_VERSION > 0)
187 /* when getting called from GDK's idle handler we
188 are no longer within GDK's grab on the GUI
189 thread so we must lock it here ourselves */
190 GDK_THREADS_ENTER ();
191 #endif
192
193 /* sent idle event to all who request them */
194 while (wxTheApp->ProcessIdle()) { }
195
196 /* we don't want any more idle events until the next event is
197 sent to wxGTK */
198 gtk_idle_remove( wxTheApp->m_idleTag );
199 wxTheApp->m_idleTag = 0;
200
201 /* indicate that we are now in idle mode - even so deeply
202 in idle mode that we don't get any idle events anymore.
203 this is like wxMSW where an idle event is sent only
204 once each time after the event queue has been completely
205 emptied */
206 g_isIdle = TRUE;
207
208 #if (GTK_MINOR_VERSION > 0)
209 /* release lock again */
210 GDK_THREADS_LEAVE ();
211 #endif
212
213 return TRUE;
214 }
215
216 void wxapp_install_idle_handler()
217 {
218 wxASSERT_MSG( wxTheApp->m_idleTag == 0, _T("attempt to install idle handler twice") );
219
220 /* this routine gets called by all event handlers
221 indicating that the idle is over. */
222
223 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
224
225 g_isIdle = FALSE;
226 }
227
228 /*
229 #if wxUSE_THREADS
230 static gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
231 {
232 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
233 wxTheApp->m_wakeUpTimerTag = 0;
234
235 #if (GTK_MINOR_VERSION > 0)
236 // when getting called from GDK's time-out handler
237 // we are no longer within GDK's grab on the GUI
238 // thread so we must lock it here ourselves
239 GDK_THREADS_ENTER ();
240 #endif
241
242 // unblock other threads wishing to do some GUI things
243 wxMutexGuiLeave();
244
245 // wake up other threads
246 wxUsleep( 1 );
247
248 // block other thread again
249 wxMutexGuiEnter();
250
251 #if (GTK_MINOR_VERSION > 0)
252 // release lock again
253 GDK_THREADS_LEAVE ();
254 #endif
255
256 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 20, wxapp_wakeup_timerout_callback, (gpointer) NULL );
257
258 return TRUE;
259 }
260 #endif
261 */
262
263 //-----------------------------------------------------------------------------
264 // wxApp
265 //-----------------------------------------------------------------------------
266
267 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
268
269 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
270 EVT_IDLE(wxApp::OnIdle)
271 END_EVENT_TABLE()
272
273 wxApp::wxApp()
274 {
275 wxTheApp = this;
276
277 m_topWindow = (wxWindow *) NULL;
278 m_exitOnFrameDelete = TRUE;
279
280 m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
281
282 /*
283 #if wxUSE_THREADS
284 m_wakeUpTimerTag = gtk_timeout_add( 20, wxapp_wakeup_timerout_callback, (gpointer) NULL );
285 #endif
286 */
287
288 m_colorCube = (unsigned char*) NULL;
289 }
290
291 wxApp::~wxApp()
292 {
293 if (m_idleTag) gtk_idle_remove( m_idleTag );
294
295 /*
296 #if wxUSE_THREADS
297 if (m_wakeUpTimerTag) gtk_timeout_remove( m_wakeUpTimerTag );
298 #endif
299 */
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( _T("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 inOnIdle = FALSE;
419
420 /* Avoid recursion (via ProcessEvent default case) */
421 if (inOnIdle)
422 return;
423
424 inOnIdle = TRUE;
425
426 #if wxUSE_THREADS
427 /* Resend in the main thread events which have been prepared in other
428 threads */
429 ProcessPendingEvents();
430 #endif
431
432 /* 'Garbage' collection of windows deleted with Close(). */
433 DeletePendingObjects();
434
435 /* flush the logged messages if any */
436 #if wxUSE_LOG
437 wxLog *log = wxLog::GetActiveTarget();
438 if (log != NULL && log->HasPendingMessages())
439 log->Flush();
440 #endif // wxUSE_LOG
441
442 /* Send OnIdle events to all windows */
443 bool needMore = SendIdleEvents();
444
445 if (needMore)
446 event.RequestMore(TRUE);
447
448 inOnIdle = FALSE;
449 }
450
451 bool wxApp::SendIdleEvents()
452 {
453 bool needMore = FALSE;
454
455 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
456 while (node)
457 {
458 wxWindow* win = node->GetData();
459 if (SendIdleEvents(win))
460 needMore = TRUE;
461 node = node->GetNext();
462 }
463
464 return needMore;
465 }
466
467 bool wxApp::SendIdleEvents( wxWindow* win )
468 {
469 bool needMore = FALSE;
470
471 wxIdleEvent event;
472 event.SetEventObject(win);
473
474 win->OnInternalIdle();
475
476 win->ProcessEvent(event);
477
478 if (event.MoreRequested())
479 needMore = TRUE;
480
481 wxNode* node = win->GetChildren().First();
482 while (node)
483 {
484 wxWindow* win = (wxWindow*) node->Data();
485 if (SendIdleEvents(win))
486 needMore = TRUE;
487
488 node = node->Next();
489 }
490 return needMore ;
491 }
492
493 int wxApp::MainLoop()
494 {
495 gtk_main();
496 return 0;
497 }
498
499 void wxApp::ExitMainLoop()
500 {
501 gtk_main_quit();
502 }
503
504 bool wxApp::Initialized()
505 {
506 return m_initialized;
507 }
508
509 bool wxApp::Pending()
510 {
511 return (gtk_events_pending() > 0);
512 }
513
514 void wxApp::Dispatch()
515 {
516 gtk_main_iteration();
517 }
518
519 #if wxUSE_THREADS
520 void wxApp::ProcessPendingEvents()
521 {
522 wxNode *node = wxPendingEvents->First();
523 wxCriticalSectionLocker locker(*wxPendingEventsLocker);
524
525 while (node)
526 {
527 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
528
529 handler->ProcessPendingEvents();
530
531 delete node;
532
533 node = wxPendingEvents->First();
534 }
535 }
536 #endif // wxUSE_THREADS
537
538 void wxApp::DeletePendingObjects()
539 {
540 wxNode *node = wxPendingDelete.First();
541 while (node)
542 {
543 wxObject *obj = (wxObject *)node->Data();
544
545 delete obj;
546
547 if (wxPendingDelete.Find(obj))
548 delete node;
549
550 node = wxPendingDelete.First();
551 }
552 }
553
554 bool wxApp::Initialize()
555 {
556 wxBuffer = new wxChar[BUFSIZ + 512];
557
558 wxClassInfo::InitializeClasses();
559
560 wxSystemSettings::Init();
561
562 // GL: I'm annoyed ... I don't know where to put this and I don't want to
563 // create a module for that as it's part of the core.
564 #if wxUSE_THREADS
565 wxPendingEvents = new wxList();
566 wxPendingEventsLocker = new wxCriticalSection();
567 #endif
568
569 /*
570 wxTheFontNameDirectory = new wxFontNameDirectory;
571 wxTheFontNameDirectory->Initialize();
572 */
573
574 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
575 wxTheColourDatabase->Initialize();
576
577 wxInitializeStockLists();
578 wxInitializeStockObjects();
579
580 #if wxUSE_WX_RESOURCES
581 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
582
583 wxInitializeResourceSystem();
584 #endif
585
586 wxImage::InitStandardHandlers();
587
588 wxModule::RegisterModules();
589 if (!wxModule::InitializeModules()) return FALSE;
590
591 return TRUE;
592 }
593
594 void wxApp::CleanUp()
595 {
596 wxModule::CleanUpModules();
597
598 #if wxUSE_WX_RESOURCES
599 wxFlushResources();
600
601 if (wxTheResourceCache)
602 delete wxTheResourceCache;
603 wxTheResourceCache = (wxResourceCache*) NULL;
604
605 wxCleanUpResourceSystem();
606 #endif
607
608 if (wxTheColourDatabase)
609 delete wxTheColourDatabase;
610 wxTheColourDatabase = (wxColourDatabase*) NULL;
611
612 /*
613 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
614 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
615 */
616
617 wxDeleteStockObjects();
618
619 wxDeleteStockLists();
620
621 wxImage::CleanUpHandlers();
622
623 delete wxTheApp;
624 wxTheApp = (wxApp*) NULL;
625
626 // GL: I'm annoyed ... I don't know where to put this and I don't want to
627 // create a module for that as it's part of the core.
628 #if wxUSE_THREADS
629 delete wxPendingEvents;
630 delete wxPendingEventsLocker;
631 #endif
632
633 wxSystemSettings::Done();
634
635 delete[] wxBuffer;
636
637 wxClassInfo::CleanUpClasses();
638
639 // check for memory leaks
640 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
641 if (wxDebugContext::CountObjectsLeft() > 0)
642 {
643 wxLogDebug(_T("There were memory leaks.\n"));
644 wxDebugContext::Dump();
645 wxDebugContext::PrintStatistics();
646 }
647 #endif // Debug
648
649 #if wxUSE_LOG
650 // do this as the very last thing because everything else can log messages
651 wxLog::DontCreateOnDemand();
652
653 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
654 if (oldLog)
655 delete oldLog;
656 }
657
658 #endif // wxUSE_LOG
659
660 //-----------------------------------------------------------------------------
661 // wxEntry
662 //-----------------------------------------------------------------------------
663
664 int wxEntry( int argc, char *argv[] )
665 {
666 gtk_set_locale();
667
668 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
669
670 gtk_init( &argc, &argv );
671
672 wxSetDetectableAutoRepeat( TRUE );
673
674 if (!wxApp::Initialize())
675 return -1;
676
677 if (!wxTheApp)
678 {
679 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
680 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
681
682 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
683
684 wxObject *test_app = app_ini();
685
686 wxTheApp = (wxApp*) test_app;
687 }
688
689 wxCHECK_MSG( wxTheApp, -1, _T("wxWindows error: no application object") );
690
691 wxTheApp->argc = argc;
692 wxTheApp->argv = argv;
693
694 wxString name(wxFileNameFromPath(argv[0]));
695 wxStripExtension( name );
696 wxTheApp->SetAppName( name );
697
698 int retValue = 0;
699
700 if ( !wxTheApp->OnInitGui() )
701 retValue = -1;
702
703 // Here frames insert themselves automatically into wxTopLevelWindows by
704 // getting created in OnInit().
705 if ( retValue == 0 )
706 {
707 if ( !wxTheApp->OnInit() )
708 retValue = -1;
709 }
710
711 if ( retValue == 0 )
712 {
713 /* delete pending toplevel windows (typically a single
714 dialog) so that, if there isn't any left, we don't
715 call OnRun() */
716 wxTheApp->DeletePendingObjects();
717
718 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
719
720 if (wxTheApp->Initialized())
721 {
722 retValue = wxTheApp->OnRun();
723
724 wxWindow *topWindow = wxTheApp->GetTopWindow();
725 if (topWindow)
726 {
727 /* Forcibly delete the window. */
728 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
729 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
730 {
731 topWindow->Close( TRUE );
732 wxTheApp->DeletePendingObjects();
733 }
734 else
735 {
736 delete topWindow;
737 wxTheApp->SetTopWindow( (wxWindow*) NULL );
738 }
739 }
740 wxTheApp->OnExit();
741 }
742 }
743
744 #if wxUSE_LOG
745 // flush the logged messages if any
746 wxLog *log = wxLog::GetActiveTarget();
747 if (log != NULL && log->HasPendingMessages())
748 log->Flush();
749
750 // continuing to use user defined log target is unsafe from now on because
751 // some resources may be already unavailable, so replace it by something
752 // more safe
753 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
754 if ( oldlog )
755 delete oldlog;
756 #endif // wxUSE_LOG
757
758 wxApp::CleanUp();
759
760 return retValue;
761 }
762
763 #include "wx/gtk/info.xpm"
764 #include "wx/gtk/error.xpm"
765 #include "wx/gtk/question.xpm"
766 #include "wx/gtk/warning.xpm"
767
768 wxIcon
769 wxApp::GetStdIcon(int which) const
770 {
771 switch(which)
772 {
773 case wxICON_INFORMATION:
774 return wxIcon(info_xpm);
775
776 case wxICON_QUESTION:
777 return wxIcon(question_xpm);
778
779 case wxICON_EXCLAMATION:
780 return wxIcon(warning_xpm);
781
782 default:
783 wxFAIL_MSG("requested non existent standard icon");
784 // still fall through
785
786 case wxICON_HAND:
787 return wxIcon(error_xpm);
788 }
789 }