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