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