]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/app.cpp
Added wxExtDialog and sample.
[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 #if wxUSE_THREADS
229 static gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
230 {
231 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
232 wxTheApp->m_wakeUpTimerTag = 0;
233
234 #if (GTK_MINOR_VERSION > 0)
235 /* when getting called from GDK's time-out handler
236 we are no longer within GDK's grab on the GUI
237 thread so we must lock it here ourselves */
238 GDK_THREADS_ENTER ();
239 #endif
240
241 /* unblock other threads wishing to do some GUI things */
242 wxMutexGuiLeave();
243
244 /* wake up other threads */
245 wxUsleep( 1 );
246
247 /* block other thread again */
248 wxMutexGuiEnter();
249
250 #if (GTK_MINOR_VERSION > 0)
251 /* release lock again */
252 GDK_THREADS_LEAVE ();
253 #endif
254
255 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 20, wxapp_wakeup_timerout_callback, (gpointer) NULL );
256
257 return TRUE;
258 }
259 #endif
260
261 //-----------------------------------------------------------------------------
262 // wxApp
263 //-----------------------------------------------------------------------------
264
265 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
266
267 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
268 EVT_IDLE(wxApp::OnIdle)
269 END_EVENT_TABLE()
270
271 wxApp::wxApp()
272 {
273 wxTheApp = this;
274
275 m_topWindow = (wxWindow *) NULL;
276 m_exitOnFrameDelete = TRUE;
277
278 m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
279
280 #if wxUSE_THREADS
281 m_wakeUpTimerTag = gtk_timeout_add( 20, wxapp_wakeup_timerout_callback, (gpointer) NULL );
282 #endif
283
284 m_colorCube = (unsigned char*) NULL;
285 }
286
287 wxApp::~wxApp()
288 {
289 if (m_idleTag) gtk_idle_remove( m_idleTag );
290
291 #if wxUSE_THREADS
292 if (m_wakeUpTimerTag) gtk_timeout_remove( m_wakeUpTimerTag );
293 #endif
294
295 if (m_colorCube) free(m_colorCube);
296 }
297
298 bool wxApp::OnInitGui()
299 {
300 GdkVisual *visual = gdk_visual_get_system();
301
302 /* on some machines, the default visual is just 256 colours, so
303 we make sure we get the best. this can sometimes be wasteful,
304 of course, but what do these guys pay $30.000 for? */
305 /*
306 if (gdk_visual_get_best() != gdk_visual_get_system())
307 {
308 GdkVisual* vis = gdk_visual_get_best();
309 gtk_widget_set_default_visual( vis );
310
311 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
312 gtk_widget_set_default_colormap( colormap );
313
314 visual = vis;
315 }
316 */
317
318 /* Nothing to do for 15, 16, 24, 32 bit displays */
319 if (visual->depth > 8) return TRUE;
320
321 /* this initiates the standard palette as defined by GdkImlib
322 in the GNOME libraries. it ensures that all GNOME applications
323 use the same 64 colormap entries on 8-bit displays so you
324 can use several rather graphics-heavy applications at the
325 same time.
326 NOTE: this doesn't really seem to work this way... */
327
328 /*
329 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
330
331 for (int i = 0; i < 64; i++)
332 {
333 GdkColor col;
334 col.red = g_palette[i*3 + 0] << 8;
335 col.green = g_palette[i*3 + 1] << 8;
336 col.blue = g_palette[i*3 + 2] << 8;
337 col.pixel = 0;
338
339 gdk_color_alloc( cmap, &col );
340 }
341
342 gtk_widget_set_default_colormap( cmap );
343 */
344
345 /* initialize color cube for 8-bit color reduction dithering */
346
347 GdkColormap *cmap = gtk_widget_get_default_colormap();
348
349 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
350
351 for (int r = 0; r < 32; r++)
352 {
353 for (int g = 0; g < 32; g++)
354 {
355 for (int b = 0; b < 32; b++)
356 {
357 int rr = (r << 3) | (r >> 2);
358 int gg = (g << 3) | (g >> 2);
359 int bb = (b << 3) | (b >> 2);
360
361 int index = -1;
362
363 GdkColor *colors = cmap->colors;
364 if (colors)
365 {
366 int max = 3 * 65536;
367
368 for (int i = 0; i < cmap->size; i++)
369 {
370 int rdiff = ((rr << 8) - colors[i].red);
371 int gdiff = ((gg << 8) - colors[i].green);
372 int bdiff = ((bb << 8) - colors[i].blue);
373 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
374 if (sum < max)
375 {
376 index = i; max = sum;
377 }
378 }
379 }
380 else
381 {
382 #if (GTK_MINOR_VERSION > 0)
383 /* assume 8-bit true or static colors. this really
384 exists. */
385 GdkVisual* vis = gdk_colormap_get_visual( cmap );
386 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
387 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
388 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
389 #else
390 wxFAIL_MSG( _T("Unsupported graphics hardware") );
391 #endif
392 }
393 m_colorCube[ (r*1024) + (g*32) + b ] = index;
394 }
395 }
396 }
397
398 return TRUE;
399 }
400
401 bool wxApp::ProcessIdle()
402 {
403 wxIdleEvent event;
404 event.SetEventObject( this );
405 ProcessEvent( event );
406
407 return event.MoreRequested();
408 }
409
410 void wxApp::OnIdle( wxIdleEvent &event )
411 {
412 static bool inOnIdle = FALSE;
413
414 /* Avoid recursion (via ProcessEvent default case) */
415 if (inOnIdle)
416 return;
417
418 inOnIdle = TRUE;
419
420 #if wxUSE_THREADS
421 /* Resend in the main thread events which have been prepared in other
422 threads */
423 ProcessPendingEvents();
424 #endif
425
426 /* 'Garbage' collection of windows deleted with Close(). */
427 DeletePendingObjects();
428
429 /* flush the logged messages if any */
430 #if wxUSE_LOG
431 wxLog *log = wxLog::GetActiveTarget();
432 if (log != NULL && log->HasPendingMessages())
433 log->Flush();
434 #endif // wxUSE_LOG
435
436 /* Send OnIdle events to all windows */
437 bool needMore = SendIdleEvents();
438
439 if (needMore)
440 event.RequestMore(TRUE);
441
442 inOnIdle = FALSE;
443 }
444
445 bool wxApp::SendIdleEvents()
446 {
447 bool needMore = FALSE;
448
449 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
450 while (node)
451 {
452 wxWindow* win = node->GetData();
453 if (SendIdleEvents(win))
454 needMore = TRUE;
455 node = node->GetNext();
456 }
457
458 return needMore;
459 }
460
461 bool wxApp::SendIdleEvents( wxWindow* win )
462 {
463 bool needMore = FALSE;
464
465 wxIdleEvent event;
466 event.SetEventObject(win);
467
468 win->OnInternalIdle();
469
470 win->ProcessEvent(event);
471
472 if (event.MoreRequested())
473 needMore = TRUE;
474
475 wxNode* node = win->GetChildren().First();
476 while (node)
477 {
478 wxWindow* win = (wxWindow*) node->Data();
479 if (SendIdleEvents(win))
480 needMore = TRUE;
481
482 node = node->Next();
483 }
484 return needMore ;
485 }
486
487 int wxApp::MainLoop()
488 {
489 gtk_main();
490 return 0;
491 }
492
493 void wxApp::ExitMainLoop()
494 {
495 gtk_main_quit();
496 }
497
498 bool wxApp::Initialized()
499 {
500 return m_initialized;
501 }
502
503 bool wxApp::Pending()
504 {
505 return (gtk_events_pending() > 0);
506 }
507
508 void wxApp::Dispatch()
509 {
510 gtk_main_iteration();
511 }
512
513 #if wxUSE_THREADS
514 void wxApp::ProcessPendingEvents()
515 {
516 wxNode *node = wxPendingEvents->First();
517 wxCriticalSectionLocker locker(*wxPendingEventsLocker);
518
519 while (node)
520 {
521 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
522
523 handler->ProcessPendingEvents();
524
525 delete node;
526
527 node = wxPendingEvents->First();
528 }
529 }
530 #endif // wxUSE_THREADS
531
532 void wxApp::DeletePendingObjects()
533 {
534 wxNode *node = wxPendingDelete.First();
535 while (node)
536 {
537 wxObject *obj = (wxObject *)node->Data();
538
539 delete obj;
540
541 if (wxPendingDelete.Find(obj))
542 delete node;
543
544 node = wxPendingDelete.First();
545 }
546 }
547
548 wxWindow *wxApp::GetTopWindow()
549 {
550 if (m_topWindow)
551 return m_topWindow;
552 else if (wxTopLevelWindows.GetCount() > 0)
553 return wxTopLevelWindows.GetFirst()->GetData();
554 else
555 return NULL;
556 }
557
558 void wxApp::SetTopWindow( wxWindow *win )
559 {
560 m_topWindow = win;
561 }
562
563 bool wxApp::Initialize()
564 {
565 wxBuffer = new wxChar[BUFSIZ + 512];
566
567 wxClassInfo::InitializeClasses();
568
569 wxSystemSettings::Init();
570
571 // GL: I'm annoyed ... I don't know where to put this and I don't want to
572 // create a module for that as it's part of the core.
573 #if wxUSE_THREADS
574 wxPendingEvents = new wxList();
575 wxPendingEventsLocker = new wxCriticalSection();
576 #endif
577
578 /*
579 wxTheFontNameDirectory = new wxFontNameDirectory;
580 wxTheFontNameDirectory->Initialize();
581 */
582
583 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
584 wxTheColourDatabase->Initialize();
585
586 wxInitializeStockLists();
587 wxInitializeStockObjects();
588
589 #if wxUSE_WX_RESOURCES
590 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
591
592 wxInitializeResourceSystem();
593 #endif
594
595 wxImage::InitStandardHandlers();
596
597 wxModule::RegisterModules();
598 if (!wxModule::InitializeModules()) return FALSE;
599
600 return TRUE;
601 }
602
603 void wxApp::CleanUp()
604 {
605 wxModule::CleanUpModules();
606
607 #if wxUSE_WX_RESOURCES
608 wxFlushResources();
609
610 if (wxTheResourceCache)
611 delete wxTheResourceCache;
612 wxTheResourceCache = (wxResourceCache*) NULL;
613
614 wxCleanUpResourceSystem();
615 #endif
616
617 if (wxTheColourDatabase)
618 delete wxTheColourDatabase;
619 wxTheColourDatabase = (wxColourDatabase*) NULL;
620
621 /*
622 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
623 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
624 */
625
626 wxDeleteStockObjects();
627
628 wxDeleteStockLists();
629
630 wxImage::CleanUpHandlers();
631
632 delete wxTheApp;
633 wxTheApp = (wxApp*) NULL;
634
635 // GL: I'm annoyed ... I don't know where to put this and I don't want to
636 // create a module for that as it's part of the core.
637 #if wxUSE_THREADS
638 delete wxPendingEvents;
639 delete wxPendingEventsLocker;
640 #endif
641
642 wxSystemSettings::Done();
643
644 delete[] wxBuffer;
645
646 wxClassInfo::CleanUpClasses();
647
648 // check for memory leaks
649 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
650 if (wxDebugContext::CountObjectsLeft() > 0)
651 {
652 wxLogDebug(_T("There were memory leaks.\n"));
653 wxDebugContext::Dump();
654 wxDebugContext::PrintStatistics();
655 }
656 #endif // Debug
657
658 #if wxUSE_LOG
659 // do this as the very last thing because everything else can log messages
660 wxLog::DontCreateOnDemand();
661
662 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
663 if (oldLog)
664 delete oldLog;
665 }
666
667 wxLog *wxApp::CreateLogTarget()
668 {
669 return new wxLogGui;
670 }
671 #endif // wxUSE_LOG
672
673 //-----------------------------------------------------------------------------
674 // wxEntry
675 //-----------------------------------------------------------------------------
676
677 int wxEntry( int argc, char *argv[] )
678 {
679 gtk_set_locale();
680
681 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
682
683 gtk_init( &argc, &argv );
684
685 wxSetDetectableAutoRepeat( TRUE );
686
687 if (!wxApp::Initialize())
688 return -1;
689
690 if (!wxTheApp)
691 {
692 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
693 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
694
695 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
696
697 wxObject *test_app = app_ini();
698
699 wxTheApp = (wxApp*) test_app;
700 }
701
702 wxCHECK_MSG( wxTheApp, -1, _T("wxWindows error: no application object") );
703
704 wxTheApp->argc = argc;
705 wxTheApp->argv = argv;
706
707 wxString name(wxFileNameFromPath(argv[0]));
708 wxStripExtension( name );
709 wxTheApp->SetAppName( name );
710
711 int retValue = 0;
712
713 if ( !wxTheApp->OnInitGui() )
714 retValue = -1;
715
716 // Here frames insert themselves automatically into wxTopLevelWindows by
717 // getting created in OnInit().
718 if ( retValue == 0 )
719 {
720 if ( !wxTheApp->OnInit() )
721 retValue = -1;
722 }
723
724 if ( retValue == 0 )
725 {
726 /* delete pending toplevel windows (typically a single
727 dialog) so that, if there isn't any left, we don't
728 call OnRun() */
729 wxTheApp->DeletePendingObjects();
730
731 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
732
733 if (wxTheApp->Initialized())
734 {
735 retValue = wxTheApp->OnRun();
736
737 wxWindow *topWindow = wxTheApp->GetTopWindow();
738 if (topWindow)
739 {
740 /* Forcibly delete the window. */
741 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
742 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
743 {
744 topWindow->Close( TRUE );
745 wxTheApp->DeletePendingObjects();
746 }
747 else
748 {
749 delete topWindow;
750 wxTheApp->SetTopWindow( (wxWindow*) NULL );
751 }
752 }
753 wxTheApp->OnExit();
754 }
755 }
756
757 #if wxUSE_LOG
758 // flush the logged messages if any
759 wxLog *log = wxLog::GetActiveTarget();
760 if (log != NULL && log->HasPendingMessages())
761 log->Flush();
762
763 // continuing to use user defined log target is unsafe from now on because
764 // some resources may be already unavailable, so replace it by something
765 // more safe
766 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
767 if ( oldlog )
768 delete oldlog;
769 #endif // wxUSE_LOG
770
771 wxApp::CleanUp();
772
773 return retValue;
774 }
775