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