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