avoid deprecated functions and direct struct access
[wxWidgets.git] / src / gtk / toplevel.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/toplevel.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 #ifdef __VMS
22 #define XIconifyWindow XICONIFYWINDOW
23 #endif
24
25 #include "wx/toplevel.h"
26
27 #ifndef WX_PRECOMP
28 #include "wx/frame.h"
29 #include "wx/icon.h"
30 #include "wx/log.h"
31 #include "wx/app.h"
32 #endif
33
34 #include "wx/gtk/private.h"
35 #include "wx/evtloop.h"
36 #include "wx/sysopt.h"
37
38 #include <gtk/gtk.h>
39 #include <gdk/gdkx.h>
40
41 #include "wx/gtk/private/win_gtk.h"
42
43 #include "wx/unix/utilsx11.h"
44
45 // XA_CARDINAL
46 #include <X11/Xatom.h>
47
48 #if wxUSE_LIBHILDON
49 #include <hildon-widgets/hildon-program.h>
50 #include <hildon-widgets/hildon-window.h>
51 #endif // wxUSE_LIBHILDON
52
53 #if wxUSE_LIBHILDON2
54 #include <hildon/hildon.h>
55 #endif // wxUSE_LIBHILDON2
56
57 // ----------------------------------------------------------------------------
58 // data
59 // ----------------------------------------------------------------------------
60
61 // this is incremented while a modal dialog is shown
62 int wxOpenModalDialogsCount = 0;
63
64 // the frame that is currently active (i.e. its child has focus). It is
65 // used to generate wxActivateEvents
66 static wxTopLevelWindowGTK *g_activeFrame = NULL;
67 static wxTopLevelWindowGTK *g_lastActiveFrame = NULL;
68
69 // if we detect that the app has got/lost the focus, we set this variable to
70 // either TRUE or FALSE and an activate event will be sent during the next
71 // OnIdle() call and it is reset to -1: this value means that we shouldn't
72 // send any activate events at all
73 static int g_sendActivateEvent = -1;
74
75 //-----------------------------------------------------------------------------
76 // RequestUserAttention related functions
77 //-----------------------------------------------------------------------------
78
79 extern "C" {
80 static void wxgtk_window_set_urgency_hint (GtkWindow *win,
81 gboolean setting)
82 {
83 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(win));
84 wxASSERT_MSG(window, "wxgtk_window_set_urgency_hint: GdkWindow not realized");
85 XWMHints *wm_hints;
86
87 wm_hints = XGetWMHints(GDK_WINDOW_XDISPLAY(window), GDK_WINDOW_XWINDOW(window));
88
89 if (!wm_hints)
90 wm_hints = XAllocWMHints();
91
92 if (setting)
93 wm_hints->flags |= XUrgencyHint;
94 else
95 wm_hints->flags &= ~XUrgencyHint;
96
97 XSetWMHints(GDK_WINDOW_XDISPLAY(window), GDK_WINDOW_XWINDOW(window), wm_hints);
98 XFree(wm_hints);
99 }
100
101 static gboolean gtk_frame_urgency_timer_callback( wxTopLevelWindowGTK *win )
102 {
103 #if GTK_CHECK_VERSION(2,7,0)
104 if(!gtk_check_version(2,7,0))
105 gtk_window_set_urgency_hint(GTK_WINDOW( win->m_widget ), FALSE);
106 else
107 #endif
108 wxgtk_window_set_urgency_hint(GTK_WINDOW( win->m_widget ), FALSE);
109
110 win->m_urgency_hint = -2;
111 return FALSE;
112 }
113 }
114
115 //-----------------------------------------------------------------------------
116 // "focus_in_event"
117 //-----------------------------------------------------------------------------
118
119 extern "C" {
120 static gboolean gtk_frame_focus_in_callback( GtkWidget *widget,
121 GdkEvent *WXUNUSED(event),
122 wxTopLevelWindowGTK *win )
123 {
124 switch ( g_sendActivateEvent )
125 {
126 case -1:
127 // we've got focus from outside, synthetize wxActivateEvent
128 g_sendActivateEvent = 1;
129 break;
130
131 case 0:
132 // another our window just lost focus, it was already ours before
133 // - don't send any wxActivateEvent
134 g_sendActivateEvent = -1;
135 break;
136 }
137
138 g_activeFrame = win;
139 g_lastActiveFrame = g_activeFrame;
140
141 // wxPrintf( wxT("active: %s\n"), win->GetTitle().c_str() );
142
143 // MR: wxRequestUserAttention related block
144 switch( win->m_urgency_hint )
145 {
146 default:
147 g_source_remove( win->m_urgency_hint );
148 // no break, fallthrough to remove hint too
149 case -1:
150 #if GTK_CHECK_VERSION(2,7,0)
151 if(!gtk_check_version(2,7,0))
152 gtk_window_set_urgency_hint(GTK_WINDOW( widget ), FALSE);
153 else
154 #endif
155 {
156 wxgtk_window_set_urgency_hint(GTK_WINDOW( widget ), FALSE);
157 }
158
159 win->m_urgency_hint = -2;
160 break;
161
162 case -2: break;
163 }
164
165 wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), g_activeFrame);
166 wxActivateEvent event(wxEVT_ACTIVATE, true, g_activeFrame->GetId());
167 event.SetEventObject(g_activeFrame);
168 g_activeFrame->HandleWindowEvent(event);
169
170 return FALSE;
171 }
172 }
173
174 //-----------------------------------------------------------------------------
175 // "focus_out_event"
176 //-----------------------------------------------------------------------------
177
178 extern "C" {
179 static
180 gboolean gtk_frame_focus_out_callback(GtkWidget * WXUNUSED(widget),
181 GdkEventFocus *WXUNUSED(gdk_event),
182 wxTopLevelWindowGTK * WXUNUSED(win))
183 {
184 // if the focus goes out of our app alltogether, OnIdle() will send
185 // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
186 // g_sendActivateEvent to -1
187 g_sendActivateEvent = 0;
188
189 // wxASSERT_MSG( (g_activeFrame == win), wxT("TLW deactivatd although it wasn't active") );
190
191 // wxPrintf( wxT("inactive: %s\n"), win->GetTitle().c_str() );
192
193 if (g_activeFrame)
194 {
195 wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), g_activeFrame);
196 wxActivateEvent event(wxEVT_ACTIVATE, false, g_activeFrame->GetId());
197 event.SetEventObject(g_activeFrame);
198 g_activeFrame->HandleWindowEvent(event);
199
200 g_activeFrame = NULL;
201 }
202
203 return FALSE;
204 }
205 }
206
207 //-----------------------------------------------------------------------------
208 // "size_allocate" from m_wxwindow
209 //-----------------------------------------------------------------------------
210
211 extern "C" {
212 static void
213 size_allocate(GtkWidget*, GtkAllocation* alloc, wxTopLevelWindowGTK* win)
214 {
215 if (win->m_oldClientWidth != alloc->width ||
216 win->m_oldClientHeight != alloc->height)
217 {
218 win->m_oldClientWidth = alloc->width;
219 win->m_oldClientHeight = alloc->height;
220
221 GtkAllocation a;
222 gtk_widget_get_allocation(win->m_widget, &a);
223 wxSize size(a.width, a.height);
224 size += win->m_decorSize;
225 win->m_width = size.x;
226 win->m_height = size.y;
227
228 if (!win->IsIconized())
229 {
230 wxSizeEvent event(size, win->GetId());
231 event.SetEventObject(win);
232 win->HandleWindowEvent(event);
233 }
234 // else the window is currently unmapped, don't generate size events
235 }
236 }
237 }
238
239 // ----------------------------------------------------------------------------
240 // "size_request"
241 // ----------------------------------------------------------------------------
242
243 extern "C" {
244 static
245 void wxgtk_tlw_size_request_callback(GtkWidget * WXUNUSED(widget),
246 GtkRequisition *requisition,
247 wxTopLevelWindowGTK *win)
248 {
249 // we must return the size of the window without WM decorations, otherwise
250 // GTK+ gets confused, so don't call just GetSize() here
251 win->GTKDoGetSize(&requisition->width, &requisition->height);
252 }
253 }
254
255 //-----------------------------------------------------------------------------
256 // "delete_event"
257 //-----------------------------------------------------------------------------
258
259 extern "C" {
260 static gboolean
261 gtk_frame_delete_callback( GtkWidget *WXUNUSED(widget),
262 GdkEvent *WXUNUSED(event),
263 wxTopLevelWindowGTK *win )
264 {
265 if (win->IsEnabled() &&
266 (wxOpenModalDialogsCount == 0 || (win->GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) ||
267 win->IsGrabbed()))
268 win->Close();
269
270 return TRUE;
271 }
272 }
273
274 //-----------------------------------------------------------------------------
275 // "configure_event"
276 //-----------------------------------------------------------------------------
277
278 extern "C" {
279 static gboolean
280 gtk_frame_configure_callback( GtkWidget* widget,
281 GdkEventConfigure *WXUNUSED(event),
282 wxTopLevelWindowGTK *win )
283 {
284 if (!win->m_hasVMT || !win->IsShown())
285 return FALSE;
286
287 wxPoint point;
288 gtk_window_get_position((GtkWindow*)widget, &point.x, &point.y);
289
290 win->m_x = point.x;
291 win->m_y = point.y;
292 wxMoveEvent mevent(point, win->GetId());
293 mevent.SetEventObject( win );
294 win->HandleWindowEvent( mevent );
295
296 return FALSE;
297 }
298 }
299
300 //-----------------------------------------------------------------------------
301 // "realize" from m_widget
302 //-----------------------------------------------------------------------------
303
304 // we cannot the WM hints and icons before the widget has been realized,
305 // so we do this directly after realization
306
307 extern "C" {
308 static void
309 gtk_frame_realized_callback( GtkWidget * WXUNUSED(widget),
310 wxTopLevelWindowGTK *win )
311 {
312 gdk_window_set_decorations(gtk_widget_get_window(win->m_widget),
313 (GdkWMDecoration)win->m_gdkDecor);
314 gdk_window_set_functions(gtk_widget_get_window(win->m_widget),
315 (GdkWMFunction)win->m_gdkFunc);
316
317 // GTK's shrinking/growing policy
318 if ( !(win->m_gdkFunc & GDK_FUNC_RESIZE) )
319 gtk_window_set_resizable(GTK_WINDOW(win->m_widget), FALSE);
320 #if !GTK_CHECK_VERSION(3,0,0) && !defined(GTK_DISABLE_DEPRECATED)
321 else
322 gtk_window_set_policy(GTK_WINDOW(win->m_widget), 1, 1, 1);
323 #endif
324
325 const wxIconBundle& icons = win->GetIcons();
326 if (icons.GetIconCount())
327 win->SetIcons(icons);
328
329 if (win->HasFlag(wxFRAME_SHAPED))
330 win->SetShape(win->m_shape); // it will really set the window shape now
331 }
332 }
333
334 //-----------------------------------------------------------------------------
335 // "map_event" from m_widget
336 //-----------------------------------------------------------------------------
337
338 extern "C" {
339 static gboolean
340 gtk_frame_map_callback( GtkWidget*,
341 GdkEvent * WXUNUSED(event),
342 wxTopLevelWindow *win )
343 {
344 const bool wasIconized = win->IsIconized();
345 if (wasIconized)
346 {
347 // Because GetClientSize() returns (0,0) when IsIconized() is true,
348 // a size event must be generated, just in case GetClientSize() was
349 // called while iconized. This specifically happens when restoring a
350 // tlw that was "rolled up" with some WMs.
351 // Queue a resize rather than sending size event directly to allow
352 // children to be made visible first.
353 win->m_oldClientWidth = 0;
354 gtk_widget_queue_resize(win->m_wxwindow);
355 }
356 // it is possible for m_isShown to be false here, see bug #9909
357 if (win->wxWindowBase::Show(true))
358 {
359 wxShowEvent eventShow(win->GetId(), true);
360 eventShow.SetEventObject(win);
361 win->GetEventHandler()->ProcessEvent(eventShow);
362 }
363
364 #if GTK_CHECK_VERSION(2,6,0)
365 if (!gtk_check_version(2,6,0))
366 {
367 // restore focus-on-map setting in case ShowWithoutActivating() was called
368 gtk_window_set_focus_on_map(GTK_WINDOW(win->m_widget), true);
369 }
370 #endif // GTK+ 2.6+
371
372 return false;
373 }
374 }
375
376 //-----------------------------------------------------------------------------
377 // "window-state-event" from m_widget
378 //-----------------------------------------------------------------------------
379
380 extern "C" {
381 static gboolean
382 gtk_frame_window_state_callback( GtkWidget* WXUNUSED(widget),
383 GdkEventWindowState *event,
384 wxTopLevelWindow *win )
385 {
386 if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)
387 win->SetIconizeState((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
388
389 // if maximized bit changed and it is now set
390 if (event->changed_mask & event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
391 {
392 wxMaximizeEvent event(win->GetId());
393 event.SetEventObject(win);
394 win->HandleWindowEvent(event);
395 }
396
397 return false;
398 }
399 }
400
401 //-----------------------------------------------------------------------------
402
403 bool wxGetFrameExtents(GdkWindow* window, int* left, int* right, int* top, int* bottom)
404 {
405 static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
406 Atom xproperty = gdk_x11_atom_to_xatom_for_display(
407 gdk_drawable_get_display(window), property);
408 Atom type;
409 int format;
410 gulong nitems, bytes_after;
411 guchar* data;
412 Status status = XGetWindowProperty(
413 gdk_x11_drawable_get_xdisplay(window),
414 gdk_x11_drawable_get_xid(window),
415 xproperty,
416 0, 4, false, XA_CARDINAL,
417 &type, &format, &nitems, &bytes_after, &data);
418 const bool success = status == Success && data && nitems == 4;
419 if (success)
420 {
421 long* p = (long*)data;
422 if (left) *left = int(p[0]);
423 if (right) *right = int(p[1]);
424 if (top) *top = int(p[2]);
425 if (bottom) *bottom = int(p[3]);
426 }
427 if (data)
428 XFree(data);
429 return success;
430 }
431
432 //-----------------------------------------------------------------------------
433 // "property_notify_event" from m_widget
434 //-----------------------------------------------------------------------------
435
436 extern "C" {
437 static gboolean property_notify_event(
438 GtkWidget*, GdkEventProperty* event, wxTopLevelWindowGTK* win)
439 {
440 // Watch for changes to _NET_FRAME_EXTENTS property
441 static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
442 if (event->state == GDK_PROPERTY_NEW_VALUE && event->atom == property)
443 {
444 wxSize decorSize = win->m_decorSize;
445 int left, right, top, bottom;
446 if (wxGetFrameExtents(event->window, &left, &right, &top, &bottom))
447 decorSize.Set(left + right, top + bottom);
448
449 win->GTKUpdateDecorSize(decorSize);
450 }
451 return false;
452 }
453 }
454
455 // ----------------------------------------------------------------------------
456 // wxTopLevelWindowGTK creation
457 // ----------------------------------------------------------------------------
458
459 void wxTopLevelWindowGTK::Init()
460 {
461 m_mainWidget = NULL;
462 m_isIconized = false;
463 m_fsIsShowing = false;
464 m_themeEnabled = true;
465 m_gdkDecor =
466 m_gdkFunc = 0;
467 m_grabbed = false;
468 m_deferShow = true;
469 m_deferShowAllowed = true;
470 m_updateDecorSize = true;
471
472 m_urgency_hint = -2;
473 }
474
475 bool wxTopLevelWindowGTK::Create( wxWindow *parent,
476 wxWindowID id,
477 const wxString& title,
478 const wxPoint& pos,
479 const wxSize& sizeOrig,
480 long style,
481 const wxString &name )
482 {
483 // always create a frame of some reasonable, even if arbitrary, size (at
484 // least for MSW compatibility)
485 wxSize size = sizeOrig;
486 size.x = WidthDefault(size.x);
487 size.y = HeightDefault(size.y);
488
489 wxTopLevelWindows.Append( this );
490
491 if (!PreCreation( parent, pos, size ) ||
492 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
493 {
494 wxFAIL_MSG( wxT("wxTopLevelWindowGTK creation failed") );
495 return false;
496 }
497
498 m_title = title;
499
500 // NB: m_widget may be !=NULL if it was created by derived class' Create,
501 // e.g. in wxTaskBarIconAreaGTK
502 if (m_widget == NULL)
503 {
504 #if wxUSE_LIBHILDON || wxUSE_LIBHILDON2
505 // we must create HildonWindow and not a normal GtkWindow as the latter
506 // doesn't look correctly in Maemo environment and it must also be
507 // registered with the main program object
508 m_widget = hildon_window_new();
509 hildon_program_add_window(wxTheApp->GetHildonProgram(),
510 HILDON_WINDOW(m_widget));
511 #else // !wxUSE_LIBHILDON || !wxUSE_LIBHILDON2
512 m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
513 if (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)
514 {
515 // Tell WM that this is a dialog window and make it center
516 // on parent by default (this is what GtkDialog ctor does):
517 gtk_window_set_type_hint(GTK_WINDOW(m_widget),
518 GDK_WINDOW_TYPE_HINT_DIALOG);
519 gtk_window_set_position(GTK_WINDOW(m_widget),
520 GTK_WIN_POS_CENTER_ON_PARENT);
521 }
522 else
523 {
524 if (style & wxFRAME_TOOL_WINDOW)
525 {
526 gtk_window_set_type_hint(GTK_WINDOW(m_widget),
527 GDK_WINDOW_TYPE_HINT_UTILITY);
528
529 // On some WMs, like KDE, a TOOL_WINDOW will still show
530 // on the taskbar, but on Gnome a TOOL_WINDOW will not.
531 // For consistency between WMs and with Windows, we
532 // should set the NO_TASKBAR flag which will apply
533 // the set_skip_taskbar_hint if it is available,
534 // ensuring no taskbar entry will appear.
535 style |= wxFRAME_NO_TASKBAR;
536 }
537 }
538 #endif // wxUSE_LIBHILDON || wxUSE_LIBHILDON2/!wxUSE_LIBHILDON || !wxUSE_LIBHILDON2
539
540 g_object_ref(m_widget);
541 }
542
543 wxWindow *topParent = wxGetTopLevelParent(m_parent);
544 if (topParent && (((GTK_IS_WINDOW(topParent->m_widget)) &&
545 (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)) ||
546 (style & wxFRAME_FLOAT_ON_PARENT)))
547 {
548 gtk_window_set_transient_for( GTK_WINDOW(m_widget),
549 GTK_WINDOW(topParent->m_widget) );
550 }
551
552 if (style & wxFRAME_NO_TASKBAR)
553 {
554 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), TRUE);
555 }
556
557 if (style & wxSTAY_ON_TOP)
558 {
559 gtk_window_set_keep_above(GTK_WINDOW(m_widget), TRUE);
560 }
561 if (style & wxMAXIMIZE)
562 gtk_window_maximize(GTK_WINDOW(m_widget));
563
564 #if 0
565 if (!name.empty())
566 gtk_window_set_role( GTK_WINDOW(m_widget), wxGTK_CONV( name ) );
567 #endif
568
569 gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
570 gtk_widget_set_can_focus(m_widget, false);
571
572 g_signal_connect (m_widget, "delete_event",
573 G_CALLBACK (gtk_frame_delete_callback), this);
574
575 // m_mainWidget is a GtkVBox, holding the bars and client area (m_wxwindow)
576 m_mainWidget = gtk_vbox_new(false, 0);
577 gtk_widget_show( m_mainWidget );
578 gtk_widget_set_can_focus(m_mainWidget, false);
579 gtk_container_add( GTK_CONTAINER(m_widget), m_mainWidget );
580
581 // m_wxwindow is the client area
582 m_wxwindow = wxPizza::New();
583 gtk_widget_show( m_wxwindow );
584 gtk_container_add( GTK_CONTAINER(m_mainWidget), m_wxwindow );
585
586 // we donm't allow the frame to get the focus as otherwise
587 // the frame will grab it at arbitrary focus changes
588 gtk_widget_set_can_focus(m_wxwindow, false);
589
590 if (m_parent) m_parent->AddChild( this );
591
592 g_signal_connect(m_wxwindow, "size_allocate",
593 G_CALLBACK(size_allocate), this);
594
595 g_signal_connect (m_widget, "size_request",
596 G_CALLBACK (wxgtk_tlw_size_request_callback), this);
597 PostCreation();
598
599 #if !GTK_CHECK_VERSION(3,0,0) && !defined(GTK_DISABLE_DEPRECATED)
600 if ((m_x != -1) || (m_y != -1))
601 gtk_widget_set_uposition( m_widget, m_x, m_y );
602 #endif
603
604 // we cannot set MWM hints and icons before the widget has
605 // been realized, so we do this directly after realization
606 g_signal_connect (m_widget, "realize",
607 G_CALLBACK (gtk_frame_realized_callback), this);
608
609 // for some reported size corrections
610 g_signal_connect (m_widget, "map_event",
611 G_CALLBACK (gtk_frame_map_callback), this);
612
613 // for iconized state
614 g_signal_connect (m_widget, "window_state_event",
615 G_CALLBACK (gtk_frame_window_state_callback), this);
616
617
618 // for wxMoveEvent
619 g_signal_connect (m_widget, "configure_event",
620 G_CALLBACK (gtk_frame_configure_callback), this);
621
622 // activation
623 g_signal_connect_after (m_widget, "focus_in_event",
624 G_CALLBACK (gtk_frame_focus_in_callback), this);
625 g_signal_connect_after (m_widget, "focus_out_event",
626 G_CALLBACK (gtk_frame_focus_out_callback), this);
627
628 gtk_widget_add_events(m_widget, GDK_PROPERTY_CHANGE_MASK);
629 g_signal_connect(m_widget, "property_notify_event",
630 G_CALLBACK(property_notify_event), this);
631
632 // translate wx decorations styles into Motif WM hints (they are recognized
633 // by other WMs as well)
634
635 // always enable moving the window as we have no separate flag for enabling
636 // it
637 m_gdkFunc = GDK_FUNC_MOVE;
638
639 if ( style & wxCLOSE_BOX )
640 m_gdkFunc |= GDK_FUNC_CLOSE;
641
642 if ( style & wxMINIMIZE_BOX )
643 m_gdkFunc |= GDK_FUNC_MINIMIZE;
644
645 if ( style & wxMAXIMIZE_BOX )
646 m_gdkFunc |= GDK_FUNC_MAXIMIZE;
647
648 if ( (style & wxSIMPLE_BORDER) || (style & wxNO_BORDER) )
649 {
650 m_gdkDecor = 0;
651 }
652 else // have border
653 {
654 m_gdkDecor = GDK_DECOR_BORDER;
655
656 if ( style & wxCAPTION )
657 m_gdkDecor |= GDK_DECOR_TITLE;
658
659 if ( style & wxSYSTEM_MENU )
660 m_gdkDecor |= GDK_DECOR_MENU;
661
662 if ( style & wxMINIMIZE_BOX )
663 m_gdkDecor |= GDK_DECOR_MINIMIZE;
664
665 if ( style & wxMAXIMIZE_BOX )
666 m_gdkDecor |= GDK_DECOR_MAXIMIZE;
667
668 if ( style & wxRESIZE_BORDER )
669 {
670 m_gdkFunc |= GDK_FUNC_RESIZE;
671 m_gdkDecor |= GDK_DECOR_RESIZEH;
672 }
673 }
674
675 m_decorSize = GetCachedDecorSize();
676 int w, h;
677 GTKDoGetSize(&w, &h);
678 gtk_window_set_default_size(GTK_WINDOW(m_widget), w, h);
679
680 return true;
681 }
682
683 wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
684 {
685 #if wxUSE_LIBHILDON || wxUSE_LIBHILDON2
686 // it can also be a (standard) dialog
687 if ( HILDON_IS_WINDOW(m_widget) )
688 {
689 hildon_program_remove_window(wxTheApp->GetHildonProgram(),
690 HILDON_WINDOW(m_widget));
691 }
692 #endif // wxUSE_LIBHILDON || wxUSE_LIBHILDON2
693
694 if (m_grabbed)
695 {
696 wxFAIL_MSG(wxT("Window still grabbed"));
697 RemoveGrab();
698 }
699
700 SendDestroyEvent();
701
702 // it may also be GtkScrolledWindow in the case of an MDI child
703 if (GTK_IS_WINDOW(m_widget))
704 {
705 gtk_window_set_focus( GTK_WINDOW(m_widget), NULL );
706 }
707
708 if (g_activeFrame == this)
709 g_activeFrame = NULL;
710 if (g_lastActiveFrame == this)
711 g_lastActiveFrame = NULL;
712 }
713
714 bool wxTopLevelWindowGTK::EnableCloseButton( bool enable )
715 {
716 if (enable)
717 m_gdkFunc |= GDK_FUNC_CLOSE;
718 else
719 m_gdkFunc &= ~GDK_FUNC_CLOSE;
720
721 GdkWindow* window = gtk_widget_get_window(m_widget);
722 if (window)
723 gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
724
725 return true;
726 }
727
728 bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long)
729 {
730 if (show == m_fsIsShowing)
731 return false; // return what?
732
733 m_fsIsShowing = show;
734
735 wxX11FullScreenMethod method =
736 wxGetFullScreenMethodX11((WXDisplay*)GDK_DISPLAY(),
737 (WXWindow)GDK_ROOT_WINDOW());
738
739 // NB: gtk_window_fullscreen() uses freedesktop.org's WMspec extensions
740 // to switch to fullscreen, which is not always available. We must
741 // check if WM supports the spec and use legacy methods if it
742 // doesn't.
743 if ( method == wxX11_FS_WMSPEC )
744 {
745 if (show)
746 gtk_window_fullscreen( GTK_WINDOW( m_widget ) );
747 else
748 gtk_window_unfullscreen( GTK_WINDOW( m_widget ) );
749 }
750 else
751 {
752 GdkWindow* window = gtk_widget_get_window(m_widget);
753
754 if (show)
755 {
756 GetPosition( &m_fsSaveFrame.x, &m_fsSaveFrame.y );
757 GetSize( &m_fsSaveFrame.width, &m_fsSaveFrame.height );
758
759 int screen_width,screen_height;
760 wxDisplaySize( &screen_width, &screen_height );
761
762 gint client_x, client_y, root_x, root_y;
763 gint width, height;
764
765 m_fsSaveGdkFunc = m_gdkFunc;
766 m_fsSaveGdkDecor = m_gdkDecor;
767 m_gdkFunc = m_gdkDecor = 0;
768 gdk_window_set_decorations(window, (GdkWMDecoration)0);
769 gdk_window_set_functions(window, (GdkWMFunction)0);
770
771 gdk_window_get_origin(window, &root_x, &root_y);
772 gdk_window_get_geometry(window, &client_x, &client_y, &width, &height, NULL);
773
774 gdk_window_move_resize(
775 window, -client_x, -client_y, screen_width + 1, screen_height + 1);
776
777 wxSetFullScreenStateX11((WXDisplay*)GDK_DISPLAY(),
778 (WXWindow)GDK_ROOT_WINDOW(),
779 (WXWindow)GDK_WINDOW_XWINDOW(window),
780 show, &m_fsSaveFrame, method);
781 }
782 else // hide
783 {
784 m_gdkFunc = m_fsSaveGdkFunc;
785 m_gdkDecor = m_fsSaveGdkDecor;
786 gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
787 gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
788
789 wxSetFullScreenStateX11((WXDisplay*)GDK_DISPLAY(),
790 (WXWindow)GDK_ROOT_WINDOW(),
791 (WXWindow)GDK_WINDOW_XWINDOW(window),
792 show, &m_fsSaveFrame, method);
793
794 SetSize(m_fsSaveFrame.x, m_fsSaveFrame.y,
795 m_fsSaveFrame.width, m_fsSaveFrame.height);
796 }
797 }
798
799 // documented behaviour is to show the window if it's still hidden when
800 // showing it full screen
801 if (show)
802 Show();
803
804 return true;
805 }
806
807 // ----------------------------------------------------------------------------
808 // overridden wxWindow methods
809 // ----------------------------------------------------------------------------
810
811 void wxTopLevelWindowGTK::Refresh( bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect) )
812 {
813 wxCHECK_RET( m_widget, wxT("invalid frame") );
814
815 gtk_widget_queue_draw( m_widget );
816
817 GdkWindow* window = NULL;
818 if (m_wxwindow)
819 window = gtk_widget_get_window(m_wxwindow);
820 if (window)
821 gdk_window_invalidate_rect(window, NULL, true);
822 }
823
824 bool wxTopLevelWindowGTK::Show( bool show )
825 {
826 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
827
828 bool deferShow = show && !m_isShown && m_deferShow;
829 if (deferShow)
830 {
831 deferShow = m_deferShowAllowed && !gtk_widget_get_realized(m_widget);
832 if (deferShow)
833 {
834 deferShow = g_signal_handler_find(m_widget,
835 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
836 g_signal_lookup("property_notify_event", GTK_TYPE_WIDGET),
837 0, NULL, NULL, this) != 0;
838 }
839 GdkScreen* screen = NULL;
840 if (deferShow)
841 {
842 screen = gtk_widget_get_screen(m_widget);
843 GdkAtom atom = gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false);
844 deferShow = gdk_x11_screen_supports_net_wm_hint(screen, atom) != 0;
845 // If _NET_REQUEST_FRAME_EXTENTS not supported, don't allow changes
846 // to m_decorSize, it breaks saving/restoring window size with
847 // GetSize()/SetSize() because it makes window bigger between each
848 // restore and save.
849 m_updateDecorSize = deferShow;
850 }
851 if (deferShow)
852 {
853 // Fluxbox support for _NET_REQUEST_FRAME_EXTENTS is broken
854 const char* name = gdk_x11_screen_get_window_manager_name(screen);
855 deferShow = strcmp(name, "Fluxbox") != 0;
856 m_updateDecorSize = deferShow;
857 }
858
859 m_deferShow = deferShow;
860 }
861 if (deferShow)
862 {
863 // Initial show. If WM supports _NET_REQUEST_FRAME_EXTENTS, defer
864 // calling gtk_widget_show() until _NET_FRAME_EXTENTS property
865 // notification is received, so correct frame extents are known.
866 // This allows resizing m_widget to keep the overall size in sync with
867 // what wxWidgets expects it to be without an obvious change in the
868 // window size immediately after it becomes visible.
869
870 // Realize m_widget, so m_widget->window can be used. Realizing normally
871 // causes the widget tree to be size_allocated, which generates size
872 // events in the wrong order. However, the size_allocates will not be
873 // done if the allocation is not the default (1,1).
874 GtkAllocation alloc;
875 gtk_widget_get_allocation(m_widget, &alloc);
876 const int alloc_width = alloc.width;
877 if (alloc_width == 1)
878 {
879 alloc.width = 2;
880 gtk_widget_set_allocation(m_widget, &alloc);
881 }
882 gtk_widget_realize(m_widget);
883 if (alloc_width == 1)
884 {
885 alloc.width = 1;
886 gtk_widget_set_allocation(m_widget, &alloc);
887 }
888
889 // send _NET_REQUEST_FRAME_EXTENTS
890 XClientMessageEvent xevent;
891 memset(&xevent, 0, sizeof(xevent));
892 xevent.type = ClientMessage;
893 GdkWindow* window = gtk_widget_get_window(m_widget);
894 xevent.window = gdk_x11_drawable_get_xid(window);
895 xevent.message_type = gdk_x11_atom_to_xatom_for_display(
896 gdk_drawable_get_display(window),
897 gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false));
898 xevent.format = 32;
899 Display* display = gdk_x11_drawable_get_xdisplay(window);
900 XSendEvent(display, DefaultRootWindow(display), false,
901 SubstructureNotifyMask | SubstructureRedirectMask,
902 (XEvent*)&xevent);
903
904 // defer calling gtk_widget_show()
905 m_isShown = true;
906 return true;
907 }
908
909 if (show && !gtk_widget_get_realized(m_widget))
910 {
911 // size_allocate signals occur in reverse order (bottom to top).
912 // Things work better if the initial wxSizeEvents are sent (from the
913 // top down), before the initial size_allocate signals occur.
914 wxSizeEvent event(GetSize(), GetId());
915 event.SetEventObject(this);
916 HandleWindowEvent(event);
917 }
918
919 bool change = base_type::Show(show);
920
921 if (change && !show)
922 {
923 // make sure window has a non-default position, so when it is shown
924 // again, it won't be repositioned by WM as if it were a new window
925 // Note that this must be done _after_ the window is hidden.
926 gtk_window_move((GtkWindow*)m_widget, m_x, m_y);
927 }
928
929 return change;
930 }
931
932 void wxTopLevelWindowGTK::ShowWithoutActivating()
933 {
934 if (!m_isShown)
935 {
936 #if GTK_CHECK_VERSION(2,6,0)
937 if (!gtk_check_version(2,6,0))
938 gtk_window_set_focus_on_map(GTK_WINDOW(m_widget), false);
939 #endif // GTK+ 2.6+
940
941 Show(true);
942 }
943 }
944
945 void wxTopLevelWindowGTK::Raise()
946 {
947 gtk_window_present( GTK_WINDOW( m_widget ) );
948 }
949
950 void wxTopLevelWindowGTK::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) )
951 {
952 wxFAIL_MSG( wxT("DoMoveWindow called for wxTopLevelWindowGTK") );
953 }
954
955 // ----------------------------------------------------------------------------
956 // window geometry
957 // ----------------------------------------------------------------------------
958
959 void wxTopLevelWindowGTK::GTKDoGetSize(int *width, int *height) const
960 {
961 wxSize size(m_width, m_height);
962 size -= m_decorSize;
963 if (size.x < 0) size.x = 0;
964 if (size.y < 0) size.y = 0;
965 #if wxUSE_LIBHILDON2
966 if (width) {
967 if (size.x == 720)
968 *width = 696;
969 else
970 *width = size.x;
971 }
972 if (height) {
973 if (size.y == 420)
974 *height = 396;
975 else if (size.y == 270)
976 *height = 246;
977 else
978 *height = size.y;
979 }
980 #else // wxUSE_LIBHILDON2
981 if (width) *width = size.x;
982 if (height) *height = size.y;
983 #endif // wxUSE_LIBHILDON2 /!wxUSE_LIBHILDON2
984 }
985
986 void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
987 {
988 wxCHECK_RET( m_widget, wxT("invalid frame") );
989
990 m_deferShowAllowed = true;
991
992 // deal with the position first
993 int old_x = m_x;
994 int old_y = m_y;
995
996 if ( !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) )
997 {
998 // -1 means "use existing" unless the flag above is specified
999 if ( x != -1 )
1000 m_x = x;
1001 if ( y != -1 )
1002 m_y = y;
1003 }
1004 else // wxSIZE_ALLOW_MINUS_ONE
1005 {
1006 m_x = x;
1007 m_y = y;
1008 }
1009
1010 if ( m_x != old_x || m_y != old_y )
1011 {
1012 gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y );
1013 }
1014
1015 const wxSize oldSize(m_width, m_height);
1016 if (width >= 0)
1017 m_width = width;
1018 if (height >= 0)
1019 m_height = height;
1020 ConstrainSize();
1021 if (m_width != oldSize.x || m_height != oldSize.y)
1022 {
1023 int w, h;
1024 GTKDoGetSize(&w, &h);
1025 gtk_window_resize(GTK_WINDOW(m_widget), w, h);
1026
1027 GetClientSize(&m_oldClientWidth, &m_oldClientHeight);
1028 wxSizeEvent event(GetSize(), GetId());
1029 event.SetEventObject(this);
1030 HandleWindowEvent(event);
1031 }
1032 }
1033
1034 void wxTopLevelWindowGTK::DoSetClientSize(int width, int height)
1035 {
1036 base_type::DoSetClientSize(width, height);
1037
1038 // Since client size is being explicitly set, don't change it later
1039 // Has to be done after calling base because it calls SetSize,
1040 // which sets this true
1041 m_deferShowAllowed = false;
1042 }
1043
1044 void wxTopLevelWindowGTK::DoGetClientSize( int *width, int *height ) const
1045 {
1046 wxASSERT_MSG(m_widget, wxT("invalid frame"));
1047
1048 if ( IsIconized() )
1049 {
1050 // for consistency with wxMSW, client area is supposed to be empty for
1051 // the iconized windows
1052 if ( width )
1053 *width = 0;
1054 if ( height )
1055 *height = 0;
1056 }
1057 else
1058 {
1059 GTKDoGetSize(width, height);
1060 }
1061 }
1062
1063 void wxTopLevelWindowGTK::DoSetSizeHints( int minW, int minH,
1064 int maxW, int maxH,
1065 int incW, int incH )
1066 {
1067 base_type::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
1068
1069 const wxSize minSize = GetMinSize();
1070 const wxSize maxSize = GetMaxSize();
1071 GdkGeometry hints;
1072 int hints_mask = 0;
1073 if (minSize.x > 0 || minSize.y > 0)
1074 {
1075 hints_mask |= GDK_HINT_MIN_SIZE;
1076 hints.min_width = minSize.x - m_decorSize.x;
1077 if (hints.min_width < 0)
1078 hints.min_width = 0;
1079 hints.min_height = minSize.y - m_decorSize.y;
1080 if (hints.min_height < 0)
1081 hints.min_height = 0;
1082 }
1083 if (maxSize.x > 0 || maxSize.y > 0)
1084 {
1085 hints_mask |= GDK_HINT_MAX_SIZE;
1086 hints.max_width = maxSize.x - m_decorSize.x;
1087 if (hints.max_width < 0)
1088 hints.max_width = INT_MAX;
1089 hints.max_height = maxSize.y - m_decorSize.y;
1090 if (hints.max_height < 0)
1091 hints.max_height = INT_MAX;
1092 }
1093 if (incW > 0 || incH > 0)
1094 {
1095 hints_mask |= GDK_HINT_RESIZE_INC;
1096 hints.width_inc = incW > 0 ? incW : 1;
1097 hints.height_inc = incH > 0 ? incH : 1;
1098 }
1099 gtk_window_set_geometry_hints(
1100 (GtkWindow*)m_widget, NULL, &hints, (GdkWindowHints)hints_mask);
1101 }
1102
1103 void wxTopLevelWindowGTK::GTKUpdateDecorSize(const wxSize& decorSize)
1104 {
1105 if (!IsMaximized() && !IsFullScreen())
1106 GetCachedDecorSize() = decorSize;
1107 if (m_updateDecorSize && m_decorSize != decorSize)
1108 {
1109 const wxSize diff = decorSize - m_decorSize;
1110 m_decorSize = decorSize;
1111 bool resized = false;
1112 if (m_deferShow)
1113 {
1114 // keep overall size unchanged by shrinking m_widget
1115 int w, h;
1116 GTKDoGetSize(&w, &h);
1117 // but not if size would be less than minimum, it won't take effect
1118 const wxSize minSize = GetMinSize();
1119 if (w >= minSize.x && h >= minSize.y)
1120 {
1121 gtk_window_resize(GTK_WINDOW(m_widget), w, h);
1122 resized = true;
1123 }
1124 }
1125 if (!resized)
1126 {
1127 // adjust overall size to match change in frame extents
1128 m_width += diff.x;
1129 m_height += diff.y;
1130 if (m_width < 0) m_width = 0;
1131 if (m_height < 0) m_height = 0;
1132 m_oldClientWidth = 0;
1133 gtk_widget_queue_resize(m_wxwindow);
1134 }
1135 }
1136 if (m_deferShow)
1137 {
1138 // gtk_widget_show() was deferred, do it now
1139 m_deferShow = false;
1140 GetClientSize(&m_oldClientWidth, &m_oldClientHeight);
1141 wxSizeEvent sizeEvent(GetSize(), GetId());
1142 sizeEvent.SetEventObject(this);
1143 HandleWindowEvent(sizeEvent);
1144
1145 gtk_widget_show(m_widget);
1146
1147 wxShowEvent showEvent(GetId(), true);
1148 showEvent.SetEventObject(this);
1149 HandleWindowEvent(showEvent);
1150 }
1151 }
1152
1153 wxSize& wxTopLevelWindowGTK::GetCachedDecorSize()
1154 {
1155 static wxSize size[8];
1156
1157 int index = 0;
1158 // title bar
1159 if (m_gdkDecor & (GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE | GDK_DECOR_TITLE))
1160 index = 1;
1161 // border
1162 if (m_gdkDecor & GDK_DECOR_BORDER)
1163 index |= 2;
1164 // utility window decor can be different
1165 if (m_windowStyle & wxFRAME_TOOL_WINDOW)
1166 index |= 4;
1167 return size[index];
1168 }
1169
1170 void wxTopLevelWindowGTK::OnInternalIdle()
1171 {
1172 wxTopLevelWindowBase::OnInternalIdle();
1173
1174 // Synthetize activate events.
1175 if ( g_sendActivateEvent != -1 )
1176 {
1177 bool activate = g_sendActivateEvent != 0;
1178
1179 // if (!activate) wxPrintf( wxT("de") );
1180 // wxPrintf( wxT("activate\n") );
1181
1182 // do it only once
1183 g_sendActivateEvent = -1;
1184
1185 wxTheApp->SetActive(activate, (wxWindow *)g_lastActiveFrame);
1186 }
1187 }
1188
1189 // ----------------------------------------------------------------------------
1190 // frame title/icon
1191 // ----------------------------------------------------------------------------
1192
1193 void wxTopLevelWindowGTK::SetTitle( const wxString &title )
1194 {
1195 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
1196
1197 if ( title == m_title )
1198 return;
1199
1200 m_title = title;
1201
1202 gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
1203 }
1204
1205 void wxTopLevelWindowGTK::SetIcons( const wxIconBundle &icons )
1206 {
1207 wxASSERT_MSG( (m_widget != NULL), wxT("invalid frame") );
1208
1209 base_type::SetIcons(icons);
1210
1211 // Setting icons before window is realized can cause a GTK assertion if
1212 // another TLW is realized before this one, and it has this one as it's
1213 // transient parent. The life demo exibits this problem.
1214 if (gtk_widget_get_realized(m_widget))
1215 {
1216 GList* list = NULL;
1217 for (size_t i = icons.GetIconCount(); i--;)
1218 list = g_list_prepend(list, icons.GetIconByIndex(i).GetPixbuf());
1219 gtk_window_set_icon_list(GTK_WINDOW(m_widget), list);
1220 g_list_free(list);
1221 }
1222 }
1223
1224 // ----------------------------------------------------------------------------
1225 // frame state: maximized/iconized/normal
1226 // ----------------------------------------------------------------------------
1227
1228 void wxTopLevelWindowGTK::Maximize(bool maximize)
1229 {
1230 if (maximize)
1231 gtk_window_maximize( GTK_WINDOW( m_widget ) );
1232 else
1233 gtk_window_unmaximize( GTK_WINDOW( m_widget ) );
1234 }
1235
1236 bool wxTopLevelWindowGTK::IsMaximized() const
1237 {
1238 GdkWindow* window = gtk_widget_get_window(m_widget);
1239 return window && (gdk_window_get_state(window) & GDK_WINDOW_STATE_MAXIMIZED);
1240 }
1241
1242 void wxTopLevelWindowGTK::Restore()
1243 {
1244 // "Present" seems similar enough to "restore"
1245 gtk_window_present( GTK_WINDOW( m_widget ) );
1246 }
1247
1248 void wxTopLevelWindowGTK::Iconize( bool iconize )
1249 {
1250 if (iconize)
1251 gtk_window_iconify( GTK_WINDOW( m_widget ) );
1252 else
1253 gtk_window_deiconify( GTK_WINDOW( m_widget ) );
1254 }
1255
1256 bool wxTopLevelWindowGTK::IsIconized() const
1257 {
1258 return m_isIconized;
1259 }
1260
1261 void wxTopLevelWindowGTK::SetIconizeState(bool iconize)
1262 {
1263 if ( iconize != m_isIconized )
1264 {
1265 m_isIconized = iconize;
1266 (void)SendIconizeEvent(iconize);
1267 }
1268 }
1269
1270 void wxTopLevelWindowGTK::AddGrab()
1271 {
1272 if (!m_grabbed)
1273 {
1274 m_grabbed = true;
1275 gtk_grab_add( m_widget );
1276 wxGUIEventLoop().Run();
1277 gtk_grab_remove( m_widget );
1278 }
1279 }
1280
1281 void wxTopLevelWindowGTK::RemoveGrab()
1282 {
1283 if (m_grabbed)
1284 {
1285 gtk_main_quit();
1286 m_grabbed = false;
1287 }
1288 }
1289
1290
1291 // helper
1292 static bool do_shape_combine_region(GdkWindow* window, const wxRegion& region)
1293 {
1294 if (window)
1295 {
1296 if (region.IsEmpty())
1297 {
1298 gdk_window_shape_combine_mask(window, NULL, 0, 0);
1299 }
1300 else
1301 {
1302 gdk_window_shape_combine_region(window, region.GetRegion(), 0, 0);
1303 return true;
1304 }
1305 }
1306 return false;
1307 }
1308
1309
1310 bool wxTopLevelWindowGTK::SetShape(const wxRegion& region)
1311 {
1312 wxCHECK_MSG( HasFlag(wxFRAME_SHAPED), false,
1313 wxT("Shaped windows must be created with the wxFRAME_SHAPED style."));
1314
1315 if ( gtk_widget_get_realized(m_widget) )
1316 {
1317 if ( m_wxwindow )
1318 do_shape_combine_region(gtk_widget_get_window(m_wxwindow), region);
1319
1320 return do_shape_combine_region(gtk_widget_get_window(m_widget), region);
1321 }
1322 else // not realized yet
1323 {
1324 // store the shape to set, it will be really set once we're realized
1325 m_shape = region;
1326
1327 // we don't know if we're going to succeed or fail, be optimistic by
1328 // default
1329 return true;
1330 }
1331 }
1332
1333 bool wxTopLevelWindowGTK::IsActive()
1334 {
1335 return (this == (wxTopLevelWindowGTK*)g_activeFrame);
1336 }
1337
1338 void wxTopLevelWindowGTK::RequestUserAttention(int flags)
1339 {
1340 bool new_hint_value = false;
1341
1342 // FIXME: This is a workaround to focus handling problem
1343 // If RequestUserAttention is called for example right after a wxSleep, OnInternalIdle
1344 // hasn't yet been processed, and the internal focus system is not up to date yet.
1345 // YieldFor(wxEVT_CATEGORY_UI) ensures the processing of it (hopefully it
1346 // won't have side effects) - MR
1347 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
1348
1349 if(m_urgency_hint >= 0)
1350 g_source_remove(m_urgency_hint);
1351
1352 m_urgency_hint = -2;
1353
1354 if( gtk_widget_get_realized(m_widget) && !IsActive() )
1355 {
1356 new_hint_value = true;
1357
1358 if (flags & wxUSER_ATTENTION_INFO)
1359 {
1360 m_urgency_hint = g_timeout_add(5000, (GSourceFunc)gtk_frame_urgency_timer_callback, this);
1361 } else {
1362 m_urgency_hint = -1;
1363 }
1364 }
1365
1366 #if GTK_CHECK_VERSION(2,7,0)
1367 if(!gtk_check_version(2,7,0))
1368 gtk_window_set_urgency_hint(GTK_WINDOW( m_widget ), new_hint_value);
1369 else
1370 #endif
1371 wxgtk_window_set_urgency_hint(GTK_WINDOW( m_widget ), new_hint_value);
1372 }
1373
1374 void wxTopLevelWindowGTK::SetWindowStyleFlag( long style )
1375 {
1376 // Store which styles were changed
1377 long styleChanges = style ^ m_windowStyle;
1378
1379 // Process wxWindow styles. This also updates the internal variable
1380 // Therefore m_windowStyle bits carry now the _new_ style values
1381 wxWindow::SetWindowStyleFlag(style);
1382
1383 // just return for now if widget does not exist yet
1384 if (!m_widget)
1385 return;
1386
1387 if ( styleChanges & wxSTAY_ON_TOP )
1388 {
1389 gtk_window_set_keep_above(GTK_WINDOW(m_widget),
1390 m_windowStyle & wxSTAY_ON_TOP);
1391 }
1392
1393 if ( styleChanges & wxFRAME_NO_TASKBAR )
1394 {
1395 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget),
1396 m_windowStyle & wxFRAME_NO_TASKBAR);
1397 }
1398 }
1399
1400 /* Get the X Window between child and the root window.
1401 This should usually be the WM managed XID */
1402 static Window wxGetTopmostWindowX11(Display *dpy, Window child)
1403 {
1404 Window root, parent;
1405 Window* children;
1406 unsigned int nchildren;
1407
1408 XQueryTree(dpy, child, &root, &parent, &children, &nchildren);
1409 XFree(children);
1410
1411 while (parent != root) {
1412 child = parent;
1413 XQueryTree(dpy, child, &root, &parent, &children, &nchildren);
1414 XFree(children);
1415 }
1416
1417 return child;
1418 }
1419
1420 bool wxTopLevelWindowGTK::SetTransparent(wxByte alpha)
1421 {
1422 GdkWindow* window = NULL;
1423 if (m_widget)
1424 window = gtk_widget_get_window(m_widget);
1425 if (window == NULL)
1426 return false;
1427
1428 Display* dpy = GDK_WINDOW_XDISPLAY(window);
1429 // We need to get the X Window that has the root window as the immediate parent
1430 // and m_widget->window as a child. This should be the X Window that the WM manages and
1431 // from which the opacity property is checked from.
1432 Window win = wxGetTopmostWindowX11(dpy, GDK_WINDOW_XID(window));
1433
1434
1435 // Using pure Xlib to not have a GTK version check mess due to gtk2.0 not having GdkDisplay
1436 if (alpha == 0xff)
1437 XDeleteProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False));
1438 else
1439 {
1440 long opacity = alpha * 0x1010101L;
1441 XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False),
1442 XA_CARDINAL, 32, PropModeReplace,
1443 (unsigned char *) &opacity, 1L);
1444 }
1445 XSync(dpy, False);
1446 return true;
1447 }
1448
1449 bool wxTopLevelWindowGTK::CanSetTransparent()
1450 {
1451 // allow to override automatic detection as it's far from perfect
1452 const wxString SYSOPT_TRANSPARENT = "gtk.tlw.can-set-transparent";
1453 if ( wxSystemOptions::HasOption(SYSOPT_TRANSPARENT) )
1454 {
1455 return wxSystemOptions::GetOptionInt(SYSOPT_TRANSPARENT) != 0;
1456 }
1457
1458 #if GTK_CHECK_VERSION(2,10,0)
1459 if (!gtk_check_version(2,10,0))
1460 {
1461 return (gtk_widget_is_composited (m_widget));
1462 }
1463 else
1464 #endif // In case of lower versions than gtk+-2.10.0 we could look for _NET_WM_CM_Sn ourselves
1465 {
1466 return false;
1467 }
1468
1469 #if 0 // Don't be optimistic here for the sake of wxAUI
1470 int opcode, event, error;
1471 // Check for the existence of a RGBA visual instead?
1472 return XQueryExtension(gdk_x11_get_default_xdisplay (),
1473 "Composite", &opcode, &event, &error);
1474 #endif
1475 }