]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/app.cpp
fixed MDI accel bug, more tests for it in the 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 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(TRUE) > 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 if ((gtk_major_version == 1) &&
579 (gtk_minor_version == 2) &&
580 (gtk_micro_version < 4))
581 {
582 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
583 }
584 else
585 {
586 g_thread_init(NULL);
587 }
588 #endif
589
590 gtk_set_locale();
591
592 #if defined(__WXGTK20__)
593 // gtk+ 2.0 supports Unicode through UTF-8 strings
594 wxConvCurrent = &wxConvUTF8;
595 #elif wxUSE_WCHAR_T
596 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
597 #else
598 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
599 #endif
600
601 gdk_threads_enter();
602
603 gtk_init( &argc, &argv );
604
605 wxSetDetectableAutoRepeat( TRUE );
606
607 if (!wxApp::Initialize())
608 {
609 gdk_threads_leave();
610 return -1;
611 }
612
613 return 0;
614 }
615
616
617 int wxEntryInitGui()
618 {
619 int retValue = 0;
620
621 if ( !wxTheApp->OnInitGui() )
622 retValue = -1;
623
624 wxRootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
625 gtk_widget_realize( wxRootWindow );
626
627 return retValue;
628 }
629
630
631 void wxEntryCleanup()
632 {
633 #if wxUSE_LOG
634 // flush the logged messages if any
635 wxLog *log = wxLog::GetActiveTarget();
636 if (log != NULL && log->HasPendingMessages())
637 log->Flush();
638
639 // continuing to use user defined log target is unsafe from now on because
640 // some resources may be already unavailable, so replace it by something
641 // more safe
642 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
643 if ( oldlog )
644 delete oldlog;
645 #endif // wxUSE_LOG
646
647 wxApp::CleanUp();
648
649 gdk_threads_leave();
650 }
651
652
653
654 int wxEntry( int argc, char *argv[] )
655 {
656 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
657 // This seems to be necessary since there are 'rogue'
658 // objects present at this point (perhaps global objects?)
659 // Setting a checkpoint will ignore them as far as the
660 // memory checking facility is concerned.
661 // Of course you may argue that memory allocated in globals should be
662 // checked, but this is a reasonable compromise.
663 wxDebugContext::SetCheckpoint();
664 #endif
665 int err = wxEntryStart(argc, argv);
666 if (err)
667 return err;
668
669 if (!wxTheApp)
670 {
671 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
672 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
673
674 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
675
676 wxObject *test_app = app_ini();
677
678 wxTheApp = (wxApp*) test_app;
679 }
680
681 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
682
683 wxTheApp->argc = argc;
684 #if wxUSE_UNICODE
685 wxTheApp->argv = new wxChar*[argc+1];
686 int mb_argc = 0;
687 while (mb_argc < argc)
688 {
689 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
690 mb_argc++;
691 }
692 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
693 #else
694 wxTheApp->argv = argv;
695 #endif
696
697 wxString name(wxFileNameFromPath(argv[0]));
698 wxStripExtension( name );
699 wxTheApp->SetAppName( name );
700
701 int retValue;
702 retValue = wxEntryInitGui();
703
704 // Here frames insert themselves automatically into wxTopLevelWindows by
705 // getting created in OnInit().
706 if ( retValue == 0 )
707 {
708 if ( !wxTheApp->OnInit() )
709 retValue = -1;
710 }
711
712 if ( retValue == 0 )
713 {
714 /* delete pending toplevel windows (typically a single
715 dialog) so that, if there isn't any left, we don't
716 call OnRun() */
717 wxTheApp->DeletePendingObjects();
718
719 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
720
721 if (wxTheApp->Initialized())
722 {
723 wxTheApp->OnRun();
724
725 wxWindow *topWindow = wxTheApp->GetTopWindow();
726 if (topWindow)
727 {
728 /* Forcibly delete the window. */
729 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
730 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
731 {
732 topWindow->Close( TRUE );
733 wxTheApp->DeletePendingObjects();
734 }
735 else
736 {
737 delete topWindow;
738 wxTheApp->SetTopWindow( (wxWindow*) NULL );
739 }
740 }
741
742 retValue = wxTheApp->OnExit();
743 }
744 }
745
746 wxEntryCleanup();
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 }