1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/toplevel.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __VMS
21 #define XIconifyWindow XICONIFYWINDOW
22 #endif
23
24 #include "wx/toplevel.h"
25
26 #ifndef WX_PRECOMP
27 #include "wx/frame.h"
28 #include "wx/icon.h"
29 #include "wx/log.h"
30 #endif
31
32 #include "wx/evtloop.h"
33 #include "wx/sysopt.h"
34
35 #include <gtk/gtk.h>
36 #ifdef GDK_WINDOWING_X11
37 #include <gdk/gdkx.h>
38 #include <X11/Xatom.h> // XA_CARDINAL
39 #include "wx/unix/utilsx11.h"
40 #endif
41
42 #include "wx/gtk/private.h"
43 #include "wx/gtk/private/gtk2-compat.h"
44 #include "wx/gtk/private/win_gtk.h"
45
46 #if wxUSE_LIBHILDON
47 #include <hildon-widgets/hildon-program.h>
48 #include <hildon-widgets/hildon-window.h>
49 #endif // wxUSE_LIBHILDON
50
51 #if wxUSE_LIBHILDON2
52 #include <hildon/hildon.h>
53 #endif // wxUSE_LIBHILDON2
54
55 #ifdef __WXGTK3__
56 extern GList* wx_sizeEventList;
57 void wxGTKSizeRevalidate(wxWindow*);
58 #endif
59
60 // ----------------------------------------------------------------------------
61 // data
62 // ----------------------------------------------------------------------------
63
64 // this is incremented while a modal dialog is shown
65 int wxOpenModalDialogsCount = 0;
66
67 // the frame that is currently active (i.e. its child has focus). It is
68 // used to generate wxActivateEvents
69 static wxTopLevelWindowGTK *g_activeFrame = NULL;
70
71 extern wxCursor g_globalCursor;
72 extern wxCursor g_busyCursor;
73
74 #ifdef GDK_WINDOWING_X11
75 // Whether _NET_REQUEST_FRAME_EXTENTS support is working
76 static enum {
77 RFE_STATUS_UNKNOWN, RFE_STATUS_WORKING, RFE_STATUS_BROKEN
78 } gs_requestFrameExtentsStatus;
79
80 static bool gs_decorCacheValid;
81 #endif
82
83 #ifdef __WXGTK3__
HasClientDecor(GtkWidget * widget)84 static bool HasClientDecor(GtkWidget* widget)
85 {
86 static bool has;
87 static bool once;
88 if (!once)
89 {
90 once = true;
91 GdkDisplay* display = gtk_widget_get_display(widget);
92 const char* name = g_type_name(G_TYPE_FROM_INSTANCE(display));
93 has =
94 strcmp(name, "GdkWaylandDisplay") == 0 ||
95 strcmp(name, "GdkMirDisplay") == 0 ||
96 strcmp(name, "GdkBroadwayDisplay") == 0;
97 }
98 return has;
99 }
100 #else
HasClientDecor(GtkWidget *)101 static inline bool HasClientDecor(GtkWidget*)
102 {
103 return false;
104 }
105 #endif
106
107 //-----------------------------------------------------------------------------
108 // RequestUserAttention related functions
109 //-----------------------------------------------------------------------------
110
111 #ifndef __WXGTK3__
wxgtk_window_set_urgency_hint(GtkWindow * win,gboolean setting)112 static void wxgtk_window_set_urgency_hint (GtkWindow *win,
113 gboolean setting)
114 {
115 #if GTK_CHECK_VERSION(2,7,0)
116 if (gtk_check_version(2,7,0) == NULL)
117 gtk_window_set_urgency_hint(win, setting);
118 else
119 #endif
120 {
121 #ifdef GDK_WINDOWING_X11
122 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(win));
123 wxCHECK_RET(window, "wxgtk_window_set_urgency_hint: GdkWindow not realized");
124
125 Display* dpy = GDK_WINDOW_XDISPLAY(window);
126 Window xid = GDK_WINDOW_XID(window);
127 XWMHints* wm_hints = XGetWMHints(dpy, xid);
128
129 if (!wm_hints)
130 wm_hints = XAllocWMHints();
131
132 if (setting)
133 wm_hints->flags |= XUrgencyHint;
134 else
135 wm_hints->flags &= ~XUrgencyHint;
136
137 XSetWMHints(dpy, xid, wm_hints);
138 XFree(wm_hints);
139 #endif // GDK_WINDOWING_X11
140 }
141 }
142 #define gtk_window_set_urgency_hint wxgtk_window_set_urgency_hint
143 #endif
144
145 extern "C" {
gtk_frame_urgency_timer_callback(wxTopLevelWindowGTK * win)146 static gboolean gtk_frame_urgency_timer_callback( wxTopLevelWindowGTK *win )
147 {
148 gtk_window_set_urgency_hint(GTK_WINDOW(win->m_widget), false);
149
150 win->m_urgency_hint = -2;
151 return FALSE;
152 }
153 }
154
155 //-----------------------------------------------------------------------------
156 // "focus_in_event"
157 //-----------------------------------------------------------------------------
158
159 extern "C" {
gtk_frame_focus_in_callback(GtkWidget * widget,GdkEvent * WXUNUSED (event),wxTopLevelWindowGTK * win)160 static gboolean gtk_frame_focus_in_callback( GtkWidget *widget,
161 GdkEvent *WXUNUSED(event),
162 wxTopLevelWindowGTK *win )
163 {
164 g_activeFrame = win;
165
166 // MR: wxRequestUserAttention related block
167 switch( win->m_urgency_hint )
168 {
169 default:
170 g_source_remove( win->m_urgency_hint );
171 // no break, fallthrough to remove hint too
172 case -1:
173 gtk_window_set_urgency_hint(GTK_WINDOW(widget), false);
174 win->m_urgency_hint = -2;
175 break;
176
177 case -2: break;
178 }
179
180 wxActivateEvent event(wxEVT_ACTIVATE, true, g_activeFrame->GetId());
181 event.SetEventObject(g_activeFrame);
182 g_activeFrame->HandleWindowEvent(event);
183
184 return FALSE;
185 }
186 }
187
188 //-----------------------------------------------------------------------------
189 // "focus_out_event"
190 //-----------------------------------------------------------------------------
191
192 extern "C" {
193 static
gtk_frame_focus_out_callback(GtkWidget * WXUNUSED (widget),GdkEventFocus * WXUNUSED (gdk_event),wxTopLevelWindowGTK * WXUNUSED (win))194 gboolean gtk_frame_focus_out_callback(GtkWidget * WXUNUSED(widget),
195 GdkEventFocus *WXUNUSED(gdk_event),
196 wxTopLevelWindowGTK * WXUNUSED(win))
197 {
198 if (g_activeFrame)
199 {
200 wxActivateEvent event(wxEVT_ACTIVATE, false, g_activeFrame->GetId());
201 event.SetEventObject(g_activeFrame);
202 g_activeFrame->HandleWindowEvent(event);
203
204 g_activeFrame = NULL;
205 }
206
207 return FALSE;
208 }
209 }
210
211 // ----------------------------------------------------------------------------
212 // "key_press_event"
213 // ----------------------------------------------------------------------------
214
215 extern "C" {
216 static gboolean
wxgtk_tlw_key_press_event(GtkWidget * widget,GdkEventKey * event)217 wxgtk_tlw_key_press_event(GtkWidget *widget, GdkEventKey *event)
218 {
219 GtkWindow* const window = GTK_WINDOW(widget);
220
221 // By default GTK+ checks for the menu accelerators in this (top level)
222 // window first and then propagates the event to the currently focused
223 // child from where it bubbles up the window parent chain. In wxWidgets,
224 // however, we want the child window to have the event first but still
225 // handle it as an accelerator if it's not processed there, so we need to
226 // customize this by reversing the order of the steps done in the standard
227 // GTK+ gtk_window_key_press_event() handler.
228
229 if ( gtk_window_propagate_key_event(window, event) )
230 return true;
231
232 if ( gtk_window_activate_key(window, event) )
233 return true;
234
235 void* parent_class = g_type_class_peek_parent(G_OBJECT_GET_CLASS(widget));
236 GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
237
238 // Avoid calling the default handler, we have already done everything it does
239 return true;
240 }
241 }
242
243 //-----------------------------------------------------------------------------
244 // "size_allocate" from m_wxwindow
245 //-----------------------------------------------------------------------------
246
247 extern "C" {
248 static void
size_allocate(GtkWidget *,GtkAllocation * alloc,wxTopLevelWindowGTK * win)249 size_allocate(GtkWidget*, GtkAllocation* alloc, wxTopLevelWindowGTK* win)
250 {
251 win->m_useCachedClientSize = true;
252 if (win->m_clientWidth != alloc->width ||
253 win->m_clientHeight != alloc->height)
254 {
255 win->m_clientWidth = alloc->width;
256 win->m_clientHeight = alloc->height;
257
258 GtkAllocation a;
259 gtk_widget_get_allocation(win->m_widget, &a);
260 wxSize size(a.width, a.height);
261 if (HasClientDecor(win->m_widget))
262 {
263 GtkAllocation a2;
264 gtk_widget_get_allocation(win->m_mainWidget, &a2);
265 wxTopLevelWindowGTK::DecorSize decorSize;
266 decorSize.left = a2.x;
267 decorSize.right = a.width - a2.width - a2.x;
268 decorSize.top = a2.y;
269 decorSize.bottom = a.height - a2.height - a2.y;
270 win->GTKUpdateDecorSize(decorSize);
271 }
272 else
273 {
274 size.x += win->m_decorSize.left + win->m_decorSize.right;
275 size.y += win->m_decorSize.top + win->m_decorSize.bottom;
276 }
277 win->m_width = size.x;
278 win->m_height = size.y;
279
280 if (!win->IsIconized())
281 {
282 wxSizeEvent event(size, win->GetId());
283 event.SetEventObject(win);
284 win->HandleWindowEvent(event);
285 }
286 // else the window is currently unmapped, don't generate size events
287 }
288 }
289 }
290
291 //-----------------------------------------------------------------------------
292 // "delete_event"
293 //-----------------------------------------------------------------------------
294
295 extern "C" {
296 static gboolean
gtk_frame_delete_callback(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxTopLevelWindowGTK * win)297 gtk_frame_delete_callback( GtkWidget *WXUNUSED(widget),
298 GdkEvent *WXUNUSED(event),
299 wxTopLevelWindowGTK *win )
300 {
301 if (win->IsEnabled() &&
302 (wxOpenModalDialogsCount == 0 || (win->GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) ||
303 win->IsGrabbed()))
304 win->Close();
305
306 return TRUE;
307 }
308 }
309
310 //-----------------------------------------------------------------------------
311 // "configure_event"
312 //-----------------------------------------------------------------------------
313
314 extern "C" {
315 static gboolean
gtk_frame_configure_callback(GtkWidget *,GdkEventConfigure * gdk_event,wxTopLevelWindowGTK * win)316 gtk_frame_configure_callback( GtkWidget*,
317 GdkEventConfigure* gdk_event,
318 wxTopLevelWindowGTK *win )
319 {
320 win->GTKConfigureEvent(gdk_event->x, gdk_event->y);
321 return false;
322 }
323 }
324
GTKConfigureEvent(int x,int y)325 void wxTopLevelWindowGTK::GTKConfigureEvent(int x, int y)
326 {
327 wxPoint point;
328 #ifdef GDK_WINDOWING_X11
329 if (gs_decorCacheValid)
330 {
331 const DecorSize& decorSize = GetCachedDecorSize();
332 point.x = x - decorSize.left;
333 point.y = y - decorSize.top;
334 }
335 else
336 #endif
337 {
338 gtk_window_get_position(GTK_WINDOW(m_widget), &point.x, &point.y);
339 }
340
341 if (m_x != point.x || m_y != point.y)
342 {
343 m_x = point.x;
344 m_y = point.y;
345 wxMoveEvent event(point, GetId());
346 event.SetEventObject(this);
347 HandleWindowEvent(event);
348 }
349 }
350
351 //-----------------------------------------------------------------------------
352 // "realize" from m_widget
353 //-----------------------------------------------------------------------------
354
355 // we cannot the WM hints and icons before the widget has been realized,
356 // so we do this directly after realization
357
GTKHandleRealized()358 void wxTopLevelWindowGTK::GTKHandleRealized()
359 {
360 wxNonOwnedWindow::GTKHandleRealized();
361
362 GdkWindow* window = gtk_widget_get_window(m_widget);
363
364 #ifdef __WXGTK3__
365 // Don't set WM decorations if GTK is using Client Side Decorations
366 if (gtk_style_context_has_class(gtk_widget_get_style_context(m_widget), "csd"))
367 m_gdkDecor = 0;
368 #endif
369 gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
370 gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
371
372 const wxIconBundle& icons = GetIcons();
373 if (icons.GetIconCount())
374 SetIcons(icons);
375
376 GdkCursor* cursor = g_globalCursor.GetCursor();
377 if (wxIsBusy() && !gtk_window_get_modal(GTK_WINDOW(m_widget)))
378 cursor = g_busyCursor.GetCursor();
379
380 if (cursor)
381 gdk_window_set_cursor(window, cursor);
382
383 #ifdef __WXGTK3__
384 if (gtk_window_get_has_resize_grip(GTK_WINDOW(m_widget)))
385 {
386 // Grip window can end up obscured, probably due to deferred show.
387 // Reset grip to ensure it is visible.
388 gtk_window_set_has_resize_grip(GTK_WINDOW(m_widget), false);
389 gtk_window_set_has_resize_grip(GTK_WINDOW(m_widget), true);
390 }
391 #endif
392 }
393
394 //-----------------------------------------------------------------------------
395 // "map_event" from m_widget
396 //-----------------------------------------------------------------------------
397
398 extern "C" {
399 static gboolean
gtk_frame_map_callback(GtkWidget *,GdkEvent * WXUNUSED (event),wxTopLevelWindow * win)400 gtk_frame_map_callback( GtkWidget*,
401 GdkEvent * WXUNUSED(event),
402 wxTopLevelWindow *win )
403 {
404 const bool wasIconized = win->IsIconized();
405 if (wasIconized)
406 {
407 // Because GetClientSize() returns (0,0) when IsIconized() is true,
408 // a size event must be generated, just in case GetClientSize() was
409 // called while iconized. This specifically happens when restoring a
410 // tlw that was "rolled up" with some WMs.
411 // Queue a resize rather than sending size event directly to allow
412 // children to be made visible first.
413 win->m_useCachedClientSize = false;
414 win->m_clientWidth = 0;
415 gtk_widget_queue_resize(win->m_wxwindow);
416 }
417 // it is possible for m_isShown to be false here, see bug #9909
418 if (win->wxWindowBase::Show(true))
419 {
420 wxShowEvent eventShow(win->GetId(), true);
421 eventShow.SetEventObject(win);
422 win->GetEventHandler()->ProcessEvent(eventShow);
423 }
424
425 // restore focus-on-map setting in case ShowWithoutActivating() was called
426 gtk_window_set_focus_on_map(GTK_WINDOW(win->m_widget), true);
427
428 return false;
429 }
430 }
431
432 //-----------------------------------------------------------------------------
433 // "window-state-event" from m_widget
434 //-----------------------------------------------------------------------------
435
436 extern "C" {
437 static gboolean
gtk_frame_window_state_callback(GtkWidget * WXUNUSED (widget),GdkEventWindowState * event,wxTopLevelWindow * win)438 gtk_frame_window_state_callback( GtkWidget* WXUNUSED(widget),
439 GdkEventWindowState *event,
440 wxTopLevelWindow *win )
441 {
442 if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)
443 win->SetIconizeState((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
444
445 // if maximized bit changed and it is now set
446 if (event->changed_mask & event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
447 {
448 wxMaximizeEvent evt(win->GetId());
449 evt.SetEventObject(win);
450 win->HandleWindowEvent(evt);
451 }
452
453 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
454 win->m_fsIsShowing = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
455
456 return false;
457 }
458 }
459
460 //-----------------------------------------------------------------------------
461 // "notify::gtk-theme-name" from GtkSettings
462 //-----------------------------------------------------------------------------
463
464 extern "C" {
notify_gtk_theme_name(GObject *,GParamSpec *,wxTopLevelWindowGTK * win)465 static void notify_gtk_theme_name(GObject*, GParamSpec*, wxTopLevelWindowGTK* win)
466 {
467 wxSysColourChangedEvent event;
468 event.SetEventObject(win);
469 win->HandleWindowEvent(event);
470 }
471 }
472
473 //-----------------------------------------------------------------------------
474
wxGetFrameExtents(GdkWindow * window,int * left,int * right,int * top,int * bottom)475 bool wxGetFrameExtents(GdkWindow* window, int* left, int* right, int* top, int* bottom)
476 {
477 #ifdef GDK_WINDOWING_X11
478 GdkDisplay* display = gdk_window_get_display(window);
479
480 if (!GDK_IS_X11_DISPLAY(display))
481 return false;
482
483 static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
484 Atom xproperty = gdk_x11_atom_to_xatom_for_display(display, property);
485 Atom type;
486 int format;
487 gulong nitems, bytes_after;
488 guchar* data = NULL;
489 Status status = XGetWindowProperty(
490 GDK_DISPLAY_XDISPLAY(display),
491 GDK_WINDOW_XID(window),
492 xproperty,
493 0, 4, false, XA_CARDINAL,
494 &type, &format, &nitems, &bytes_after, &data);
495 const bool success = status == Success && data && nitems == 4;
496 if (success)
497 {
498 // We need to convert the X11 physical extents to GTK+ "logical" units
499 int scale = 1;
500 #if GTK_CHECK_VERSION(3,10,0)
501 if (gtk_check_version(3,10,0) == NULL)
502 scale = gdk_window_get_scale_factor(window);
503 #endif
504 long* p = (long*)data;
505 if (left) *left = int(p[0]) / scale;
506 if (right) *right = int(p[1]) / scale;
507 if (top) *top = int(p[2]) / scale;
508 if (bottom) *bottom = int(p[3]) / scale;
509 }
510 if (data)
511 XFree(data);
512 return success;
513 #else
514 return false;
515 #endif
516 }
517
518 #ifdef GDK_WINDOWING_X11
519 //-----------------------------------------------------------------------------
520 // "property_notify_event" from m_widget
521 //-----------------------------------------------------------------------------
522
523 extern "C" {
property_notify_event(GtkWidget *,GdkEventProperty * event,wxTopLevelWindowGTK * win)524 static gboolean property_notify_event(
525 GtkWidget*, GdkEventProperty* event, wxTopLevelWindowGTK* win)
526 {
527 // Watch for changes to _NET_FRAME_EXTENTS property
528 static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
529 if (event->state == GDK_PROPERTY_NEW_VALUE && event->atom == property)
530 {
531 if (win->m_netFrameExtentsTimerId)
532 {
533 // WM support for _NET_REQUEST_FRAME_EXTENTS is working
534 gs_requestFrameExtentsStatus = RFE_STATUS_WORKING;
535 g_source_remove(win->m_netFrameExtentsTimerId);
536 win->m_netFrameExtentsTimerId = 0;
537 }
538
539 wxTopLevelWindowGTK::DecorSize decorSize = win->m_decorSize;
540 gs_decorCacheValid = wxGetFrameExtents(event->window,
541 &decorSize.left, &decorSize.right, &decorSize.top, &decorSize.bottom);
542
543 win->GTKUpdateDecorSize(decorSize);
544 }
545 return false;
546 }
547 }
548
549 extern "C" {
request_frame_extents_timeout(void * data)550 static gboolean request_frame_extents_timeout(void* data)
551 {
552 // WM support for _NET_REQUEST_FRAME_EXTENTS is broken
553 gs_requestFrameExtentsStatus = RFE_STATUS_BROKEN;
554 gdk_threads_enter();
555 wxTopLevelWindowGTK* win = static_cast<wxTopLevelWindowGTK*>(data);
556 win->m_netFrameExtentsTimerId = 0;
557 wxTopLevelWindowGTK::DecorSize decorSize = win->m_decorSize;
558 wxGetFrameExtents(gtk_widget_get_window(win->m_widget),
559 &decorSize.left, &decorSize.right, &decorSize.top, &decorSize.bottom);
560 win->GTKUpdateDecorSize(decorSize);
561 gdk_threads_leave();
562 return false;
563 }
564 }
565 #endif // GDK_WINDOWING_X11
566
567 // ----------------------------------------------------------------------------
568 // wxTopLevelWindowGTK creation
569 // ----------------------------------------------------------------------------
570
Init()571 void wxTopLevelWindowGTK::Init()
572 {
573 m_mainWidget = NULL;
574 m_isIconized = false;
575 m_fsIsShowing = false;
576 m_themeEnabled = true;
577 m_gdkDecor =
578 m_gdkFunc = 0;
579 m_grabbed = false;
580 m_deferShow = true;
581 m_deferShowAllowed = true;
582 m_updateDecorSize = true;
583 m_netFrameExtentsTimerId = 0;
584 m_incWidth = m_incHeight = 0;
585 memset(&m_decorSize, 0, sizeof(m_decorSize));
586
587 m_urgency_hint = -2;
588 }
589
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & sizeOrig,long style,const wxString & name)590 bool wxTopLevelWindowGTK::Create( wxWindow *parent,
591 wxWindowID id,
592 const wxString& title,
593 const wxPoint& pos,
594 const wxSize& sizeOrig,
595 long style,
596 const wxString &name )
597 {
598 wxSize size(sizeOrig);
599 if (!size.IsFullySpecified())
600 size.SetDefaults(GetDefaultSize());
601
602 wxTopLevelWindows.Append( this );
603
604 if (!PreCreation( parent, pos, size ) ||
605 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
606 {
607 wxFAIL_MSG( wxT("wxTopLevelWindowGTK creation failed") );
608 return false;
609 }
610
611 m_title = title;
612
613 // NB: m_widget may be !=NULL if it was created by derived class' Create,
614 // e.g. in wxTaskBarIconAreaGTK
615 if (m_widget == NULL)
616 {
617 #if wxUSE_LIBHILDON || wxUSE_LIBHILDON2
618 // we must create HildonWindow and not a normal GtkWindow as the latter
619 // doesn't look correctly in Maemo environment and it must also be
620 // registered with the main program object
621 m_widget = hildon_window_new();
622 hildon_program_add_window(wxTheApp->GetHildonProgram(),
623 HILDON_WINDOW(m_widget));
624 #else // !wxUSE_LIBHILDON || !wxUSE_LIBHILDON2
625 m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
626 if (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)
627 {
628 // Tell WM that this is a dialog window and make it center
629 // on parent by default (this is what GtkDialog ctor does):
630 gtk_window_set_type_hint(GTK_WINDOW(m_widget),
631 GDK_WINDOW_TYPE_HINT_DIALOG);
632 gtk_window_set_position(GTK_WINDOW(m_widget),
633 GTK_WIN_POS_CENTER_ON_PARENT);
634 }
635 else
636 {
637 if (style & wxFRAME_TOOL_WINDOW)
638 {
639 gtk_window_set_type_hint(GTK_WINDOW(m_widget),
640 GDK_WINDOW_TYPE_HINT_UTILITY);
641
642 // On some WMs, like KDE, a TOOL_WINDOW will still show
643 // on the taskbar, but on Gnome a TOOL_WINDOW will not.
644 // For consistency between WMs and with Windows, we
645 // should set the NO_TASKBAR flag which will apply
646 // the set_skip_taskbar_hint if it is available,
647 // ensuring no taskbar entry will appear.
648 style |= wxFRAME_NO_TASKBAR;
649 }
650 }
651 #endif // wxUSE_LIBHILDON || wxUSE_LIBHILDON2/!wxUSE_LIBHILDON || !wxUSE_LIBHILDON2
652
653 g_object_ref(m_widget);
654 }
655
656 wxWindow *topParent = wxGetTopLevelParent(m_parent);
657 if (topParent && (((GTK_IS_WINDOW(topParent->m_widget)) &&
658 (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)) ||
659 (style & wxFRAME_FLOAT_ON_PARENT)))
660 {
661 gtk_window_set_transient_for( GTK_WINDOW(m_widget),
662 GTK_WINDOW(topParent->m_widget) );
663 }
664
665 if (style & wxFRAME_NO_TASKBAR)
666 {
667 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), TRUE);
668 }
669
670 if (style & wxSTAY_ON_TOP)
671 {
672 gtk_window_set_keep_above(GTK_WINDOW(m_widget), TRUE);
673 }
674 if (style & wxMAXIMIZE)
675 gtk_window_maximize(GTK_WINDOW(m_widget));
676
677 #if 0
678 if (!name.empty())
679 gtk_window_set_role( GTK_WINDOW(m_widget), wxGTK_CONV( name ) );
680 #endif
681
682 gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
683 gtk_widget_set_can_focus(m_widget, false);
684
685 g_signal_connect (m_widget, "delete_event",
686 G_CALLBACK (gtk_frame_delete_callback), this);
687
688 // m_mainWidget is a GtkVBox, holding the bars and client area (m_wxwindow)
689 m_mainWidget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
690 gtk_widget_show( m_mainWidget );
691 gtk_widget_set_can_focus(m_mainWidget, false);
692 gtk_container_add( GTK_CONTAINER(m_widget), m_mainWidget );
693
694 // m_wxwindow is the client area
695 m_wxwindow = wxPizza::New();
696 gtk_widget_show( m_wxwindow );
697 gtk_box_pack_start(GTK_BOX(m_mainWidget), m_wxwindow, true, true, 0);
698
699 // we donm't allow the frame to get the focus as otherwise
700 // the frame will grab it at arbitrary focus changes
701 gtk_widget_set_can_focus(m_wxwindow, false);
702
703 if (m_parent) m_parent->AddChild( this );
704
705 g_signal_connect(m_wxwindow, "size_allocate",
706 G_CALLBACK(size_allocate), this);
707
708 PostCreation();
709
710 if (pos.IsFullySpecified())
711 {
712 #ifdef __WXGTK3__
713 GtkWindowPosition windowPos;
714 g_object_get(m_widget, "window-position", &windowPos, NULL);
715 if (windowPos == GTK_WIN_POS_NONE)
716 gtk_window_move(GTK_WINDOW(m_widget), m_x, m_y);
717 #else
718 gtk_widget_set_uposition( m_widget, m_x, m_y );
719 #endif
720 }
721
722 // for some reported size corrections
723 g_signal_connect (m_widget, "map_event",
724 G_CALLBACK (gtk_frame_map_callback), this);
725
726 // for iconized state
727 g_signal_connect (m_widget, "window_state_event",
728 G_CALLBACK (gtk_frame_window_state_callback), this);
729
730
731 // for wxMoveEvent
732 g_signal_connect (m_widget, "configure_event",
733 G_CALLBACK (gtk_frame_configure_callback), this);
734
735 // activation
736 g_signal_connect_after (m_widget, "focus_in_event",
737 G_CALLBACK (gtk_frame_focus_in_callback), this);
738 g_signal_connect_after (m_widget, "focus_out_event",
739 G_CALLBACK (gtk_frame_focus_out_callback), this);
740
741 // We need to customize the default GTK+ logic for key processing to make
742 // it conforming to wxWidgets event processing order.
743 g_signal_connect (m_widget, "key_press_event",
744 G_CALLBACK (wxgtk_tlw_key_press_event), NULL);
745
746 #ifdef GDK_WINDOWING_X11
747 #ifdef __WXGTK3__
748 if (GDK_IS_X11_SCREEN(gtk_window_get_screen(GTK_WINDOW(m_widget))))
749 #endif
750 {
751 gtk_widget_add_events(m_widget, GDK_PROPERTY_CHANGE_MASK);
752 g_signal_connect(m_widget, "property_notify_event",
753 G_CALLBACK(property_notify_event), this);
754 }
755 #endif // GDK_WINDOWING_X11
756
757 // translate wx decorations styles into Motif WM hints (they are recognized
758 // by other WMs as well)
759
760 // always enable moving the window as we have no separate flag for enabling
761 // it
762 m_gdkFunc = GDK_FUNC_MOVE;
763
764 if ( style & wxCLOSE_BOX )
765 m_gdkFunc |= GDK_FUNC_CLOSE;
766
767 if ( style & wxMINIMIZE_BOX )
768 m_gdkFunc |= GDK_FUNC_MINIMIZE;
769
770 if ( style & wxMAXIMIZE_BOX )
771 m_gdkFunc |= GDK_FUNC_MAXIMIZE;
772
773 if ( (style & wxSIMPLE_BORDER) || (style & wxNO_BORDER) )
774 {
775 m_gdkDecor = 0;
776 gtk_window_set_decorated(GTK_WINDOW(m_widget), false);
777 }
778 else // have border
779 {
780 m_gdkDecor = GDK_DECOR_BORDER;
781
782 if ( style & wxCAPTION )
783 m_gdkDecor |= GDK_DECOR_TITLE;
784 #if GTK_CHECK_VERSION(3,10,0)
785 else if (
786 strcmp("GdkWaylandDisplay", g_type_name(G_TYPE_FROM_INSTANCE(gtk_widget_get_display(m_widget)))) == 0 &&
787 gtk_check_version(3,10,0) == NULL)
788 {
789 gtk_window_set_titlebar(GTK_WINDOW(m_widget), gtk_header_bar_new());
790 }
791 #endif
792
793 if ( style & wxSYSTEM_MENU )
794 m_gdkDecor |= GDK_DECOR_MENU;
795
796 if ( style & wxMINIMIZE_BOX )
797 m_gdkDecor |= GDK_DECOR_MINIMIZE;
798
799 if ( style & wxMAXIMIZE_BOX )
800 m_gdkDecor |= GDK_DECOR_MAXIMIZE;
801
802 if ( style & wxRESIZE_BORDER )
803 {
804 m_gdkFunc |= GDK_FUNC_RESIZE;
805 m_gdkDecor |= GDK_DECOR_RESIZEH;
806 }
807 }
808
809 m_decorSize = GetCachedDecorSize();
810 int w, h;
811 GTKDoGetSize(&w, &h);
812
813 if (style & wxRESIZE_BORDER)
814 {
815 gtk_window_set_default_size(GTK_WINDOW(m_widget), w, h);
816 #ifndef __WXGTK3__
817 gtk_window_set_policy(GTK_WINDOW(m_widget), 1, 1, 1);
818 #endif
819 }
820 else
821 {
822 gtk_window_set_resizable(GTK_WINDOW(m_widget), false);
823 // gtk_window_set_default_size() does not work for un-resizable windows,
824 // unless you set the size hints, but that causes Ubuntu's WM to make
825 // the window resizable even though GDK_FUNC_RESIZE is not set.
826 gtk_widget_set_size_request(m_widget, w, h);
827 }
828
829 g_signal_connect(gtk_settings_get_default(), "notify::gtk-theme-name",
830 G_CALLBACK(notify_gtk_theme_name), this);
831
832 return true;
833 }
834
~wxTopLevelWindowGTK()835 wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
836 {
837 if ( m_netFrameExtentsTimerId )
838 {
839 // Don't let the timer callback fire as the window pointer passed to it
840 // will become invalid very soon.
841 g_source_remove(m_netFrameExtentsTimerId);
842 }
843
844 #if wxUSE_LIBHILDON || wxUSE_LIBHILDON2
845 // it can also be a (standard) dialog
846 if ( HILDON_IS_WINDOW(m_widget) )
847 {
848 hildon_program_remove_window(wxTheApp->GetHildonProgram(),
849 HILDON_WINDOW(m_widget));
850 }
851 #endif // wxUSE_LIBHILDON || wxUSE_LIBHILDON2
852
853 if (m_grabbed)
854 {
855 wxFAIL_MSG(wxT("Window still grabbed"));
856 RemoveGrab();
857 }
858
859 SendDestroyEvent();
860
861 // it may also be GtkScrolledWindow in the case of an MDI child
862 if (GTK_IS_WINDOW(m_widget))
863 {
864 gtk_window_set_focus( GTK_WINDOW(m_widget), NULL );
865 }
866
867 if (g_activeFrame == this)
868 g_activeFrame = NULL;
869
870 g_signal_handlers_disconnect_by_func(
871 gtk_settings_get_default(), (void*)notify_gtk_theme_name, this);
872 }
873
EnableCloseButton(bool enable)874 bool wxTopLevelWindowGTK::EnableCloseButton( bool enable )
875 {
876 if (enable)
877 m_gdkFunc |= GDK_FUNC_CLOSE;
878 else
879 m_gdkFunc &= ~GDK_FUNC_CLOSE;
880
881 GdkWindow* window = gtk_widget_get_window(m_widget);
882 if (window)
883 gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
884
885 return true;
886 }
887
ShowFullScreen(bool show,long)888 bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long)
889 {
890 if (show == m_fsIsShowing)
891 return false; // return what?
892
893 m_fsIsShowing = show;
894
895 #ifdef GDK_WINDOWING_X11
896 GdkScreen* screen = gtk_widget_get_screen(m_widget);
897 GdkDisplay* display = gdk_screen_get_display(screen);
898 Display* xdpy = NULL;
899 Window xroot = None;
900 wxX11FullScreenMethod method = wxX11_FS_WMSPEC;
901
902 if (GDK_IS_X11_DISPLAY(display))
903 {
904 xdpy = GDK_DISPLAY_XDISPLAY(display);
905 xroot = GDK_WINDOW_XID(gdk_screen_get_root_window(screen));
906 method = wxGetFullScreenMethodX11(xdpy, (WXWindow)xroot);
907 }
908
909 // NB: gtk_window_fullscreen() uses freedesktop.org's WMspec extensions
910 // to switch to fullscreen, which is not always available. We must
911 // check if WM supports the spec and use legacy methods if it
912 // doesn't.
913 if ( method == wxX11_FS_WMSPEC )
914 #endif // GDK_WINDOWING_X11
915 {
916 if (show)
917 gtk_window_fullscreen( GTK_WINDOW( m_widget ) );
918 else
919 gtk_window_unfullscreen( GTK_WINDOW( m_widget ) );
920 }
921 #ifdef GDK_WINDOWING_X11
922 else if (xdpy != NULL)
923 {
924 GdkWindow* window = gtk_widget_get_window(m_widget);
925 Window xid = GDK_WINDOW_XID(window);
926
927 if (show)
928 {
929 GetPosition( &m_fsSaveFrame.x, &m_fsSaveFrame.y );
930 GetSize( &m_fsSaveFrame.width, &m_fsSaveFrame.height );
931
932 const int screen_width = gdk_screen_get_width(screen);
933 const int screen_height = gdk_screen_get_height(screen);
934
935 gint client_x, client_y, root_x, root_y;
936 gint width, height;
937
938 m_fsSaveGdkFunc = m_gdkFunc;
939 m_fsSaveGdkDecor = m_gdkDecor;
940 m_gdkFunc = m_gdkDecor = 0;
941 gdk_window_set_decorations(window, (GdkWMDecoration)0);
942 gdk_window_set_functions(window, (GdkWMFunction)0);
943
944 gdk_window_get_origin(window, &root_x, &root_y);
945 gdk_window_get_geometry(window, &client_x, &client_y, &width, &height);
946
947 gdk_window_move_resize(
948 window, -client_x, -client_y, screen_width + 1, screen_height + 1);
949
950 wxSetFullScreenStateX11(xdpy,
951 (WXWindow)xroot,
952 (WXWindow)xid,
953 show, &m_fsSaveFrame, method);
954 }
955 else // hide
956 {
957 m_gdkFunc = m_fsSaveGdkFunc;
958 m_gdkDecor = m_fsSaveGdkDecor;
959 gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
960 gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
961
962 wxSetFullScreenStateX11(xdpy,
963 (WXWindow)xroot,
964 (WXWindow)xid,
965 show, &m_fsSaveFrame, method);
966
967 SetSize(m_fsSaveFrame.x, m_fsSaveFrame.y,
968 m_fsSaveFrame.width, m_fsSaveFrame.height);
969 }
970 }
971 #endif // GDK_WINDOWING_X11
972
973 // documented behaviour is to show the window if it's still hidden when
974 // showing it full screen
975 if (show)
976 Show();
977
978 return true;
979 }
980
981 // ----------------------------------------------------------------------------
982 // overridden wxWindow methods
983 // ----------------------------------------------------------------------------
984
Refresh(bool WXUNUSED (eraseBackground),const wxRect * WXUNUSED (rect))985 void wxTopLevelWindowGTK::Refresh( bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect) )
986 {
987 wxCHECK_RET( m_widget, wxT("invalid frame") );
988
989 gtk_widget_queue_draw( m_widget );
990
991 GdkWindow* window = NULL;
992 if (m_wxwindow)
993 window = gtk_widget_get_window(m_wxwindow);
994 if (window)
995 gdk_window_invalidate_rect(window, NULL, true);
996 }
997
998 #if defined(__WXGTK3__) && defined(GDK_WINDOWING_X11)
999 // Check conditions under which GTK will use Client Side Decorations with X11
isUsingCSD(GtkWidget * widget)1000 static bool isUsingCSD(GtkWidget* widget)
1001 {
1002 const char* csd = getenv("GTK_CSD");
1003 if (csd == NULL || strcmp(csd, "1") != 0)
1004 return false;
1005
1006 GdkScreen* screen = gtk_widget_get_screen(widget);
1007 if (!gdk_screen_is_composited(screen))
1008 return false;
1009
1010 GdkAtom atom = gdk_atom_intern_static_string("_GTK_FRAME_EXTENTS");
1011 if (!gdk_x11_screen_supports_net_wm_hint(screen, atom))
1012 return false;
1013
1014 if (gdk_screen_get_rgba_visual(screen) == NULL)
1015 return false;
1016
1017 return true;
1018 }
1019 #endif // __WXGTK3__ && GDK_WINDOWING_X11
1020
Show(bool show)1021 bool wxTopLevelWindowGTK::Show( bool show )
1022 {
1023 wxCHECK_MSG(m_widget, false, "invalid frame");
1024
1025 #ifdef GDK_WINDOWING_X11
1026 bool deferShow = show && !m_isShown && m_deferShow;
1027 if (deferShow)
1028 {
1029 deferShow = m_deferShowAllowed &&
1030 // Assume size (from cache or wxPersistentTLW) is correct.
1031 // Avoids problems when WM initially provides an incorrect value
1032 // for extents, then corrects it later.
1033 m_decorSize.top == 0 &&
1034
1035 gs_requestFrameExtentsStatus != RFE_STATUS_BROKEN &&
1036 !gtk_widget_get_realized(m_widget) &&
1037 GDK_IS_X11_DISPLAY(gtk_widget_get_display(m_widget)) &&
1038 g_signal_handler_find(m_widget,
1039 GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
1040 g_signal_lookup("property_notify_event", GTK_TYPE_WIDGET),
1041 0, NULL, NULL, this);
1042 #ifdef __WXGTK3__
1043 // Don't defer with CSD, it isn't needed and causes pixman errors
1044 if (deferShow)
1045 deferShow = !isUsingCSD(m_widget);
1046 #endif
1047 if (deferShow)
1048 {
1049 GdkScreen* screen = gtk_widget_get_screen(m_widget);
1050 GdkAtom atom = gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false);
1051 deferShow = gdk_x11_screen_supports_net_wm_hint(screen, atom) != 0;
1052
1053 // If _NET_REQUEST_FRAME_EXTENTS not supported, don't allow changes
1054 // to m_decorSize, it breaks saving/restoring window size with
1055 // GetSize()/SetSize() because it makes window bigger between each
1056 // restore and save.
1057 m_updateDecorSize = deferShow;
1058 }
1059
1060 m_deferShow = deferShow;
1061 }
1062 if (deferShow)
1063 {
1064 // Initial show. If WM supports _NET_REQUEST_FRAME_EXTENTS, defer
1065 // calling gtk_widget_show() until _NET_FRAME_EXTENTS property
1066 // notification is received, so correct frame extents are known.
1067 // This allows resizing m_widget to keep the overall size in sync with
1068 // what wxWidgets expects it to be without an obvious change in the
1069 // window size immediately after it becomes visible.
1070
1071 // Realize m_widget, so m_widget->window can be used. Realizing normally
1072 // causes the widget tree to be size_allocated, which generates size
1073 // events in the wrong order. However, the size_allocates will not be
1074 // done if the allocation is not the default (1,1).
1075 GtkAllocation alloc;
1076 gtk_widget_get_allocation(m_widget, &alloc);
1077 const int alloc_width = alloc.width;
1078 if (alloc_width == 1)
1079 {
1080 alloc.width = 2;
1081 gtk_widget_set_allocation(m_widget, &alloc);
1082 }
1083 gtk_widget_realize(m_widget);
1084 if (alloc_width == 1)
1085 {
1086 alloc.width = 1;
1087 gtk_widget_set_allocation(m_widget, &alloc);
1088 }
1089
1090 // send _NET_REQUEST_FRAME_EXTENTS
1091 XClientMessageEvent xevent;
1092 memset(&xevent, 0, sizeof(xevent));
1093 xevent.type = ClientMessage;
1094 GdkWindow* window = gtk_widget_get_window(m_widget);
1095 xevent.window = GDK_WINDOW_XID(window);
1096 xevent.message_type = gdk_x11_atom_to_xatom_for_display(
1097 gdk_window_get_display(window),
1098 gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false));
1099 xevent.format = 32;
1100 Display* display = GDK_DISPLAY_XDISPLAY(gdk_window_get_display(window));
1101 XSendEvent(display, DefaultRootWindow(display), false,
1102 SubstructureNotifyMask | SubstructureRedirectMask,
1103 (XEvent*)&xevent);
1104
1105 if (gs_requestFrameExtentsStatus == RFE_STATUS_UNKNOWN)
1106 {
1107 // if WM does not respond to request within 1 second,
1108 // we assume support for _NET_REQUEST_FRAME_EXTENTS is not working
1109 m_netFrameExtentsTimerId =
1110 g_timeout_add(1000, request_frame_extents_timeout, this);
1111 }
1112
1113 // defer calling gtk_widget_show()
1114 m_isShown = true;
1115 return true;
1116 }
1117 #endif // GDK_WINDOWING_X11
1118
1119 if (show && !gtk_widget_get_realized(m_widget))
1120 {
1121 // size_allocate signals occur in reverse order (bottom to top).
1122 // Things work better if the initial wxSizeEvents are sent (from the
1123 // top down), before the initial size_allocate signals occur.
1124 SendSizeEvent();
1125
1126 #ifdef __WXGTK3__
1127 wxGTKSizeRevalidate(this);
1128 #endif
1129 }
1130
1131 bool change = base_type::Show(show);
1132
1133 #ifdef __WXGTK3__
1134 if (GList* p = g_list_find(wx_sizeEventList, this))
1135 {
1136 wx_sizeEventList = g_list_delete_link(wx_sizeEventList, p);
1137 SendSizeEvent();
1138 }
1139 #endif
1140
1141 if (change && !show)
1142 {
1143 // make sure window has a non-default position, so when it is shown
1144 // again, it won't be repositioned by WM as if it were a new window
1145 // Note that this must be done _after_ the window is hidden.
1146 gtk_window_move((GtkWindow*)m_widget, m_x, m_y);
1147 }
1148
1149 return change;
1150 }
1151
ShowWithoutActivating()1152 void wxTopLevelWindowGTK::ShowWithoutActivating()
1153 {
1154 if (!m_isShown)
1155 {
1156 gtk_window_set_focus_on_map(GTK_WINDOW(m_widget), false);
1157 Show(true);
1158 }
1159 }
1160
Raise()1161 void wxTopLevelWindowGTK::Raise()
1162 {
1163 gtk_window_present( GTK_WINDOW( m_widget ) );
1164 }
1165
DoMoveWindow(int WXUNUSED (x),int WXUNUSED (y),int WXUNUSED (width),int WXUNUSED (height))1166 void wxTopLevelWindowGTK::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) )
1167 {
1168 wxFAIL_MSG( wxT("DoMoveWindow called for wxTopLevelWindowGTK") );
1169 }
1170
1171 // ----------------------------------------------------------------------------
1172 // window geometry
1173 // ----------------------------------------------------------------------------
1174
GTKDoGetSize(int * width,int * height) const1175 void wxTopLevelWindowGTK::GTKDoGetSize(int *width, int *height) const
1176 {
1177 wxSize size(m_width, m_height);
1178 if (!HasClientDecor(m_widget))
1179 {
1180 size.x -= m_decorSize.left + m_decorSize.right;
1181 size.y -= m_decorSize.top + m_decorSize.bottom;
1182 }
1183 if (size.x < 0) size.x = 0;
1184 if (size.y < 0) size.y = 0;
1185 #if wxUSE_LIBHILDON2
1186 if (width) {
1187 if (size.x == 720)
1188 *width = 696;
1189 else
1190 *width = size.x;
1191 }
1192 if (height) {
1193 if (size.y == 420)
1194 *height = 396;
1195 else if (size.y == 270)
1196 *height = 246;
1197 else
1198 *height = size.y;
1199 }
1200 #else // wxUSE_LIBHILDON2
1201 if (width) *width = size.x;
1202 if (height) *height = size.y;
1203 #endif // wxUSE_LIBHILDON2 /!wxUSE_LIBHILDON2
1204 }
1205
DoSetSize(int x,int y,int width,int height,int sizeFlags)1206 void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
1207 {
1208 wxCHECK_RET( m_widget, wxT("invalid frame") );
1209
1210 // deal with the position first
1211 int old_x = m_x;
1212 int old_y = m_y;
1213
1214 if ( !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) )
1215 {
1216 // -1 means "use existing" unless the flag above is specified
1217 if ( x != -1 )
1218 m_x = x;
1219 if ( y != -1 )
1220 m_y = y;
1221 }
1222 else // wxSIZE_ALLOW_MINUS_ONE
1223 {
1224 m_x = x;
1225 m_y = y;
1226 }
1227
1228 const wxSize oldSize(m_width, m_height);
1229 if (width >= 0)
1230 m_width = width;
1231 if (height >= 0)
1232 m_height = height;
1233 ConstrainSize();
1234 if (m_width < 1) m_width = 1;
1235 if (m_height < 1) m_height = 1;
1236
1237 if ( m_x != old_x || m_y != old_y )
1238 {
1239 gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y );
1240 wxMoveEvent event(wxPoint(m_x, m_y), GetId());
1241 event.SetEventObject(this);
1242 HandleWindowEvent(event);
1243 }
1244
1245 if (m_width != oldSize.x || m_height != oldSize.y)
1246 {
1247 m_deferShowAllowed = true;
1248 m_useCachedClientSize = false;
1249
1250 int w, h;
1251 GTKDoGetSize(&w, &h);
1252 gtk_window_resize(GTK_WINDOW(m_widget), w, h);
1253 if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
1254 gtk_widget_set_size_request(GTK_WIDGET(m_widget), w, h);
1255
1256 DoGetClientSize(&m_clientWidth, &m_clientHeight);
1257 wxSizeEvent event(GetSize(), GetId());
1258 event.SetEventObject(this);
1259 HandleWindowEvent(event);
1260 }
1261 }
1262
1263 extern "C" {
reset_size_request(void * data)1264 static gboolean reset_size_request(void* data)
1265 {
1266 gtk_widget_set_size_request(GTK_WIDGET(data), -1, -1);
1267 g_object_unref(data);
1268 return false;
1269 }
1270 }
1271
DoSetClientSize(int width,int height)1272 void wxTopLevelWindowGTK::DoSetClientSize(int width, int height)
1273 {
1274 base_type::DoSetClientSize(width, height);
1275
1276 // Since client size is being explicitly set, don't change it later
1277 // Has to be done after calling base because it calls SetSize,
1278 // which sets this true
1279 m_deferShowAllowed = false;
1280
1281 if (m_wxwindow)
1282 {
1283 // If window is not resizable or not yet shown, set size request on
1284 // client widget, to make it more likely window will get correct size
1285 // even if our decorations size cache is incorrect (as it will be before
1286 // showing first TLW).
1287 if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
1288 {
1289 gtk_widget_set_size_request(m_widget, -1, -1);
1290 gtk_widget_set_size_request(m_wxwindow, m_clientWidth, m_clientHeight);
1291 }
1292 else if (!IsShown())
1293 {
1294 gtk_widget_set_size_request(m_wxwindow, m_clientWidth, m_clientHeight);
1295 // Cancel size request at next idle to allow resizing
1296 g_idle_add_full(G_PRIORITY_LOW - 1, reset_size_request, m_wxwindow, NULL);
1297 g_object_ref(m_wxwindow);
1298 }
1299 }
1300 }
1301
DoGetClientSize(int * width,int * height) const1302 void wxTopLevelWindowGTK::DoGetClientSize( int *width, int *height ) const
1303 {
1304 wxCHECK_RET(m_widget, "invalid frame");
1305
1306 if ( IsIconized() )
1307 {
1308 // for consistency with wxMSW, client area is supposed to be empty for
1309 // the iconized windows
1310 if ( width )
1311 *width = 0;
1312 if ( height )
1313 *height = 0;
1314 }
1315 else if (m_useCachedClientSize)
1316 base_type::DoGetClientSize(width, height);
1317 else
1318 {
1319 int w = m_width - (m_decorSize.left + m_decorSize.right);
1320 int h = m_height - (m_decorSize.top + m_decorSize.bottom);
1321 if (w < 0) w = 0;
1322 if (h < 0) h = 0;
1323 if (width) *width = w;
1324 if (height) *height = h;
1325 }
1326 }
1327
DoSetSizeHints(int minW,int minH,int maxW,int maxH,int incW,int incH)1328 void wxTopLevelWindowGTK::DoSetSizeHints( int minW, int minH,
1329 int maxW, int maxH,
1330 int incW, int incH )
1331 {
1332 base_type::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
1333 m_incWidth = incW;
1334 m_incHeight = incH;
1335
1336 const wxSize minSize = GetMinSize();
1337 const wxSize maxSize = GetMaxSize();
1338 GdkGeometry hints;
1339 // always set both min and max hints, otherwise GTK will
1340 // make assumptions we don't want about the unset values
1341 int hints_mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
1342 hints.min_width = 1;
1343 hints.min_height = 1;
1344 // using INT_MAX for size will lead to integer overflow with HiDPI scaling
1345 hints.max_width = INT_MAX / 16;
1346 hints.max_height = INT_MAX / 16;
1347 int decorSize_x;
1348 int decorSize_y;
1349 if (HasClientDecor(m_widget))
1350 {
1351 decorSize_x = 0;
1352 decorSize_y = 0;
1353 }
1354 else
1355 {
1356 decorSize_x = m_decorSize.left + m_decorSize.right;
1357 decorSize_y = m_decorSize.top + m_decorSize.bottom;
1358 }
1359 if (minSize.x > decorSize_x)
1360 hints.min_width = minSize.x - decorSize_x;
1361 if (minSize.y > decorSize_y)
1362 hints.min_height = minSize.y - decorSize_y;
1363 if (maxSize.x > 0)
1364 {
1365 hints.max_width = maxSize.x - decorSize_x;
1366 if (hints.max_width < hints.min_width)
1367 hints.max_width = hints.min_width;
1368 }
1369 if (maxSize.y > 0)
1370 {
1371 hints.max_height = maxSize.y - decorSize_y;
1372 if (hints.max_height < hints.min_height)
1373 hints.max_height = hints.min_height;
1374 }
1375 if (incW > 0 || incH > 0)
1376 {
1377 hints_mask |= GDK_HINT_RESIZE_INC;
1378 hints.width_inc = incW > 0 ? incW : 1;
1379 hints.height_inc = incH > 0 ? incH : 1;
1380 }
1381 gtk_window_set_geometry_hints(
1382 (GtkWindow*)m_widget, NULL, &hints, (GdkWindowHints)hints_mask);
1383 }
1384
GTKUpdateDecorSize(const DecorSize & decorSize)1385 void wxTopLevelWindowGTK::GTKUpdateDecorSize(const DecorSize& decorSize)
1386 {
1387 if (!IsMaximized() && !IsFullScreen())
1388 GetCachedDecorSize() = decorSize;
1389
1390 if (HasClientDecor(m_widget))
1391 {
1392 m_decorSize = decorSize;
1393 return;
1394 }
1395 #ifdef GDK_WINDOWING_X11
1396 if (m_updateDecorSize && memcmp(&m_decorSize, &decorSize, sizeof(DecorSize)))
1397 {
1398 m_useCachedClientSize = false;
1399 const wxSize diff(
1400 decorSize.left - m_decorSize.left + decorSize.right - m_decorSize.right,
1401 decorSize.top - m_decorSize.top + decorSize.bottom - m_decorSize.bottom);
1402 m_decorSize = decorSize;
1403 bool resized = false;
1404 if (m_minWidth > 0 || m_minHeight > 0 || m_maxWidth > 0 || m_maxHeight > 0)
1405 {
1406 // update size hints, they depend on m_decorSize
1407 if (!m_deferShow)
1408 {
1409 // if size hints match old size, assume hints were set to
1410 // maintain current client size, and adjust hints accordingly
1411 if (m_minWidth == m_width) m_minWidth += diff.x;
1412 if (m_maxWidth == m_width) m_maxWidth += diff.x;
1413 if (m_minHeight == m_height) m_minHeight += diff.y;
1414 if (m_maxHeight == m_height) m_maxHeight += diff.y;
1415 }
1416 DoSetSizeHints(m_minWidth, m_minHeight, m_maxWidth, m_maxHeight, m_incWidth, m_incHeight);
1417 }
1418 if (m_deferShow)
1419 {
1420 // keep overall size unchanged by shrinking m_widget
1421 int w, h;
1422 GTKDoGetSize(&w, &h);
1423 // but not if size would be less than minimum, it won't take effect
1424 if (w >= m_minWidth - (decorSize.left + decorSize.right) &&
1425 h >= m_minHeight - (decorSize.top + decorSize.bottom))
1426 {
1427 gtk_window_resize(GTK_WINDOW(m_widget), w, h);
1428 if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
1429 gtk_widget_set_size_request(GTK_WIDGET(m_widget), w, h);
1430 resized = true;
1431 }
1432 }
1433 if (!resized)
1434 {
1435 // adjust overall size to match change in frame extents
1436 m_width += diff.x;
1437 m_height += diff.y;
1438 if (m_width < 1) m_width = 1;
1439 if (m_height < 1) m_height = 1;
1440 m_clientWidth = 0;
1441 gtk_widget_queue_resize(m_wxwindow);
1442 }
1443 }
1444 if (m_deferShow)
1445 {
1446 // gtk_widget_show() was deferred, do it now
1447 m_deferShow = false;
1448 DoGetClientSize(&m_clientWidth, &m_clientHeight);
1449 SendSizeEvent();
1450
1451 #ifdef __WXGTK3__
1452 wxGTKSizeRevalidate(this);
1453 #endif
1454 if (!m_isShown)
1455 return;
1456
1457 gtk_widget_show(m_widget);
1458
1459 #ifdef __WXGTK3__
1460 if (GList* p = g_list_find(wx_sizeEventList, this))
1461 {
1462 wx_sizeEventList = g_list_delete_link(wx_sizeEventList, p);
1463 SendSizeEvent();
1464 }
1465 #endif
1466 wxShowEvent showEvent(GetId(), true);
1467 showEvent.SetEventObject(this);
1468 HandleWindowEvent(showEvent);
1469 }
1470 #endif // GDK_WINDOWING_X11
1471 }
1472
GetCachedDecorSize()1473 wxTopLevelWindowGTK::DecorSize& wxTopLevelWindowGTK::GetCachedDecorSize()
1474 {
1475 static DecorSize size[8];
1476
1477 int index = 0;
1478 // title bar
1479 if (m_gdkDecor & (GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE | GDK_DECOR_TITLE))
1480 index = 1;
1481 // border
1482 if (m_gdkDecor & GDK_DECOR_BORDER)
1483 index |= 2;
1484 // utility window decor can be different
1485 if (m_windowStyle & wxFRAME_TOOL_WINDOW)
1486 index |= 4;
1487 return size[index];
1488 }
1489
OnInternalIdle()1490 void wxTopLevelWindowGTK::OnInternalIdle()
1491 {
1492 wxTopLevelWindowBase::OnInternalIdle();
1493 }
1494
1495 // ----------------------------------------------------------------------------
1496 // frame title/icon
1497 // ----------------------------------------------------------------------------
1498
SetTitle(const wxString & title)1499 void wxTopLevelWindowGTK::SetTitle( const wxString &title )
1500 {
1501 wxCHECK_RET(m_widget, "invalid frame");
1502
1503 if ( title == m_title )
1504 return;
1505
1506 m_title = title;
1507
1508 gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
1509 }
1510
SetIcons(const wxIconBundle & icons)1511 void wxTopLevelWindowGTK::SetIcons( const wxIconBundle &icons )
1512 {
1513 base_type::SetIcons(icons);
1514
1515 // Setting icons before window is realized can cause a GTK assertion if
1516 // another TLW is realized before this one, and it has this one as it's
1517 // transient parent. The life demo exibits this problem.
1518 if (m_widget && gtk_widget_get_realized(m_widget))
1519 {
1520 GList* list = NULL;
1521 for (size_t i = icons.GetIconCount(); i--;)
1522 list = g_list_prepend(list, icons.GetIconByIndex(i).GetPixbuf());
1523 gtk_window_set_icon_list(GTK_WINDOW(m_widget), list);
1524 g_list_free(list);
1525 }
1526 }
1527
1528 // ----------------------------------------------------------------------------
1529 // frame state: maximized/iconized/normal
1530 // ----------------------------------------------------------------------------
1531
Maximize(bool maximize)1532 void wxTopLevelWindowGTK::Maximize(bool maximize)
1533 {
1534 if (maximize)
1535 gtk_window_maximize( GTK_WINDOW( m_widget ) );
1536 else
1537 gtk_window_unmaximize( GTK_WINDOW( m_widget ) );
1538 }
1539
IsMaximized() const1540 bool wxTopLevelWindowGTK::IsMaximized() const
1541 {
1542 GdkWindow* window = NULL;
1543 if (m_widget)
1544 window = gtk_widget_get_window(m_widget);
1545 return window && (gdk_window_get_state(window) & GDK_WINDOW_STATE_MAXIMIZED);
1546 }
1547
Restore()1548 void wxTopLevelWindowGTK::Restore()
1549 {
1550 // "Present" seems similar enough to "restore"
1551 gtk_window_present( GTK_WINDOW( m_widget ) );
1552 }
1553
Iconize(bool iconize)1554 void wxTopLevelWindowGTK::Iconize( bool iconize )
1555 {
1556 if (iconize)
1557 gtk_window_iconify( GTK_WINDOW( m_widget ) );
1558 else
1559 gtk_window_deiconify( GTK_WINDOW( m_widget ) );
1560 }
1561
IsIconized() const1562 bool wxTopLevelWindowGTK::IsIconized() const
1563 {
1564 return m_isIconized;
1565 }
1566
SetIconizeState(bool iconize)1567 void wxTopLevelWindowGTK::SetIconizeState(bool iconize)
1568 {
1569 if ( iconize != m_isIconized )
1570 {
1571 m_isIconized = iconize;
1572 (void)SendIconizeEvent(iconize);
1573 }
1574 }
1575
AddGrab()1576 void wxTopLevelWindowGTK::AddGrab()
1577 {
1578 if (!m_grabbed)
1579 {
1580 m_grabbed = true;
1581 gtk_grab_add( m_widget );
1582 wxGUIEventLoop().Run();
1583 gtk_grab_remove( m_widget );
1584 }
1585 }
1586
RemoveGrab()1587 void wxTopLevelWindowGTK::RemoveGrab()
1588 {
1589 if (m_grabbed)
1590 {
1591 wxGUIEventLoop::GetActive()->Exit();
1592 m_grabbed = false;
1593 }
1594 }
1595
IsActive()1596 bool wxTopLevelWindowGTK::IsActive()
1597 {
1598 return (this == (wxTopLevelWindowGTK*)g_activeFrame);
1599 }
1600
RequestUserAttention(int flags)1601 void wxTopLevelWindowGTK::RequestUserAttention(int flags)
1602 {
1603 bool new_hint_value = false;
1604
1605 // FIXME: This is a workaround to focus handling problem
1606 // If RequestUserAttention is called for example right after a wxSleep, OnInternalIdle
1607 // hasn't yet been processed, and the internal focus system is not up to date yet.
1608 // YieldFor(wxEVT_CATEGORY_UI) ensures the processing of it (hopefully it
1609 // won't have side effects) - MR
1610 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
1611
1612 if(m_urgency_hint >= 0)
1613 g_source_remove(m_urgency_hint);
1614
1615 m_urgency_hint = -2;
1616
1617 if( gtk_widget_get_realized(m_widget) && !IsActive() )
1618 {
1619 new_hint_value = true;
1620
1621 if (flags & wxUSER_ATTENTION_INFO)
1622 {
1623 m_urgency_hint = g_timeout_add(5000, (GSourceFunc)gtk_frame_urgency_timer_callback, this);
1624 } else {
1625 m_urgency_hint = -1;
1626 }
1627 }
1628
1629 gtk_window_set_urgency_hint(GTK_WINDOW(m_widget), new_hint_value);
1630 }
1631
SetWindowStyleFlag(long style)1632 void wxTopLevelWindowGTK::SetWindowStyleFlag( long style )
1633 {
1634 // Store which styles were changed
1635 long styleChanges = style ^ m_windowStyle;
1636
1637 // Process wxWindow styles. This also updates the internal variable
1638 // Therefore m_windowStyle bits carry now the _new_ style values
1639 wxWindow::SetWindowStyleFlag(style);
1640
1641 // just return for now if widget does not exist yet
1642 if (!m_widget)
1643 return;
1644
1645 if ( styleChanges & wxSTAY_ON_TOP )
1646 {
1647 gtk_window_set_keep_above(GTK_WINDOW(m_widget),
1648 m_windowStyle & wxSTAY_ON_TOP);
1649 }
1650
1651 if ( styleChanges & wxFRAME_NO_TASKBAR )
1652 {
1653 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget),
1654 m_windowStyle & wxFRAME_NO_TASKBAR);
1655 }
1656 }
1657
SetTransparent(wxByte alpha)1658 bool wxTopLevelWindowGTK::SetTransparent(wxByte alpha)
1659 {
1660 if (m_widget == NULL)
1661 return false;
1662 #if GTK_CHECK_VERSION(2,12,0)
1663 #ifndef __WXGTK3__
1664 if (gtk_check_version(2,12,0) == NULL)
1665 #endif
1666 {
1667 gtk_window_set_opacity(GTK_WINDOW(m_widget), alpha / 255.0);
1668 return true;
1669 }
1670 #endif // GTK_CHECK_VERSION(2,12,0)
1671 #ifndef __WXGTK3__
1672 #ifdef GDK_WINDOWING_X11
1673 GdkWindow* window = gtk_widget_get_window(m_widget);
1674 if (window == NULL)
1675 return false;
1676
1677 Display* dpy = GDK_WINDOW_XDISPLAY(window);
1678 Window win = GDK_WINDOW_XID(window);
1679
1680 if (alpha == 0xff)
1681 XDeleteProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False));
1682 else
1683 {
1684 long opacity = alpha * 0x1010101L;
1685 XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False),
1686 XA_CARDINAL, 32, PropModeReplace,
1687 (unsigned char *) &opacity, 1L);
1688 }
1689 XSync(dpy, False);
1690 return true;
1691 #else // !GDK_WINDOWING_X11
1692 return false;
1693 #endif // GDK_WINDOWING_X11 / !GDK_WINDOWING_X11
1694 #endif // !__WXGTK3__
1695 }
1696
CanSetTransparent()1697 bool wxTopLevelWindowGTK::CanSetTransparent()
1698 {
1699 // allow to override automatic detection as it's far from perfect
1700 const wxString SYSOPT_TRANSPARENT = "gtk.tlw.can-set-transparent";
1701 if ( wxSystemOptions::HasOption(SYSOPT_TRANSPARENT) )
1702 {
1703 return wxSystemOptions::GetOptionInt(SYSOPT_TRANSPARENT) != 0;
1704 }
1705
1706 #ifdef __WXGTK3__
1707 return gtk_widget_is_composited(m_widget) != 0;
1708 #else
1709 #if GTK_CHECK_VERSION(2,10,0)
1710 if (!gtk_check_version(2,10,0))
1711 {
1712 return gtk_widget_is_composited(m_widget) != 0;
1713 }
1714 else
1715 #endif // In case of lower versions than gtk+-2.10.0 we could look for _NET_WM_CM_Sn ourselves
1716 {
1717 return false;
1718 }
1719 #endif // !__WXGTK3__
1720
1721 #if 0 // Don't be optimistic here for the sake of wxAUI
1722 int opcode, event, error;
1723 // Check for the existence of a RGBA visual instead?
1724 return XQueryExtension(gdk_x11_get_default_xdisplay (),
1725 "Composite", &opcode, &event, &error);
1726 #endif
1727 }
1728