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