]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/app.cpp
treectrl.cpp renamed to treegtk.cpp due to usage of VPATH in makefiles
[wxWidgets.git] / src / gtk / app.cpp
... / ...
CommitLineData
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
45wxApp *wxTheApp = (wxApp *) NULL;
46wxAppInitializerFunction wxApp::m_appInitFn = (wxAppInitializerFunction) NULL;
47
48#if wxUSE_THREADS
49extern wxList *wxPendingEvents;
50extern wxCriticalSection *wxPendingEventsLocker;
51#endif
52extern wxResourceCache *wxTheResourceCache;
53extern bool g_isIdle;
54
55unsigned 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
127extern void wxFlushResources(void);
128
129//-----------------------------------------------------------------------------
130// global functions
131//-----------------------------------------------------------------------------
132
133void wxExit()
134{
135 gtk_main_quit();
136}
137
138/* forward declaration */
139gint wxapp_idle_callback( gpointer WXUNUSED(data) );
140
141bool 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
180gint 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
214void 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
227static 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( 10, wxapp_wakeup_timerout_callback, (gpointer) NULL );
254
255 return TRUE;
256}
257#endif
258
259//-----------------------------------------------------------------------------
260// wxApp
261//-----------------------------------------------------------------------------
262
263IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
264
265BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
266 EVT_IDLE(wxApp::OnIdle)
267END_EVENT_TABLE()
268
269wxApp::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( 10, wxapp_wakeup_timerout_callback, (gpointer) NULL );
280#endif
281
282 m_colorCube = (unsigned char*) NULL;
283}
284
285wxApp::~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
296bool 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
380bool wxApp::ProcessIdle()
381{
382 wxIdleEvent event;
383 event.SetEventObject( this );
384 ProcessEvent( event );
385
386 return event.MoreRequested();
387}
388
389void 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 wxLog *log = wxLog::GetActiveTarget();
410 if (log != NULL && log->HasPendingMessages())
411 log->Flush();
412
413 /* Send OnIdle events to all windows */
414 bool needMore = SendIdleEvents();
415
416 if (needMore)
417 event.RequestMore(TRUE);
418
419 inOnIdle = FALSE;
420}
421
422bool wxApp::SendIdleEvents()
423{
424 bool needMore = FALSE;
425
426 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
427 while (node)
428 {
429 wxWindow* win = node->GetData();
430 if (SendIdleEvents(win))
431 needMore = TRUE;
432 node = node->GetNext();
433 }
434
435 return needMore;
436}
437
438bool wxApp::SendIdleEvents( wxWindow* win )
439{
440 bool needMore = FALSE;
441
442 wxIdleEvent event;
443 event.SetEventObject(win);
444
445 win->OnInternalIdle();
446
447 win->ProcessEvent(event);
448
449 if (event.MoreRequested())
450 needMore = TRUE;
451
452 wxNode* node = win->GetChildren().First();
453 while (node)
454 {
455 wxWindow* win = (wxWindow*) node->Data();
456 if (SendIdleEvents(win))
457 needMore = TRUE;
458
459 node = node->Next();
460 }
461 return needMore ;
462}
463
464int wxApp::MainLoop()
465{
466 gtk_main();
467 return 0;
468}
469
470void wxApp::ExitMainLoop()
471{
472 gtk_main_quit();
473}
474
475bool wxApp::Initialized()
476{
477 return m_initialized;
478}
479
480bool wxApp::Pending()
481{
482 return (gtk_events_pending() > 0);
483}
484
485void wxApp::Dispatch()
486{
487 gtk_main_iteration();
488}
489
490#if wxUSE_THREADS
491void wxApp::ProcessPendingEvents()
492{
493 wxNode *node = wxPendingEvents->First();
494 wxCriticalSectionLocker locker(*wxPendingEventsLocker);
495
496 while (node)
497 {
498 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
499
500 handler->ProcessPendingEvents();
501
502 delete node;
503
504 node = wxPendingEvents->First();
505 }
506}
507#endif // wxUSE_THREADS
508
509void wxApp::DeletePendingObjects()
510{
511 wxNode *node = wxPendingDelete.First();
512 while (node)
513 {
514 wxObject *obj = (wxObject *)node->Data();
515
516 delete obj;
517
518 if (wxPendingDelete.Find(obj))
519 delete node;
520
521 node = wxPendingDelete.First();
522 }
523}
524
525wxWindow *wxApp::GetTopWindow()
526{
527 if (m_topWindow)
528 return m_topWindow;
529 else if (wxTopLevelWindows.GetCount() > 0)
530 return wxTopLevelWindows.GetFirst()->GetData();
531 else
532 return NULL;
533}
534
535void wxApp::SetTopWindow( wxWindow *win )
536{
537 m_topWindow = win;
538}
539
540bool wxApp::Initialize()
541{
542 wxBuffer = new wxChar[BUFSIZ + 512];
543
544 wxClassInfo::InitializeClasses();
545
546 wxSystemSettings::Init();
547
548 // GL: I'm annoyed ... I don't know where to put this and I don't want to
549 // create a module for that as it's part of the core.
550#if wxUSE_THREADS
551 wxPendingEvents = new wxList();
552 wxPendingEventsLocker = new wxCriticalSection();
553#endif
554
555/*
556 wxTheFontNameDirectory = new wxFontNameDirectory;
557 wxTheFontNameDirectory->Initialize();
558*/
559
560 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
561 wxTheColourDatabase->Initialize();
562
563 wxInitializeStockLists();
564 wxInitializeStockObjects();
565
566#if wxUSE_WX_RESOURCES
567 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
568
569 wxInitializeResourceSystem();
570#endif
571
572 wxImage::InitStandardHandlers();
573
574 /* no global cursor under X
575 g_globalCursor = new wxCursor; */
576
577 wxModule::RegisterModules();
578 if (!wxModule::InitializeModules()) return FALSE;
579
580 return TRUE;
581}
582
583void wxApp::CleanUp()
584{
585 wxModule::CleanUpModules();
586
587#if wxUSE_WX_RESOURCES
588 wxFlushResources();
589
590 if (wxTheResourceCache)
591 delete wxTheResourceCache;
592 wxTheResourceCache = (wxResourceCache*) NULL;
593
594 wxCleanUpResourceSystem();
595#endif
596
597 if (wxTheColourDatabase)
598 delete wxTheColourDatabase;
599 wxTheColourDatabase = (wxColourDatabase*) NULL;
600
601/*
602 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
603 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
604*/
605
606 wxDeleteStockObjects();
607
608 wxDeleteStockLists();
609
610 wxImage::CleanUpHandlers();
611
612 delete wxTheApp;
613 wxTheApp = (wxApp*) NULL;
614
615 // GL: I'm annoyed ... I don't know where to put this and I don't want to
616 // create a module for that as it's part of the core.
617#if wxUSE_THREADS
618 delete wxPendingEvents;
619 delete wxPendingEventsLocker;
620#endif
621
622 wxSystemSettings::Done();
623
624 delete[] wxBuffer;
625
626 wxClassInfo::CleanUpClasses();
627
628 // check for memory leaks
629#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
630 if (wxDebugContext::CountObjectsLeft() > 0)
631 {
632 wxLogDebug(_T("There were memory leaks.\n"));
633 wxDebugContext::Dump();
634 wxDebugContext::PrintStatistics();
635 }
636#endif // Debug
637
638 // do this as the very last thing because everything else can log messages
639 wxLog::DontCreateOnDemand();
640
641 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
642 if (oldLog)
643 delete oldLog;
644}
645
646wxLog *wxApp::CreateLogTarget()
647{
648 return new wxLogGui;
649}
650
651//-----------------------------------------------------------------------------
652// wxEntry
653//-----------------------------------------------------------------------------
654
655int wxEntry( int argc, char *argv[] )
656{
657 gtk_set_locale();
658
659 gtk_init( &argc, &argv );
660
661 wxSetDetectableAutoRepeat( TRUE );
662
663 if (!wxApp::Initialize())
664 return -1;
665
666 if (!wxTheApp)
667 {
668 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
669 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
670
671 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
672
673 wxObject *test_app = app_ini();
674
675 wxTheApp = (wxApp*) test_app;
676 }
677
678 wxCHECK_MSG( wxTheApp, -1, _T("wxWindows error: no application object") );
679
680 wxTheApp->argc = argc;
681 wxTheApp->argv = argv;
682
683 wxString name(wxFileNameFromPath(argv[0]));
684 wxStripExtension( name );
685 wxTheApp->SetAppName( name );
686
687 int retValue = 0;
688
689 if ( !wxTheApp->OnInitGui() )
690 retValue = -1;
691
692 // Here frames insert themselves automatically into wxTopLevelWindows by
693 // getting created in OnInit().
694 if ( retValue == 0 )
695 {
696 if ( !wxTheApp->OnInit() )
697 retValue = -1;
698 }
699
700 if ( retValue == 0 )
701 {
702 /* delete pending toplevel windows (typically a single
703 dialog) so that, if there isn't any left, we don't
704 call OnRun() */
705 wxTheApp->DeletePendingObjects();
706
707 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
708
709 if (wxTheApp->Initialized())
710 {
711 retValue = wxTheApp->OnRun();
712
713 wxWindow *topWindow = wxTheApp->GetTopWindow();
714 if (topWindow)
715 {
716 /* Forcibly delete the window. */
717 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
718 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
719 {
720 topWindow->Close( TRUE );
721 wxTheApp->DeletePendingObjects();
722 }
723 else
724 {
725 delete topWindow;
726 wxTheApp->SetTopWindow( (wxWindow*) NULL );
727 }
728 }
729 wxTheApp->OnExit();
730 }
731 }
732
733 // flush the logged messages if any
734 wxLog *log = wxLog::GetActiveTarget();
735 if (log != NULL && log->HasPendingMessages())
736 log->Flush();
737
738 // continuing to use user defined log target is unsafe from now on because
739 // some resources may be already unavailable, so replace it by something
740 // more safe
741 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
742 if ( oldlog )
743 delete oldlog;
744
745 wxApp::CleanUp();
746
747 return retValue;
748}
749