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