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