New "pizza" widget implementation. Window border widths now match the GtkStyle they...
[wxWidgets.git] / src / gtk / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/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 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #include "wx/app.h"
14
15 #ifndef WX_PRECOMP
16 #include "wx/intl.h"
17 #include "wx/log.h"
18 #include "wx/utils.h"
19 #include "wx/memory.h"
20 #include "wx/font.h"
21 #endif
22
23 #include "wx/thread.h"
24
25 #ifdef __WXGPE__
26 #include <gpe/init.h>
27 #endif
28
29 #include "wx/gtk/private.h"
30 #include "wx/apptrait.h"
31
32 #include <gdk/gdkx.h>
33
34 //-----------------------------------------------------------------------------
35 // link GnomeVFS
36 //-----------------------------------------------------------------------------
37
38 #if wxUSE_MIMETYPE && wxUSE_LIBGNOMEVFS
39 #include "wx/link.h"
40 wxFORCE_LINK_MODULE(gnome_vfs)
41 #endif
42
43 //-----------------------------------------------------------------------------
44 // global data
45 //-----------------------------------------------------------------------------
46
47 bool g_mainThreadLocked = false;
48
49 static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
50
51 //-----------------------------------------------------------------------------
52 // wxYield
53 //-----------------------------------------------------------------------------
54
55 // not static because used by textctrl.cpp
56 //
57 // MT-FIXME
58 bool wxIsInsideYield = false;
59
60 bool wxApp::Yield(bool onlyIfNeeded)
61 {
62 if ( wxIsInsideYield )
63 {
64 if ( !onlyIfNeeded )
65 {
66 wxFAIL_MSG( wxT("wxYield called recursively" ) );
67 }
68
69 return false;
70 }
71
72 #if wxUSE_THREADS
73 if ( !wxThread::IsMain() )
74 {
75 // can't call gtk_main_iteration() from other threads like this
76 return true;
77 }
78 #endif // wxUSE_THREADS
79
80 wxIsInsideYield = true;
81
82 #if wxUSE_LOG
83 // disable log flushing from here because a call to wxYield() shouldn't
84 // normally result in message boxes popping up &c
85 wxLog::Suspend();
86 #endif
87
88 while (EventsPending())
89 gtk_main_iteration();
90
91 // It's necessary to call ProcessIdle() to update the frames sizes which
92 // might have been changed (it also will update other things set from
93 // OnUpdateUI() which is a nice (and desired) side effect). But we
94 // call ProcessIdle() only once since this is not meant for longish
95 // background jobs (controlled by wxIdleEvent::RequestMore() and the
96 // return value of Processidle().
97 ProcessIdle();
98
99 #if wxUSE_LOG
100 // let the logs be flashed again
101 wxLog::Resume();
102 #endif
103
104 wxIsInsideYield = false;
105
106 return true;
107 }
108
109 //-----------------------------------------------------------------------------
110 // local functions
111 //-----------------------------------------------------------------------------
112
113 // One-shot signal emission hook, to install idle handler.
114 extern "C" {
115 static gboolean
116 wx_emission_hook(GSignalInvocationHint*, guint, const GValue*, gpointer data)
117 {
118 wxApp* app = wxTheApp;
119 if (app != NULL)
120 app->WakeUpIdle();
121 gulong* hook_id = (gulong*)data;
122 // record that hook is not installed
123 *hook_id = 0;
124 // remove hook
125 return false;
126 }
127 }
128
129 // Add signal emission hooks, to re-install idle handler when needed.
130 static void wx_add_idle_hooks()
131 {
132 // "event" hook
133 {
134 static gulong hook_id = 0;
135 if (hook_id == 0)
136 {
137 static guint sig_id = 0;
138 if (sig_id == 0)
139 sig_id = g_signal_lookup("event", GTK_TYPE_WIDGET);
140 hook_id = g_signal_add_emission_hook(
141 sig_id, 0, wx_emission_hook, &hook_id, NULL);
142 }
143 }
144 // "size_allocate" hook
145 // Needed to match the behavior of the old idle system,
146 // but probably not necessary.
147 {
148 static gulong hook_id = 0;
149 if (hook_id == 0)
150 {
151 static guint sig_id = 0;
152 if (sig_id == 0)
153 sig_id = g_signal_lookup("size_allocate", GTK_TYPE_WIDGET);
154 hook_id = g_signal_add_emission_hook(
155 sig_id, 0, wx_emission_hook, &hook_id, NULL);
156 }
157 }
158 }
159
160 extern "C" {
161 static gboolean wxapp_idle_callback(gpointer)
162 {
163 return wxTheApp->DoIdle();
164 }
165 }
166
167 bool wxApp::DoIdle()
168 {
169 guint id_save;
170 {
171 // Allow another idle source to be added while this one is busy.
172 // Needed if an idle event handler runs a new event loop,
173 // for example by showing a dialog.
174 #if wxUSE_THREADS
175 wxMutexLocker lock(*m_idleMutex);
176 #endif
177 id_save = m_idleSourceId;
178 m_idleSourceId = 0;
179 wx_add_idle_hooks();
180 #ifdef __WXDEBUG__
181 // don't generate the idle events while the assert modal dialog is shown,
182 // this matches the behavior of wxMSW
183 if (m_isInAssert)
184 return false;
185 #endif
186 }
187
188 gdk_threads_enter();
189 bool needMore;
190 do {
191 needMore = ProcessIdle();
192 } while (needMore && gtk_events_pending() == 0);
193 gdk_threads_leave();
194
195 #if wxUSE_THREADS
196 wxMutexLocker lock(*m_idleMutex);
197 #endif
198 // if a new idle source was added during ProcessIdle
199 if (m_idleSourceId != 0)
200 {
201 // remove it
202 g_source_remove(m_idleSourceId);
203 m_idleSourceId = 0;
204 }
205 // if more idle processing requested
206 if (needMore)
207 {
208 // keep this source installed
209 m_idleSourceId = id_save;
210 return true;
211 }
212 // add hooks and remove this source
213 wx_add_idle_hooks();
214 return false;
215 }
216
217 #if wxUSE_THREADS
218
219 static GPollFunc wxgs_poll_func;
220
221 extern "C" {
222 static gint wxapp_poll_func( GPollFD *ufds, guint nfds, gint timeout )
223 {
224 gdk_threads_enter();
225
226 wxMutexGuiLeave();
227 g_mainThreadLocked = true;
228
229 gint res = (*wxgs_poll_func)(ufds, nfds, timeout);
230
231 wxMutexGuiEnter();
232 g_mainThreadLocked = false;
233
234 gdk_threads_leave();
235
236 return res;
237 }
238 }
239
240 #endif // wxUSE_THREADS
241
242 //-----------------------------------------------------------------------------
243 // Access to the root window global
244 //-----------------------------------------------------------------------------
245
246 GtkWidget* wxGetRootWindow()
247 {
248 if (gs_RootWindow == NULL)
249 {
250 gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
251 gtk_widget_realize( gs_RootWindow );
252 }
253 return gs_RootWindow;
254 }
255
256 //-----------------------------------------------------------------------------
257 // wxApp
258 //-----------------------------------------------------------------------------
259
260 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
261
262 wxApp::wxApp()
263 {
264 #ifdef __WXDEBUG__
265 m_isInAssert = false;
266 #endif // __WXDEBUG__
267 #if wxUSE_THREADS
268 m_idleMutex = NULL;
269 #endif
270 m_idleSourceId = 0;
271 }
272
273 wxApp::~wxApp()
274 {
275 }
276
277 bool wxApp::OnInitGui()
278 {
279 if ( !wxAppBase::OnInitGui() )
280 return false;
281
282 // if this is a wxGLApp (derived from wxApp), and we've already
283 // chosen a specific visual, then derive the GdkVisual from that
284 if ( GetXVisualInfo() )
285 {
286 GdkVisual* vis = gtk_widget_get_default_visual();
287
288 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
289 gtk_widget_set_default_colormap( colormap );
290 }
291 else
292 {
293 // On some machines, the default visual is just 256 colours, so
294 // we make sure we get the best. This can sometimes be wasteful.
295 if (m_useBestVisual)
296 {
297 if (m_forceTrueColour)
298 {
299 GdkVisual* visual = gdk_visual_get_best_with_both( 24, GDK_VISUAL_TRUE_COLOR );
300 if (!visual)
301 {
302 wxLogError(wxT("Unable to initialize TrueColor visual."));
303 return false;
304 }
305 GdkColormap *colormap = gdk_colormap_new( visual, FALSE );
306 gtk_widget_set_default_colormap( colormap );
307 }
308 else
309 {
310 if (gdk_visual_get_best() != gdk_visual_get_system())
311 {
312 GdkVisual* visual = gdk_visual_get_best();
313 GdkColormap *colormap = gdk_colormap_new( visual, FALSE );
314 gtk_widget_set_default_colormap( colormap );
315 }
316 }
317 }
318 }
319
320 return true;
321 }
322
323 GdkVisual *wxApp::GetGdkVisual()
324 {
325 GdkVisual *visual = NULL;
326
327 XVisualInfo *xvi = (XVisualInfo *)GetXVisualInfo();
328 if ( xvi )
329 visual = gdkx_visual_get( xvi->visualid );
330 else
331 visual = gdk_drawable_get_visual( wxGetRootWindow()->window );
332
333 wxASSERT( visual );
334
335 return visual;
336 }
337
338 // use unusual names for the parameters to avoid conflict with wxApp::arg[cv]
339 bool wxApp::Initialize(int& argc_, wxChar **argv_)
340 {
341 if ( !wxAppBase::Initialize(argc_, argv_) )
342 return false;
343
344 #if wxUSE_THREADS
345 if (!g_thread_supported())
346 g_thread_init(NULL);
347
348 wxgs_poll_func = g_main_context_get_poll_func(NULL);
349 g_main_context_set_poll_func(NULL, wxapp_poll_func);
350 #endif // wxUSE_THREADS
351
352 // We should have the wxUSE_WCHAR_T test on the _outside_
353 #if wxUSE_WCHAR_T
354 // gtk+ 2.0 supports Unicode through UTF-8 strings
355 wxConvCurrent = &wxConvUTF8;
356 #else // !wxUSE_WCHAR_T
357 if (!wxOKlibc())
358 wxConvCurrent = (wxMBConv*) NULL;
359 #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
360
361 // decide which conversion to use for the file names
362
363 // (1) this variable exists for the sole purpose of specifying the encoding
364 // of the filenames for GTK+ programs, so use it if it is set
365 wxString encName(wxGetenv(_T("G_FILENAME_ENCODING")));
366 encName = encName.BeforeFirst(_T(','));
367 if (encName.CmpNoCase(_T("@locale")) == 0)
368 encName.clear();
369 encName.MakeUpper();
370 #if wxUSE_INTL
371 if (encName.empty())
372 {
373 // (2) if a non default locale is set, assume that the user wants his
374 // filenames in this locale too
375 encName = wxLocale::GetSystemEncodingName().Upper();
376 // (3) finally use UTF-8 by default
377 if (encName.empty() || encName == _T("US-ASCII"))
378 encName = _T("UTF-8");
379 wxSetEnv(_T("G_FILENAME_ENCODING"), encName);
380 }
381 #else
382 if (encName.empty())
383 encName = _T("UTF-8");
384
385 // if wxUSE_INTL==0 it probably indicates that only "C" locale is supported
386 // by the program anyhow so prevent GTK+ from calling setlocale(LC_ALL, "")
387 // from gtk_init_check() as it does by default
388 gtk_disable_setlocale();
389
390 #endif // wxUSE_INTL
391 static wxConvBrokenFileNames fileconv(encName);
392 wxConvFileName = &fileconv;
393
394
395 bool init_result;
396 int i;
397
398 #if wxUSE_UNICODE
399 // gtk_init() wants UTF-8, not wchar_t, so convert
400 char **argvGTK = new char *[argc_ + 1];
401 for ( i = 0; i < argc_; i++ )
402 {
403 argvGTK[i] = wxStrdupA(wxConvUTF8.cWX2MB(argv_[i]));
404 }
405
406 argvGTK[argc_] = NULL;
407
408 int argcGTK = argc_;
409
410 #ifdef __WXGPE__
411 init_result = true; // is there a _check() version of this?
412 gpe_application_init( &argcGTK, &argvGTK );
413 #else
414 init_result = gtk_init_check( &argcGTK, &argvGTK );
415 #endif
416 wxUpdateLocaleIsUtf8();
417
418 if ( argcGTK != argc_ )
419 {
420 // we have to drop the parameters which were consumed by GTK+
421 for ( i = 0; i < argcGTK; i++ )
422 {
423 while ( strcmp(wxConvUTF8.cWX2MB(argv_[i]), argvGTK[i]) != 0 )
424 {
425 memmove(argv_ + i, argv_ + i + 1, (argc_ - i)*sizeof(*argv_));
426 }
427 }
428
429 argc_ = argcGTK;
430 }
431 //else: gtk_init() didn't modify our parameters
432
433 // free our copy
434 for ( i = 0; i < argcGTK; i++ )
435 {
436 free(argvGTK[i]);
437 }
438
439 delete [] argvGTK;
440 #else // !wxUSE_UNICODE
441 // gtk_init() shouldn't actually change argv_ itself (just its contents) so
442 // it's ok to pass pointer to it
443 init_result = gtk_init_check( &argc_, &argv_ );
444 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
445
446 // update internal arg[cv] as GTK+ may have removed processed options:
447 this->argc = argc_;
448 this->argv = argv_;
449
450 if ( m_traits )
451 {
452 // if there are still GTK+ standard options unparsed in the command
453 // line, it means that they were not syntactically correct and GTK+
454 // already printed a warning on the command line and we should now
455 // exit:
456 wxArrayString opt, desc;
457 m_traits->GetStandardCmdLineOptions(opt, desc);
458
459 for ( i = 0; i < argc_; i++ )
460 {
461 // leave just the names of the options with values
462 const wxString str = wxString(argv_[i]).BeforeFirst('=');
463
464 for ( size_t j = 0; j < opt.size(); j++ )
465 {
466 // remove the leading spaces from the option string as it does
467 // have them
468 if ( opt[j].Trim(false).BeforeFirst('=') == str )
469 {
470 // a GTK+ option can be left on the command line only if
471 // there was an error in (or before, in another standard
472 // options) it, so abort, just as we do if incorrect
473 // program option is given
474 wxLogError(_("Invalid GTK+ command line option, use \"%s --help\""),
475 argv_[0]);
476 return false;
477 }
478 }
479 }
480 }
481
482 if ( !init_result )
483 {
484 wxLogError(_("Unable to initialize GTK+, is DISPLAY set properly?"));
485 return false;
486 }
487
488 // we can not enter threads before gtk_init is done
489 gdk_threads_enter();
490
491 wxSetDetectableAutoRepeat( true );
492
493 #if wxUSE_INTL
494 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
495 #endif
496
497 #if wxUSE_THREADS
498 m_idleMutex = new wxMutex;
499 #endif
500 // make sure GtkWidget type is loaded, idle hooks need it
501 g_type_class_ref(GTK_TYPE_WIDGET);
502 WakeUpIdle();
503
504 return true;
505 }
506
507 void wxApp::CleanUp()
508 {
509 if (m_idleSourceId != 0)
510 g_source_remove(m_idleSourceId);
511 #if wxUSE_THREADS
512 delete m_idleMutex;
513 m_idleMutex = NULL;
514 #endif
515 // release reference acquired by Initialize()
516 g_type_class_unref(g_type_class_peek(GTK_TYPE_WIDGET));
517
518 gdk_threads_leave();
519
520 wxAppBase::CleanUp();
521 }
522
523 void wxApp::WakeUpIdle()
524 {
525 #if wxUSE_THREADS
526 wxMutexLocker lock(*m_idleMutex);
527 #endif
528 if (m_idleSourceId == 0)
529 m_idleSourceId = g_idle_add_full(G_PRIORITY_LOW, wxapp_idle_callback, NULL, NULL);
530 }
531
532 // Checking for pending events requires first removing our idle source,
533 // otherwise it will cause the check to always return true.
534 bool wxApp::EventsPending()
535 {
536 #if wxUSE_THREADS
537 wxMutexLocker lock(*m_idleMutex);
538 #endif
539 if (m_idleSourceId != 0)
540 {
541 g_source_remove(m_idleSourceId);
542 m_idleSourceId = 0;
543 wx_add_idle_hooks();
544 }
545 return gtk_events_pending() != 0;
546 }
547
548 #ifdef __WXDEBUG__
549
550 void wxApp::OnAssertFailure(const wxChar *file,
551 int line,
552 const wxChar* func,
553 const wxChar* cond,
554 const wxChar *msg)
555 {
556
557 // block wx idle events while assert dialog is showing
558 m_isInAssert = true;
559
560 wxAppBase::OnAssertFailure(file, line, func, cond, msg);
561
562 m_isInAssert = false;
563 }
564
565 #endif // __WXDEBUG__