1739 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1739 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/////////////////////////////////////////////////////////////////////////////
 | 
						|
// Name:        src/gtk/toplevel.cpp
 | 
						|
// Purpose:
 | 
						|
// Author:      Robert Roebling
 | 
						|
// Copyright:   (c) 1998 Robert Roebling
 | 
						|
// Licence:     wxWindows licence
 | 
						|
/////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
// For compilers that support precompilation, includes "wx.h".
 | 
						|
#include "wx/wxprec.h"
 | 
						|
 | 
						|
// ============================================================================
 | 
						|
// declarations
 | 
						|
// ============================================================================
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// headers
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
#ifdef __VMS
 | 
						|
#define XIconifyWindow XICONIFYWINDOW
 | 
						|
#endif
 | 
						|
 | 
						|
#include "wx/toplevel.h"
 | 
						|
 | 
						|
#ifndef WX_PRECOMP
 | 
						|
    #include "wx/frame.h"
 | 
						|
    #include "wx/icon.h"
 | 
						|
    #include "wx/log.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include "wx/evtloop.h"
 | 
						|
#include "wx/sysopt.h"
 | 
						|
 | 
						|
#include "wx/gtk/private.h"
 | 
						|
#include "wx/gtk/private/gtk3-compat.h"
 | 
						|
#include "wx/gtk/private/win_gtk.h"
 | 
						|
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
    #include <gdk/gdkx.h>
 | 
						|
    #include <X11/Xatom.h>  // XA_CARDINAL
 | 
						|
    #include "wx/unix/utilsx11.h"
 | 
						|
#endif
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// data
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// this is incremented while a modal dialog is shown
 | 
						|
int wxOpenModalDialogsCount = 0;
 | 
						|
 | 
						|
// the frame that is currently active (i.e. its child has focus). It is
 | 
						|
// used to generate wxActivateEvents
 | 
						|
static wxTopLevelWindowGTK *g_activeFrame = NULL;
 | 
						|
 | 
						|
extern wxCursor g_globalCursor;
 | 
						|
extern wxCursor g_busyCursor;
 | 
						|
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
// Whether _NET_REQUEST_FRAME_EXTENTS support is working
 | 
						|
static enum {
 | 
						|
    RFE_STATUS_UNKNOWN, RFE_STATUS_WORKING, RFE_STATUS_BROKEN
 | 
						|
} gs_requestFrameExtentsStatus;
 | 
						|
 | 
						|
static bool gs_decorCacheValid;
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
static bool HasClientDecor(GtkWidget* widget)
 | 
						|
{
 | 
						|
    static bool has;
 | 
						|
    static bool once;
 | 
						|
    if (!once)
 | 
						|
    {
 | 
						|
        once = true;
 | 
						|
        GdkDisplay* display = gtk_widget_get_display(widget);
 | 
						|
        const char* name = g_type_name(G_TYPE_FROM_INSTANCE(display));
 | 
						|
        has =
 | 
						|
            strcmp(name, "GdkWaylandDisplay") == 0 ||
 | 
						|
            strcmp(name, "GdkMirDisplay") == 0 ||
 | 
						|
            strcmp(name, "GdkBroadwayDisplay") == 0;
 | 
						|
    }
 | 
						|
    return has;
 | 
						|
}
 | 
						|
#else
 | 
						|
static inline bool HasClientDecor(GtkWidget*)
 | 
						|
{
 | 
						|
    return false;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// RequestUserAttention related functions
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
#ifndef __WXGTK3__
 | 
						|
static void wxgtk_window_set_urgency_hint (GtkWindow *win,
 | 
						|
                                           gboolean setting)
 | 
						|
{
 | 
						|
#if GTK_CHECK_VERSION(2,7,0)
 | 
						|
    if (wx_is_at_least_gtk2(7))
 | 
						|
        gtk_window_set_urgency_hint(win, setting);
 | 
						|
    else
 | 
						|
#endif
 | 
						|
    {
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(win));
 | 
						|
        wxCHECK_RET(window, "wxgtk_window_set_urgency_hint: GdkWindow not realized");
 | 
						|
 | 
						|
        Display* dpy = GDK_WINDOW_XDISPLAY(window);
 | 
						|
        Window xid = GDK_WINDOW_XID(window);
 | 
						|
        XWMHints* wm_hints = XGetWMHints(dpy, xid);
 | 
						|
 | 
						|
        if (!wm_hints)
 | 
						|
            wm_hints = XAllocWMHints();
 | 
						|
 | 
						|
        if (setting)
 | 
						|
            wm_hints->flags |= XUrgencyHint;
 | 
						|
        else
 | 
						|
            wm_hints->flags &= ~XUrgencyHint;
 | 
						|
 | 
						|
        XSetWMHints(dpy, xid, wm_hints);
 | 
						|
        XFree(wm_hints);
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
    }
 | 
						|
}
 | 
						|
#define gtk_window_set_urgency_hint wxgtk_window_set_urgency_hint
 | 
						|
#endif
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean gtk_frame_urgency_timer_callback( wxTopLevelWindowGTK *win )
 | 
						|
{
 | 
						|
    gtk_window_set_urgency_hint(GTK_WINDOW(win->m_widget), false);
 | 
						|
 | 
						|
    win->m_urgency_hint = -2;
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "focus_in_event"
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean gtk_frame_focus_in_callback( GtkWidget *widget,
 | 
						|
                                         GdkEvent *WXUNUSED(event),
 | 
						|
                                         wxTopLevelWindowGTK *win )
 | 
						|
{
 | 
						|
    g_activeFrame = win;
 | 
						|
 | 
						|
    // MR: wxRequestUserAttention related block
 | 
						|
    switch( win->m_urgency_hint )
 | 
						|
    {
 | 
						|
        default:
 | 
						|
            g_source_remove( win->m_urgency_hint );
 | 
						|
            // no break, fallthrough to remove hint too
 | 
						|
            wxFALLTHROUGH;
 | 
						|
        case -1:
 | 
						|
            gtk_window_set_urgency_hint(GTK_WINDOW(widget), false);
 | 
						|
            win->m_urgency_hint = -2;
 | 
						|
            break;
 | 
						|
 | 
						|
        case -2: break;
 | 
						|
    }
 | 
						|
 | 
						|
    wxActivateEvent event(wxEVT_ACTIVATE, true, g_activeFrame->GetId());
 | 
						|
    event.SetEventObject(g_activeFrame);
 | 
						|
    g_activeFrame->HandleWindowEvent(event);
 | 
						|
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "focus_out_event"
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static
 | 
						|
gboolean gtk_frame_focus_out_callback(GtkWidget * WXUNUSED(widget),
 | 
						|
                                      GdkEventFocus *WXUNUSED(gdk_event),
 | 
						|
                                      wxTopLevelWindowGTK * WXUNUSED(win))
 | 
						|
{
 | 
						|
    if (g_activeFrame)
 | 
						|
    {
 | 
						|
        wxActivateEvent event(wxEVT_ACTIVATE, false, g_activeFrame->GetId());
 | 
						|
        event.SetEventObject(g_activeFrame);
 | 
						|
        g_activeFrame->HandleWindowEvent(event);
 | 
						|
 | 
						|
        g_activeFrame = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// "key_press_event"
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean
 | 
						|
wxgtk_tlw_key_press_event(GtkWidget *widget, GdkEventKey *event)
 | 
						|
{
 | 
						|
    GtkWindow* const window = GTK_WINDOW(widget);
 | 
						|
 | 
						|
    // By default GTK+ checks for the menu accelerators in this (top level)
 | 
						|
    // window first and then propagates the event to the currently focused
 | 
						|
    // child from where it bubbles up the window parent chain. In wxWidgets,
 | 
						|
    // however, we want the child window to have the event first but still
 | 
						|
    // handle it as an accelerator if it's not processed there, so we need to
 | 
						|
    // customize this by reversing the order of the steps done in the standard
 | 
						|
    // GTK+ gtk_window_key_press_event() handler.
 | 
						|
 | 
						|
    if ( gtk_window_propagate_key_event(window, event) )
 | 
						|
        return true;
 | 
						|
 | 
						|
    if ( gtk_window_activate_key(window, event) )
 | 
						|
        return true;
 | 
						|
 | 
						|
    void* parent_class = g_type_class_peek_parent(G_OBJECT_GET_CLASS(widget));
 | 
						|
    GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
 | 
						|
 | 
						|
    // Avoid calling the default handler, we have already done everything it does
 | 
						|
    return true;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "size_allocate" from m_wxwindow
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static void
 | 
						|
size_allocate(GtkWidget*, GtkAllocation* alloc, wxTopLevelWindowGTK* win)
 | 
						|
{
 | 
						|
    win->m_useCachedClientSize = true;
 | 
						|
    if (win->m_clientWidth  != alloc->width ||
 | 
						|
        win->m_clientHeight != alloc->height)
 | 
						|
    {
 | 
						|
        win->m_clientWidth  = alloc->width;
 | 
						|
        win->m_clientHeight = alloc->height;
 | 
						|
 | 
						|
        GtkAllocation a;
 | 
						|
        gtk_widget_get_allocation(win->m_widget, &a);
 | 
						|
        wxSize size(a.width, a.height);
 | 
						|
        if (HasClientDecor(win->m_widget))
 | 
						|
        {
 | 
						|
            GtkAllocation a2;
 | 
						|
            gtk_widget_get_allocation(win->m_mainWidget, &a2);
 | 
						|
            wxTopLevelWindowGTK::DecorSize decorSize;
 | 
						|
            decorSize.left = a2.x;
 | 
						|
            decorSize.right = a.width - a2.width - a2.x;
 | 
						|
            decorSize.top = a2.y;
 | 
						|
            decorSize.bottom = a.height - a2.height - a2.y;
 | 
						|
            win->GTKUpdateDecorSize(decorSize);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            size.x += win->m_decorSize.left + win->m_decorSize.right;
 | 
						|
            size.y += win->m_decorSize.top + win->m_decorSize.bottom;
 | 
						|
        }
 | 
						|
        win->m_width  = size.x;
 | 
						|
        win->m_height = size.y;
 | 
						|
 | 
						|
        if (!win->IsIconized())
 | 
						|
        {
 | 
						|
            wxSizeEvent event(size, win->GetId());
 | 
						|
            event.SetEventObject(win);
 | 
						|
            win->HandleWindowEvent(event);
 | 
						|
        }
 | 
						|
        // else the window is currently unmapped, don't generate size events
 | 
						|
    }
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "delete_event"
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean
 | 
						|
gtk_frame_delete_callback( GtkWidget *WXUNUSED(widget),
 | 
						|
                           GdkEvent *WXUNUSED(event),
 | 
						|
                           wxTopLevelWindowGTK *win )
 | 
						|
{
 | 
						|
    if (win->IsEnabled() &&
 | 
						|
        (wxOpenModalDialogsCount == 0 || (win->GetExtraStyle() & wxTOPLEVEL_EX_DIALOG) ||
 | 
						|
         win->IsGrabbed()))
 | 
						|
        win->Close();
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "configure_event"
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean
 | 
						|
gtk_frame_configure_callback( GtkWidget*,
 | 
						|
                              GdkEventConfigure* gdk_event,
 | 
						|
                              wxTopLevelWindowGTK *win )
 | 
						|
{
 | 
						|
    win->GTKConfigureEvent(gdk_event->x, gdk_event->y);
 | 
						|
    return false;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::GTKConfigureEvent(int x, int y)
 | 
						|
{
 | 
						|
    wxPoint point;
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
    if (gs_decorCacheValid)
 | 
						|
    {
 | 
						|
        const DecorSize& decorSize = GetCachedDecorSize();
 | 
						|
        point.x = x - decorSize.left;
 | 
						|
        point.y = y - decorSize.top;
 | 
						|
    }
 | 
						|
    else
 | 
						|
#endif
 | 
						|
    {
 | 
						|
        gtk_window_get_position(GTK_WINDOW(m_widget), &point.x, &point.y);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_x != point.x || m_y != point.y)
 | 
						|
    {
 | 
						|
        m_lastPos = wxPoint(m_x, m_y);
 | 
						|
 | 
						|
        m_x = point.x;
 | 
						|
        m_y = point.y;
 | 
						|
        wxMoveEvent event(point, GetId());
 | 
						|
        event.SetEventObject(this);
 | 
						|
        HandleWindowEvent(event);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "realize" from m_widget
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
// we cannot the WM hints and icons before the widget has been realized,
 | 
						|
// so we do this directly after realization
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::GTKHandleRealized()
 | 
						|
{
 | 
						|
    wxNonOwnedWindow::GTKHandleRealized();
 | 
						|
 | 
						|
    GdkWindow* window = gtk_widget_get_window(m_widget);
 | 
						|
 | 
						|
    gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
 | 
						|
    gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
 | 
						|
 | 
						|
    const wxIconBundle& icons = GetIcons();
 | 
						|
    if (icons.GetIconCount())
 | 
						|
        SetIcons(icons);
 | 
						|
 | 
						|
    GdkCursor* cursor = g_globalCursor.GetCursor();
 | 
						|
    if (wxIsBusy() && !gtk_window_get_modal(GTK_WINDOW(m_widget)))
 | 
						|
        cursor = g_busyCursor.GetCursor();
 | 
						|
 | 
						|
    if (cursor)
 | 
						|
        gdk_window_set_cursor(window, cursor);
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
    wxGCC_WARNING_SUPPRESS(deprecated-declarations)
 | 
						|
    if (gtk_window_get_has_resize_grip(GTK_WINDOW(m_widget)))
 | 
						|
    {
 | 
						|
        // Grip window can end up obscured, probably due to deferred show.
 | 
						|
        // Reset grip to ensure it is visible.
 | 
						|
        gtk_window_set_has_resize_grip(GTK_WINDOW(m_widget), false);
 | 
						|
        gtk_window_set_has_resize_grip(GTK_WINDOW(m_widget), true);
 | 
						|
    }
 | 
						|
    wxGCC_WARNING_RESTORE()
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "map_event" from m_widget
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean
 | 
						|
gtk_frame_map_callback( GtkWidget*,
 | 
						|
                        GdkEvent * WXUNUSED(event),
 | 
						|
                        wxTopLevelWindow *win )
 | 
						|
{
 | 
						|
    const bool wasIconized = win->IsIconized();
 | 
						|
    if (wasIconized)
 | 
						|
    {
 | 
						|
        // Because GetClientSize() returns (0,0) when IsIconized() is true,
 | 
						|
        // a size event must be generated, just in case GetClientSize() was
 | 
						|
        // called while iconized. This specifically happens when restoring a
 | 
						|
        // tlw that was "rolled up" with some WMs.
 | 
						|
        // Queue a resize rather than sending size event directly to allow
 | 
						|
        // children to be made visible first.
 | 
						|
        win->m_useCachedClientSize = false;
 | 
						|
        win->m_clientWidth = 0;
 | 
						|
        gtk_widget_queue_resize(win->m_wxwindow);
 | 
						|
    }
 | 
						|
    // it is possible for m_isShown to be false here, see bug #9909
 | 
						|
    if (win->wxWindowBase::Show(true))
 | 
						|
    {
 | 
						|
        wxShowEvent eventShow(win->GetId(), true);
 | 
						|
        eventShow.SetEventObject(win);
 | 
						|
        win->GetEventHandler()->ProcessEvent(eventShow);
 | 
						|
    }
 | 
						|
 | 
						|
    // restore focus-on-map setting in case ShowWithoutActivating() was called
 | 
						|
    gtk_window_set_focus_on_map(GTK_WINDOW(win->m_widget), true);
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "window-state-event" from m_widget
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean
 | 
						|
gtk_frame_window_state_callback( GtkWidget* WXUNUSED(widget),
 | 
						|
                          GdkEventWindowState *event,
 | 
						|
                          wxTopLevelWindow *win )
 | 
						|
{
 | 
						|
    if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)
 | 
						|
        win->SetIconizeState((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
 | 
						|
 | 
						|
    // if maximized bit changed and it is now set
 | 
						|
    if (event->changed_mask & event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
 | 
						|
    {
 | 
						|
        wxMaximizeEvent evt(win->GetId());
 | 
						|
        evt.SetEventObject(win);
 | 
						|
        win->HandleWindowEvent(evt);
 | 
						|
    }
 | 
						|
 | 
						|
    if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
 | 
						|
        win->m_fsIsShowing = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "notify::gtk-theme-name" from GtkSettings
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static void notify_gtk_theme_name(GObject*, GParamSpec*, wxTopLevelWindowGTK* win)
 | 
						|
{
 | 
						|
    wxSysColourChangedEvent event;
 | 
						|
    event.SetEventObject(win);
 | 
						|
    win->HandleWindowEvent(event);
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
bool wxGetFrameExtents(GdkWindow* window, int* left, int* right, int* top, int* bottom)
 | 
						|
{
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
    GdkDisplay* display = gdk_window_get_display(window);
 | 
						|
 | 
						|
    if (!GDK_IS_X11_DISPLAY(display))
 | 
						|
        return false;
 | 
						|
 | 
						|
    static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
 | 
						|
    Atom xproperty = gdk_x11_atom_to_xatom_for_display(display, property);
 | 
						|
    Atom type;
 | 
						|
    int format;
 | 
						|
    gulong nitems, bytes_after;
 | 
						|
    guchar* data = NULL;
 | 
						|
    Status status = XGetWindowProperty(
 | 
						|
        GDK_DISPLAY_XDISPLAY(display),
 | 
						|
        GDK_WINDOW_XID(window),
 | 
						|
        xproperty,
 | 
						|
        0, 4, false, XA_CARDINAL,
 | 
						|
        &type, &format, &nitems, &bytes_after, &data);
 | 
						|
    const bool success = status == Success && data && nitems == 4;
 | 
						|
    if (success)
 | 
						|
    {
 | 
						|
        // We need to convert the X11 physical extents to GTK+ "logical" units
 | 
						|
        int scale = 1;
 | 
						|
#if GTK_CHECK_VERSION(3,10,0)
 | 
						|
        if (wx_is_at_least_gtk3(10))
 | 
						|
            scale = gdk_window_get_scale_factor(window);
 | 
						|
#endif
 | 
						|
        long* p = (long*)data;
 | 
						|
        if (left)   *left   = int(p[0]) / scale;
 | 
						|
        if (right)  *right  = int(p[1]) / scale;
 | 
						|
        if (top)    *top    = int(p[2]) / scale;
 | 
						|
        if (bottom) *bottom = int(p[3]) / scale;
 | 
						|
    }
 | 
						|
    if (data)
 | 
						|
        XFree(data);
 | 
						|
    return success;
 | 
						|
#else
 | 
						|
    return false;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// "property_notify_event" from m_widget
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean property_notify_event(
 | 
						|
    GtkWidget*, GdkEventProperty* event, wxTopLevelWindowGTK* win)
 | 
						|
{
 | 
						|
    // Watch for changes to _NET_FRAME_EXTENTS property
 | 
						|
    static GdkAtom property = gdk_atom_intern("_NET_FRAME_EXTENTS", false);
 | 
						|
    if (event->state == GDK_PROPERTY_NEW_VALUE && event->atom == property)
 | 
						|
    {
 | 
						|
        if (win->m_netFrameExtentsTimerId)
 | 
						|
        {
 | 
						|
            // WM support for _NET_REQUEST_FRAME_EXTENTS is working
 | 
						|
            gs_requestFrameExtentsStatus = RFE_STATUS_WORKING;
 | 
						|
            g_source_remove(win->m_netFrameExtentsTimerId);
 | 
						|
            win->m_netFrameExtentsTimerId = 0;
 | 
						|
        }
 | 
						|
 | 
						|
        wxTopLevelWindowGTK::DecorSize decorSize = win->m_decorSize;
 | 
						|
        gs_decorCacheValid = wxGetFrameExtents(event->window,
 | 
						|
            &decorSize.left, &decorSize.right, &decorSize.top, &decorSize.bottom);
 | 
						|
 | 
						|
        win->GTKUpdateDecorSize(decorSize);
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean request_frame_extents_timeout(void* data)
 | 
						|
{
 | 
						|
    // WM support for _NET_REQUEST_FRAME_EXTENTS is broken
 | 
						|
    gs_requestFrameExtentsStatus = RFE_STATUS_BROKEN;
 | 
						|
    gdk_threads_enter();
 | 
						|
    wxTopLevelWindowGTK* win = static_cast<wxTopLevelWindowGTK*>(data);
 | 
						|
    win->m_netFrameExtentsTimerId = 0;
 | 
						|
    wxTopLevelWindowGTK::DecorSize decorSize = win->m_decorSize;
 | 
						|
    wxGetFrameExtents(gtk_widget_get_window(win->m_widget),
 | 
						|
        &decorSize.left, &decorSize.right, &decorSize.top, &decorSize.bottom);
 | 
						|
    win->GTKUpdateDecorSize(decorSize);
 | 
						|
    gdk_threads_leave();
 | 
						|
    return false;
 | 
						|
}
 | 
						|
}
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// wxTopLevelWindowGTK creation
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::Init()
 | 
						|
{
 | 
						|
    m_mainWidget = NULL;
 | 
						|
    m_isIconized = false;
 | 
						|
    m_fsIsShowing = false;
 | 
						|
    m_themeEnabled = true;
 | 
						|
    m_gdkDecor =
 | 
						|
    m_gdkFunc = 0;
 | 
						|
    m_grabbedEventLoop = NULL;
 | 
						|
    m_deferShow = true;
 | 
						|
    m_deferShowAllowed = true;
 | 
						|
    m_updateDecorSize = true;
 | 
						|
    m_netFrameExtentsTimerId = 0;
 | 
						|
    m_incWidth = m_incHeight = 0;
 | 
						|
 | 
						|
    m_urgency_hint = -2;
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::Create( wxWindow *parent,
 | 
						|
                                  wxWindowID id,
 | 
						|
                                  const wxString& title,
 | 
						|
                                  const wxPoint& pos,
 | 
						|
                                  const wxSize& sizeOrig,
 | 
						|
                                  long style,
 | 
						|
                                  const wxString &name )
 | 
						|
{
 | 
						|
    wxSize size(sizeOrig);
 | 
						|
    if (!size.IsFullySpecified())
 | 
						|
        size.SetDefaults(GetDefaultSize());
 | 
						|
 | 
						|
    wxTopLevelWindows.Append( this );
 | 
						|
 | 
						|
    if (!PreCreation( parent, pos, size ) ||
 | 
						|
        !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
 | 
						|
    {
 | 
						|
        wxFAIL_MSG( wxT("wxTopLevelWindowGTK creation failed") );
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    m_title = title;
 | 
						|
 | 
						|
    // NB: m_widget may be !=NULL if it was created by derived class' Create,
 | 
						|
    //     e.g. in wxTaskBarIconAreaGTK
 | 
						|
    if (m_widget == NULL)
 | 
						|
    {
 | 
						|
        m_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 | 
						|
        if (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)
 | 
						|
        {
 | 
						|
            // Tell WM that this is a dialog window and make it center
 | 
						|
            // on parent by default (this is what GtkDialog ctor does):
 | 
						|
            gtk_window_set_type_hint(GTK_WINDOW(m_widget),
 | 
						|
                                     GDK_WINDOW_TYPE_HINT_DIALOG);
 | 
						|
            gtk_window_set_position(GTK_WINDOW(m_widget),
 | 
						|
                                    GTK_WIN_POS_CENTER_ON_PARENT);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if (style & wxFRAME_TOOL_WINDOW)
 | 
						|
            {
 | 
						|
                gtk_window_set_type_hint(GTK_WINDOW(m_widget),
 | 
						|
                                         GDK_WINDOW_TYPE_HINT_UTILITY);
 | 
						|
 | 
						|
                // On some WMs, like KDE, a TOOL_WINDOW will still show
 | 
						|
                // on the taskbar, but on Gnome a TOOL_WINDOW will not.
 | 
						|
                // For consistency between WMs and with Windows, we
 | 
						|
                // should set the NO_TASKBAR flag which will apply
 | 
						|
                // the set_skip_taskbar_hint if it is available,
 | 
						|
                // ensuring no taskbar entry will appear.
 | 
						|
                style |= wxFRAME_NO_TASKBAR;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        g_object_ref(m_widget);
 | 
						|
    }
 | 
						|
 | 
						|
    wxWindow *topParent = wxGetTopLevelParent(m_parent);
 | 
						|
    if (topParent && (((GTK_IS_WINDOW(topParent->m_widget)) &&
 | 
						|
                       (GetExtraStyle() & wxTOPLEVEL_EX_DIALOG)) ||
 | 
						|
                       (style & wxFRAME_FLOAT_ON_PARENT)))
 | 
						|
    {
 | 
						|
        gtk_window_set_transient_for( GTK_WINDOW(m_widget),
 | 
						|
                                      GTK_WINDOW(topParent->m_widget) );
 | 
						|
    }
 | 
						|
 | 
						|
    if (style & wxFRAME_NO_TASKBAR)
 | 
						|
    {
 | 
						|
        gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget), TRUE);
 | 
						|
    }
 | 
						|
 | 
						|
    if (style & wxSTAY_ON_TOP)
 | 
						|
    {
 | 
						|
        gtk_window_set_keep_above(GTK_WINDOW(m_widget), TRUE);
 | 
						|
    }
 | 
						|
    if (style & wxMAXIMIZE)
 | 
						|
        gtk_window_maximize(GTK_WINDOW(m_widget));
 | 
						|
 | 
						|
#if 0
 | 
						|
    if (!name.empty())
 | 
						|
        gtk_window_set_role( GTK_WINDOW(m_widget), wxGTK_CONV( name ) );
 | 
						|
#endif
 | 
						|
 | 
						|
    gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
 | 
						|
    gtk_widget_set_can_focus(m_widget, false);
 | 
						|
 | 
						|
    g_signal_connect (m_widget, "delete_event",
 | 
						|
                      G_CALLBACK (gtk_frame_delete_callback), this);
 | 
						|
 | 
						|
    // m_mainWidget is a GtkVBox, holding the bars and client area (m_wxwindow)
 | 
						|
    m_mainWidget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 | 
						|
    gtk_widget_show( m_mainWidget );
 | 
						|
    gtk_widget_set_can_focus(m_mainWidget, false);
 | 
						|
    gtk_container_add( GTK_CONTAINER(m_widget), m_mainWidget );
 | 
						|
 | 
						|
    // m_wxwindow is the client area
 | 
						|
    m_wxwindow = wxPizza::New();
 | 
						|
    gtk_widget_show( m_wxwindow );
 | 
						|
    gtk_box_pack_start(GTK_BOX(m_mainWidget), m_wxwindow, true, true, 0);
 | 
						|
 | 
						|
    // we donm't allow the frame to get the focus as otherwise
 | 
						|
    // the frame will grab it at arbitrary focus changes
 | 
						|
    gtk_widget_set_can_focus(m_wxwindow, false);
 | 
						|
 | 
						|
    if (m_parent) m_parent->AddChild( this );
 | 
						|
 | 
						|
    g_signal_connect(m_wxwindow, "size_allocate",
 | 
						|
        G_CALLBACK(size_allocate), this);
 | 
						|
 | 
						|
    PostCreation();
 | 
						|
 | 
						|
    if (pos.IsFullySpecified())
 | 
						|
    {
 | 
						|
#ifdef __WXGTK3__
 | 
						|
        GtkWindowPosition windowPos;
 | 
						|
        g_object_get(m_widget, "window-position", &windowPos, NULL);
 | 
						|
        if (windowPos == GTK_WIN_POS_NONE)
 | 
						|
            gtk_window_move(GTK_WINDOW(m_widget), m_x, m_y);
 | 
						|
#else
 | 
						|
        gtk_widget_set_uposition( m_widget, m_x, m_y );
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    // for some reported size corrections
 | 
						|
    g_signal_connect (m_widget, "map_event",
 | 
						|
                      G_CALLBACK (gtk_frame_map_callback), this);
 | 
						|
 | 
						|
    // for iconized state
 | 
						|
    g_signal_connect (m_widget, "window_state_event",
 | 
						|
                      G_CALLBACK (gtk_frame_window_state_callback), this);
 | 
						|
 | 
						|
 | 
						|
    // for wxMoveEvent
 | 
						|
    g_signal_connect (m_widget, "configure_event",
 | 
						|
                      G_CALLBACK (gtk_frame_configure_callback), this);
 | 
						|
 | 
						|
    // activation
 | 
						|
    g_signal_connect_after (m_widget, "focus_in_event",
 | 
						|
                      G_CALLBACK (gtk_frame_focus_in_callback), this);
 | 
						|
    g_signal_connect_after (m_widget, "focus_out_event",
 | 
						|
                      G_CALLBACK (gtk_frame_focus_out_callback), this);
 | 
						|
 | 
						|
    // We need to customize the default GTK+ logic for key processing to make
 | 
						|
    // it conforming to wxWidgets event processing order.
 | 
						|
    g_signal_connect (m_widget, "key_press_event",
 | 
						|
                      G_CALLBACK (wxgtk_tlw_key_press_event), NULL);
 | 
						|
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
#ifdef __WXGTK3__
 | 
						|
    if (GDK_IS_X11_SCREEN(gtk_window_get_screen(GTK_WINDOW(m_widget))))
 | 
						|
#endif
 | 
						|
    {
 | 
						|
        gtk_widget_add_events(m_widget, GDK_PROPERTY_CHANGE_MASK);
 | 
						|
        g_signal_connect(m_widget, "property_notify_event",
 | 
						|
            G_CALLBACK(property_notify_event), this);
 | 
						|
    }
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
 | 
						|
    // translate wx decorations styles into Motif WM hints (they are recognized
 | 
						|
    // by other WMs as well)
 | 
						|
 | 
						|
    // always enable moving the window as we have no separate flag for enabling
 | 
						|
    // it
 | 
						|
    m_gdkFunc = GDK_FUNC_MOVE;
 | 
						|
 | 
						|
    if ( style & wxCLOSE_BOX )
 | 
						|
        m_gdkFunc |= GDK_FUNC_CLOSE;
 | 
						|
 | 
						|
    if ( style & wxMINIMIZE_BOX )
 | 
						|
        m_gdkFunc |= GDK_FUNC_MINIMIZE;
 | 
						|
 | 
						|
    if ( style & wxMAXIMIZE_BOX )
 | 
						|
        m_gdkFunc |= GDK_FUNC_MAXIMIZE;
 | 
						|
 | 
						|
    if ( (style & wxSIMPLE_BORDER) || (style & wxNO_BORDER) )
 | 
						|
    {
 | 
						|
        m_gdkDecor = 0;
 | 
						|
        gtk_window_set_decorated(GTK_WINDOW(m_widget), false);
 | 
						|
    }
 | 
						|
    else // have border
 | 
						|
    {
 | 
						|
        m_gdkDecor = GDK_DECOR_BORDER;
 | 
						|
 | 
						|
        if ( style & wxCAPTION )
 | 
						|
            m_gdkDecor |= GDK_DECOR_TITLE;
 | 
						|
#if GTK_CHECK_VERSION(3,10,0)
 | 
						|
        else if (
 | 
						|
            strcmp("GdkWaylandDisplay", g_type_name(G_TYPE_FROM_INSTANCE(gtk_widget_get_display(m_widget)))) == 0 &&
 | 
						|
            gtk_check_version(3,10,0) == NULL)
 | 
						|
        {
 | 
						|
            gtk_window_set_titlebar(GTK_WINDOW(m_widget), gtk_header_bar_new());
 | 
						|
        }
 | 
						|
#endif
 | 
						|
 | 
						|
        if ( style & wxSYSTEM_MENU )
 | 
						|
            m_gdkDecor |= GDK_DECOR_MENU;
 | 
						|
 | 
						|
        if ( style & wxMINIMIZE_BOX )
 | 
						|
            m_gdkDecor |= GDK_DECOR_MINIMIZE;
 | 
						|
 | 
						|
        if ( style & wxMAXIMIZE_BOX )
 | 
						|
            m_gdkDecor |= GDK_DECOR_MAXIMIZE;
 | 
						|
 | 
						|
        if ( style & wxRESIZE_BORDER )
 | 
						|
        {
 | 
						|
           m_gdkFunc |= GDK_FUNC_RESIZE;
 | 
						|
           m_gdkDecor |= GDK_DECOR_RESIZEH;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    m_decorSize = GetCachedDecorSize();
 | 
						|
    int w, h;
 | 
						|
    GTKDoGetSize(&w, &h);
 | 
						|
 | 
						|
    if (style & wxRESIZE_BORDER)
 | 
						|
    {
 | 
						|
        gtk_window_set_default_size(GTK_WINDOW(m_widget), w, h);
 | 
						|
#ifndef __WXGTK3__
 | 
						|
        gtk_window_set_policy(GTK_WINDOW(m_widget), 1, 1, 1);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        gtk_window_set_resizable(GTK_WINDOW(m_widget), false);
 | 
						|
        // gtk_window_set_default_size() does not work for un-resizable windows,
 | 
						|
        // unless you set the size hints, but that causes Ubuntu's WM to make
 | 
						|
        // the window resizable even though GDK_FUNC_RESIZE is not set.
 | 
						|
        gtk_widget_set_size_request(m_widget, w, h);
 | 
						|
    }
 | 
						|
 | 
						|
    g_signal_connect(gtk_settings_get_default(), "notify::gtk-theme-name",
 | 
						|
        G_CALLBACK(notify_gtk_theme_name), this);
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
wxTopLevelWindowGTK::~wxTopLevelWindowGTK()
 | 
						|
{
 | 
						|
    if ( m_netFrameExtentsTimerId )
 | 
						|
    {
 | 
						|
        // Don't let the timer callback fire as the window pointer passed to it
 | 
						|
        // will become invalid very soon.
 | 
						|
        g_source_remove(m_netFrameExtentsTimerId);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_grabbedEventLoop)
 | 
						|
    {
 | 
						|
        wxFAIL_MSG(wxT("Window still grabbed"));
 | 
						|
        RemoveGrab();
 | 
						|
    }
 | 
						|
 | 
						|
    SendDestroyEvent();
 | 
						|
 | 
						|
    // it may also be GtkScrolledWindow in the case of an MDI child
 | 
						|
    if (GTK_IS_WINDOW(m_widget))
 | 
						|
    {
 | 
						|
        gtk_window_set_focus( GTK_WINDOW(m_widget), NULL );
 | 
						|
    }
 | 
						|
 | 
						|
    if (g_activeFrame == this)
 | 
						|
        g_activeFrame = NULL;
 | 
						|
 | 
						|
    g_signal_handlers_disconnect_by_func(
 | 
						|
        gtk_settings_get_default(), (void*)notify_gtk_theme_name, this);
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::EnableCloseButton( bool enable )
 | 
						|
{
 | 
						|
    if (enable)
 | 
						|
        m_gdkFunc |= GDK_FUNC_CLOSE;
 | 
						|
    else
 | 
						|
        m_gdkFunc &= ~GDK_FUNC_CLOSE;
 | 
						|
 | 
						|
    GdkWindow* window = gtk_widget_get_window(m_widget);
 | 
						|
    if (window)
 | 
						|
        gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::ShowFullScreen(bool show, long)
 | 
						|
{
 | 
						|
    if (show == m_fsIsShowing)
 | 
						|
        return false; // return what?
 | 
						|
 | 
						|
    m_fsIsShowing = show;
 | 
						|
 | 
						|
#if defined(GDK_WINDOWING_X11) && !defined(__WXGTK4__)
 | 
						|
    GdkScreen* screen = gtk_widget_get_screen(m_widget);
 | 
						|
    GdkDisplay* display = gdk_screen_get_display(screen);
 | 
						|
    Display* xdpy = NULL;
 | 
						|
    Window xroot = None;
 | 
						|
    wxX11FullScreenMethod method = wxX11_FS_WMSPEC;
 | 
						|
 | 
						|
    if (GDK_IS_X11_DISPLAY(display))
 | 
						|
    {
 | 
						|
        xdpy = GDK_DISPLAY_XDISPLAY(display);
 | 
						|
        xroot = GDK_WINDOW_XID(gdk_screen_get_root_window(screen));
 | 
						|
        method = wxGetFullScreenMethodX11(xdpy, (WXWindow)xroot);
 | 
						|
    }
 | 
						|
 | 
						|
    // NB: gtk_window_fullscreen() uses freedesktop.org's WMspec extensions
 | 
						|
    //     to switch to fullscreen, which is not always available. We must
 | 
						|
    //     check if WM supports the spec and use legacy methods if it
 | 
						|
    //     doesn't.
 | 
						|
    if ( method == wxX11_FS_WMSPEC )
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
    {
 | 
						|
        if (show)
 | 
						|
            gtk_window_fullscreen( GTK_WINDOW( m_widget ) );
 | 
						|
        else
 | 
						|
            gtk_window_unfullscreen( GTK_WINDOW( m_widget ) );
 | 
						|
    }
 | 
						|
#if defined(GDK_WINDOWING_X11) && !defined(__WXGTK4__)
 | 
						|
    else if (xdpy != NULL)
 | 
						|
    {
 | 
						|
        GdkWindow* window = gtk_widget_get_window(m_widget);
 | 
						|
        Window xid = GDK_WINDOW_XID(window);
 | 
						|
 | 
						|
        if (show)
 | 
						|
        {
 | 
						|
            GetPosition( &m_fsSaveFrame.x, &m_fsSaveFrame.y );
 | 
						|
            GetSize( &m_fsSaveFrame.width, &m_fsSaveFrame.height );
 | 
						|
 | 
						|
            wxGCC_WARNING_SUPPRESS(deprecated-declarations)
 | 
						|
            const int screen_width = gdk_screen_get_width(screen);
 | 
						|
            const int screen_height = gdk_screen_get_height(screen);
 | 
						|
            wxGCC_WARNING_RESTORE()
 | 
						|
 | 
						|
            gint client_x, client_y, root_x, root_y;
 | 
						|
            gint width, height;
 | 
						|
 | 
						|
            m_fsSaveGdkFunc = m_gdkFunc;
 | 
						|
            m_fsSaveGdkDecor = m_gdkDecor;
 | 
						|
            m_gdkFunc = m_gdkDecor = 0;
 | 
						|
            gdk_window_set_decorations(window, (GdkWMDecoration)0);
 | 
						|
            gdk_window_set_functions(window, (GdkWMFunction)0);
 | 
						|
 | 
						|
            gdk_window_get_origin(window, &root_x, &root_y);
 | 
						|
            gdk_window_get_geometry(window, &client_x, &client_y, &width, &height);
 | 
						|
 | 
						|
            gdk_window_move_resize(
 | 
						|
                window, -client_x, -client_y, screen_width + 1, screen_height + 1);
 | 
						|
 | 
						|
            wxSetFullScreenStateX11(xdpy,
 | 
						|
                                    (WXWindow)xroot,
 | 
						|
                                    (WXWindow)xid,
 | 
						|
                                    show, &m_fsSaveFrame, method);
 | 
						|
        }
 | 
						|
        else // hide
 | 
						|
        {
 | 
						|
            m_gdkFunc = m_fsSaveGdkFunc;
 | 
						|
            m_gdkDecor = m_fsSaveGdkDecor;
 | 
						|
            gdk_window_set_decorations(window, (GdkWMDecoration)m_gdkDecor);
 | 
						|
            gdk_window_set_functions(window, (GdkWMFunction)m_gdkFunc);
 | 
						|
 | 
						|
            wxSetFullScreenStateX11(xdpy,
 | 
						|
                                    (WXWindow)xroot,
 | 
						|
                                    (WXWindow)xid,
 | 
						|
                                    show, &m_fsSaveFrame, method);
 | 
						|
 | 
						|
            SetSize(m_fsSaveFrame.x, m_fsSaveFrame.y,
 | 
						|
                    m_fsSaveFrame.width, m_fsSaveFrame.height);
 | 
						|
        }
 | 
						|
    }
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
 | 
						|
    // documented behaviour is to show the window if it's still hidden when
 | 
						|
    // showing it full screen
 | 
						|
    if (show)
 | 
						|
        Show();
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// overridden wxWindow methods
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::Refresh( bool WXUNUSED(eraseBackground), const wxRect *WXUNUSED(rect) )
 | 
						|
{
 | 
						|
    wxCHECK_RET( m_widget, wxT("invalid frame") );
 | 
						|
 | 
						|
    gtk_widget_queue_draw( m_widget );
 | 
						|
 | 
						|
    GdkWindow* window = NULL;
 | 
						|
    if (m_wxwindow)
 | 
						|
        window = gtk_widget_get_window(m_wxwindow);
 | 
						|
    if (window)
 | 
						|
        gdk_window_invalidate_rect(window, NULL, true);
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::Show( bool show )
 | 
						|
{
 | 
						|
    wxCHECK_MSG(m_widget, false, "invalid frame");
 | 
						|
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
    bool deferShow = show && !m_isShown && !m_isIconized && m_deferShow;
 | 
						|
    if (deferShow)
 | 
						|
    {
 | 
						|
        deferShow = m_deferShowAllowed &&
 | 
						|
            // Assume size (from cache or wxPersistentTLW) is correct.
 | 
						|
            // Avoids problems when WM initially provides an incorrect value
 | 
						|
            // for extents, then corrects it later.
 | 
						|
            m_decorSize.top == 0 &&
 | 
						|
 | 
						|
            gs_requestFrameExtentsStatus != RFE_STATUS_BROKEN &&
 | 
						|
            !gtk_widget_get_realized(m_widget) &&
 | 
						|
            GDK_IS_X11_DISPLAY(gtk_widget_get_display(m_widget)) &&
 | 
						|
            g_signal_handler_find(m_widget,
 | 
						|
                GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DATA),
 | 
						|
                g_signal_lookup("property_notify_event", GTK_TYPE_WIDGET),
 | 
						|
                0, NULL, NULL, this);
 | 
						|
        if (deferShow)
 | 
						|
        {
 | 
						|
            GdkScreen* screen = gtk_widget_get_screen(m_widget);
 | 
						|
            GdkAtom atom = gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false);
 | 
						|
            deferShow = gdk_x11_screen_supports_net_wm_hint(screen, atom) != 0;
 | 
						|
 | 
						|
            // If _NET_REQUEST_FRAME_EXTENTS not supported, don't allow changes
 | 
						|
            // to m_decorSize, it breaks saving/restoring window size with
 | 
						|
            // GetSize()/SetSize() because it makes window bigger between each
 | 
						|
            // restore and save.
 | 
						|
            m_updateDecorSize = deferShow;
 | 
						|
        }
 | 
						|
 | 
						|
        m_deferShow = deferShow;
 | 
						|
    }
 | 
						|
    if (deferShow)
 | 
						|
    {
 | 
						|
        // Initial show. If WM supports _NET_REQUEST_FRAME_EXTENTS, defer
 | 
						|
        // calling gtk_widget_show() until _NET_FRAME_EXTENTS property
 | 
						|
        // notification is received, so correct frame extents are known.
 | 
						|
        // This allows resizing m_widget to keep the overall size in sync with
 | 
						|
        // what wxWidgets expects it to be without an obvious change in the
 | 
						|
        // window size immediately after it becomes visible.
 | 
						|
 | 
						|
        // Realize m_widget, so m_widget->window can be used. Realizing normally
 | 
						|
        // causes the widget tree to be size_allocated, which generates size
 | 
						|
        // events in the wrong order. However, the size_allocates will not be
 | 
						|
        // done if the allocation is not the default (1,1).
 | 
						|
        GtkAllocation alloc;
 | 
						|
        gtk_widget_get_allocation(m_widget, &alloc);
 | 
						|
        const int alloc_width = alloc.width;
 | 
						|
        if (alloc_width == 1)
 | 
						|
        {
 | 
						|
            alloc.width = 2;
 | 
						|
            gtk_widget_set_allocation(m_widget, &alloc);
 | 
						|
        }
 | 
						|
        gtk_widget_realize(m_widget);
 | 
						|
        if (alloc_width == 1)
 | 
						|
        {
 | 
						|
            alloc.width = 1;
 | 
						|
            gtk_widget_set_allocation(m_widget, &alloc);
 | 
						|
        }
 | 
						|
 | 
						|
        // send _NET_REQUEST_FRAME_EXTENTS
 | 
						|
        XClientMessageEvent xevent;
 | 
						|
        memset(&xevent, 0, sizeof(xevent));
 | 
						|
        xevent.type = ClientMessage;
 | 
						|
        GdkWindow* window = gtk_widget_get_window(m_widget);
 | 
						|
        xevent.window = GDK_WINDOW_XID(window);
 | 
						|
        xevent.message_type = gdk_x11_atom_to_xatom_for_display(
 | 
						|
            gdk_window_get_display(window),
 | 
						|
            gdk_atom_intern("_NET_REQUEST_FRAME_EXTENTS", false));
 | 
						|
        xevent.format = 32;
 | 
						|
        Display* display = GDK_DISPLAY_XDISPLAY(gdk_window_get_display(window));
 | 
						|
        XSendEvent(display, DefaultRootWindow(display), false,
 | 
						|
            SubstructureNotifyMask | SubstructureRedirectMask,
 | 
						|
            (XEvent*)&xevent);
 | 
						|
 | 
						|
        if (gs_requestFrameExtentsStatus == RFE_STATUS_UNKNOWN)
 | 
						|
        {
 | 
						|
            // if WM does not respond to request within 1 second,
 | 
						|
            // we assume support for _NET_REQUEST_FRAME_EXTENTS is not working
 | 
						|
            m_netFrameExtentsTimerId =
 | 
						|
                g_timeout_add(1000, request_frame_extents_timeout, this);
 | 
						|
        }
 | 
						|
 | 
						|
        // defer calling gtk_widget_show()
 | 
						|
        m_isShown = true;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
 | 
						|
    if (show && !gtk_widget_get_realized(m_widget))
 | 
						|
    {
 | 
						|
        // size_allocate signals occur in reverse order (bottom to top).
 | 
						|
        // Things work better if the initial wxSizeEvents are sent (from the
 | 
						|
        // top down), before the initial size_allocate signals occur.
 | 
						|
        SendSizeEvent();
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
        GTKSizeRevalidate();
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    bool change = base_type::Show(show);
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
    if (m_needSizeEvent)
 | 
						|
    {
 | 
						|
        m_needSizeEvent = false;
 | 
						|
        SendSizeEvent();
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    if (change && !show)
 | 
						|
    {
 | 
						|
        // make sure window has a non-default position, so when it is shown
 | 
						|
        // again, it won't be repositioned by WM as if it were a new window
 | 
						|
        // Note that this must be done _after_ the window is hidden.
 | 
						|
        gtk_window_move((GtkWindow*)m_widget, m_x, m_y);
 | 
						|
    }
 | 
						|
 | 
						|
    return change;
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::ShowWithoutActivating()
 | 
						|
{
 | 
						|
    if (!m_isShown)
 | 
						|
    {
 | 
						|
        gtk_window_set_focus_on_map(GTK_WINDOW(m_widget), false);
 | 
						|
        Show(true);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::Raise()
 | 
						|
{
 | 
						|
    gtk_window_present( GTK_WINDOW( m_widget ) );
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::DoMoveWindow(int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) )
 | 
						|
{
 | 
						|
    wxFAIL_MSG( wxT("DoMoveWindow called for wxTopLevelWindowGTK") );
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// window geometry
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::GTKDoGetSize(int *width, int *height) const
 | 
						|
{
 | 
						|
    wxSize size(m_width, m_height);
 | 
						|
    if (!HasClientDecor(m_widget))
 | 
						|
    {
 | 
						|
        size.x -= m_decorSize.left + m_decorSize.right;
 | 
						|
        size.y -= m_decorSize.top + m_decorSize.bottom;
 | 
						|
    }
 | 
						|
    if (size.x < 0) size.x = 0;
 | 
						|
    if (size.y < 0) size.y = 0;
 | 
						|
    if (width)  *width  = size.x;
 | 
						|
    if (height) *height = size.y;
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
 | 
						|
{
 | 
						|
    wxCHECK_RET( m_widget, wxT("invalid frame") );
 | 
						|
 | 
						|
    // deal with the position first
 | 
						|
    int old_x = m_x;
 | 
						|
    int old_y = m_y;
 | 
						|
 | 
						|
    if ( !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE) )
 | 
						|
    {
 | 
						|
        // -1 means "use existing" unless the flag above is specified
 | 
						|
        if ( x != -1 )
 | 
						|
            m_x = x;
 | 
						|
        if ( y != -1 )
 | 
						|
            m_y = y;
 | 
						|
    }
 | 
						|
    else // wxSIZE_ALLOW_MINUS_ONE
 | 
						|
    {
 | 
						|
        m_x = x;
 | 
						|
        m_y = y;
 | 
						|
    }
 | 
						|
 | 
						|
    const wxSize oldSize(m_width, m_height);
 | 
						|
    if (width >= 0)
 | 
						|
        m_width = width;
 | 
						|
    if (height >= 0)
 | 
						|
        m_height = height;
 | 
						|
    ConstrainSize();
 | 
						|
    if (m_width < 1) m_width = 1;
 | 
						|
    if (m_height < 1) m_height = 1;
 | 
						|
 | 
						|
    if ( m_x != old_x || m_y != old_y )
 | 
						|
    {
 | 
						|
        gtk_window_move( GTK_WINDOW(m_widget), m_x, m_y );
 | 
						|
        wxMoveEvent event(wxPoint(m_x, m_y), GetId());
 | 
						|
        event.SetEventObject(this);
 | 
						|
        HandleWindowEvent(event);
 | 
						|
    }
 | 
						|
 | 
						|
    if (m_width != oldSize.x || m_height != oldSize.y)
 | 
						|
    {
 | 
						|
        m_deferShowAllowed = true;
 | 
						|
        m_useCachedClientSize = false;
 | 
						|
 | 
						|
        int w, h;
 | 
						|
        GTKDoGetSize(&w, &h);
 | 
						|
        gtk_window_resize(GTK_WINDOW(m_widget), w, h);
 | 
						|
        if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
 | 
						|
            gtk_widget_set_size_request(GTK_WIDGET(m_widget), w, h);
 | 
						|
 | 
						|
        DoGetClientSize(&m_clientWidth, &m_clientHeight);
 | 
						|
        wxSizeEvent event(GetSize(), GetId());
 | 
						|
        event.SetEventObject(this);
 | 
						|
        HandleWindowEvent(event);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static gboolean reset_size_request(void* data)
 | 
						|
{
 | 
						|
    gtk_widget_set_size_request(GTK_WIDGET(data), -1, -1);
 | 
						|
    g_object_unref(data);
 | 
						|
    return false;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::DoSetClientSize(int width, int height)
 | 
						|
{
 | 
						|
    base_type::DoSetClientSize(width, height);
 | 
						|
 | 
						|
    // Since client size is being explicitly set, don't change it later
 | 
						|
    // Has to be done after calling base because it calls SetSize,
 | 
						|
    // which sets this true
 | 
						|
    m_deferShowAllowed = false;
 | 
						|
 | 
						|
    if (m_wxwindow)
 | 
						|
    {
 | 
						|
        // If window is not resizable or not yet shown, set size request on
 | 
						|
        // client widget, to make it more likely window will get correct size
 | 
						|
        // even if our decorations size cache is incorrect (as it will be before
 | 
						|
        // showing first TLW).
 | 
						|
        if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
 | 
						|
        {
 | 
						|
            gtk_widget_set_size_request(m_widget, -1, -1);
 | 
						|
            gtk_widget_set_size_request(m_wxwindow, m_clientWidth, m_clientHeight);
 | 
						|
        }
 | 
						|
        else if (!IsShown())
 | 
						|
        {
 | 
						|
            gtk_widget_set_size_request(m_wxwindow, m_clientWidth, m_clientHeight);
 | 
						|
            // Cancel size request at next idle to allow resizing
 | 
						|
            g_idle_add_full(G_PRIORITY_LOW - 1, reset_size_request, m_wxwindow, NULL);
 | 
						|
            g_object_ref(m_wxwindow);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::DoGetClientSize( int *width, int *height ) const
 | 
						|
{
 | 
						|
    wxCHECK_RET(m_widget, "invalid frame");
 | 
						|
 | 
						|
    if ( IsIconized() )
 | 
						|
    {
 | 
						|
        // for consistency with wxMSW, client area is supposed to be empty for
 | 
						|
        // the iconized windows
 | 
						|
        if ( width )
 | 
						|
            *width = 0;
 | 
						|
        if ( height )
 | 
						|
            *height = 0;
 | 
						|
    }
 | 
						|
    else if (m_useCachedClientSize)
 | 
						|
        base_type::DoGetClientSize(width, height);
 | 
						|
    else
 | 
						|
    {
 | 
						|
        int w = m_width - (m_decorSize.left + m_decorSize.right);
 | 
						|
        int h = m_height - (m_decorSize.top + m_decorSize.bottom);
 | 
						|
        if (w < 0) w = 0;
 | 
						|
        if (h < 0) h = 0;
 | 
						|
        if (width) *width = w;
 | 
						|
        if (height) *height = h;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::DoSetSizeHints( int minW, int minH,
 | 
						|
                                          int maxW, int maxH,
 | 
						|
                                          int incW, int incH )
 | 
						|
{
 | 
						|
    base_type::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
 | 
						|
 | 
						|
    if (!HasFlag(wxRESIZE_BORDER))
 | 
						|
    {
 | 
						|
        // It's not useful to set size hints for the windows which can't be
 | 
						|
        // resized anyhow, and it even results in warnings from Gnome window
 | 
						|
        // manager, so just don't do it (but notice that it's not an error to
 | 
						|
        // call this for non-resizeable windows, e.g. because it's done
 | 
						|
        // implicitly by SetSizerAndFit() which is useful even in this case).
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    m_incWidth = incW;
 | 
						|
    m_incHeight = incH;
 | 
						|
 | 
						|
    const wxSize minSize = GetMinSize();
 | 
						|
    const wxSize maxSize = GetMaxSize();
 | 
						|
    GdkGeometry hints;
 | 
						|
    // always set both min and max hints, otherwise GTK will
 | 
						|
    // make assumptions we don't want about the unset values
 | 
						|
    int hints_mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
 | 
						|
    hints.min_width = 1;
 | 
						|
    hints.min_height = 1;
 | 
						|
    // using INT_MAX for size will lead to integer overflow with HiDPI scaling
 | 
						|
    hints.max_width = INT_MAX / 16;
 | 
						|
    hints.max_height = INT_MAX / 16;
 | 
						|
    int decorSize_x;
 | 
						|
    int decorSize_y;
 | 
						|
    if (HasClientDecor(m_widget))
 | 
						|
    {
 | 
						|
        decorSize_x = 0;
 | 
						|
        decorSize_y = 0;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        decorSize_x = m_decorSize.left + m_decorSize.right;
 | 
						|
        decorSize_y = m_decorSize.top + m_decorSize.bottom;
 | 
						|
    }
 | 
						|
    if (minSize.x > decorSize_x)
 | 
						|
        hints.min_width = minSize.x - decorSize_x;
 | 
						|
    if (minSize.y > decorSize_y)
 | 
						|
        hints.min_height = minSize.y - decorSize_y;
 | 
						|
    if (maxSize.x > 0)
 | 
						|
    {
 | 
						|
        hints.max_width = maxSize.x - decorSize_x;
 | 
						|
        if (hints.max_width < hints.min_width)
 | 
						|
            hints.max_width = hints.min_width;
 | 
						|
    }
 | 
						|
    if (maxSize.y > 0)
 | 
						|
    {
 | 
						|
        hints.max_height = maxSize.y - decorSize_y;
 | 
						|
        if (hints.max_height < hints.min_height)
 | 
						|
            hints.max_height = hints.min_height;
 | 
						|
    }
 | 
						|
    if (incW > 0 || incH > 0)
 | 
						|
    {
 | 
						|
        hints_mask |= GDK_HINT_RESIZE_INC;
 | 
						|
        hints.width_inc  = incW > 0 ? incW : 1;
 | 
						|
        hints.height_inc = incH > 0 ? incH : 1;
 | 
						|
    }
 | 
						|
    gtk_window_set_geometry_hints(
 | 
						|
        (GtkWindow*)m_widget, NULL, &hints, (GdkWindowHints)hints_mask);
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::GTKUpdateDecorSize(const DecorSize& decorSize)
 | 
						|
{
 | 
						|
    if (!IsMaximized() && !IsFullScreen())
 | 
						|
        GetCachedDecorSize() = decorSize;
 | 
						|
 | 
						|
    if (HasClientDecor(m_widget))
 | 
						|
    {
 | 
						|
        m_decorSize = decorSize;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
    if (m_updateDecorSize && memcmp(&m_decorSize, &decorSize, sizeof(DecorSize)))
 | 
						|
    {
 | 
						|
        m_useCachedClientSize = false;
 | 
						|
        const wxSize diff(
 | 
						|
            decorSize.left - m_decorSize.left + decorSize.right - m_decorSize.right,
 | 
						|
            decorSize.top - m_decorSize.top + decorSize.bottom - m_decorSize.bottom);
 | 
						|
        m_decorSize = decorSize;
 | 
						|
        bool resized = false;
 | 
						|
        if (m_minWidth > 0 || m_minHeight > 0 || m_maxWidth > 0 || m_maxHeight > 0)
 | 
						|
        {
 | 
						|
            // update size hints, they depend on m_decorSize
 | 
						|
            if (!m_deferShow)
 | 
						|
            {
 | 
						|
                // if size hints match old size, assume hints were set to
 | 
						|
                // maintain current client size, and adjust hints accordingly
 | 
						|
                if (m_minWidth == m_width) m_minWidth += diff.x;
 | 
						|
                if (m_maxWidth == m_width) m_maxWidth += diff.x;
 | 
						|
                if (m_minHeight == m_height) m_minHeight += diff.y;
 | 
						|
                if (m_maxHeight == m_height) m_maxHeight += diff.y;
 | 
						|
            }
 | 
						|
            DoSetSizeHints(m_minWidth, m_minHeight, m_maxWidth, m_maxHeight, m_incWidth, m_incHeight);
 | 
						|
        }
 | 
						|
        if (m_deferShow)
 | 
						|
        {
 | 
						|
            // keep overall size unchanged by shrinking m_widget
 | 
						|
            int w, h;
 | 
						|
            GTKDoGetSize(&w, &h);
 | 
						|
            // but not if size would be less than minimum, it won't take effect
 | 
						|
            if (w >= m_minWidth - (decorSize.left + decorSize.right) &&
 | 
						|
                h >= m_minHeight - (decorSize.top + decorSize.bottom))
 | 
						|
            {
 | 
						|
                gtk_window_resize(GTK_WINDOW(m_widget), w, h);
 | 
						|
                if (!gtk_window_get_resizable(GTK_WINDOW(m_widget)))
 | 
						|
                    gtk_widget_set_size_request(GTK_WIDGET(m_widget), w, h);
 | 
						|
                resized = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (!resized)
 | 
						|
        {
 | 
						|
            // adjust overall size to match change in frame extents
 | 
						|
            m_width  += diff.x;
 | 
						|
            m_height += diff.y;
 | 
						|
            if (m_width  < 1) m_width  = 1;
 | 
						|
            if (m_height < 1) m_height = 1;
 | 
						|
            m_clientWidth = 0;
 | 
						|
            gtk_widget_queue_resize(m_wxwindow);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (m_deferShow)
 | 
						|
    {
 | 
						|
        // gtk_widget_show() was deferred, do it now
 | 
						|
        m_deferShow = false;
 | 
						|
        DoGetClientSize(&m_clientWidth, &m_clientHeight);
 | 
						|
        SendSizeEvent();
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
        GTKSizeRevalidate();
 | 
						|
#endif
 | 
						|
        if (!m_isShown)
 | 
						|
            return;
 | 
						|
 | 
						|
        gtk_widget_show(m_widget);
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
        if (m_needSizeEvent)
 | 
						|
        {
 | 
						|
            m_needSizeEvent = false;
 | 
						|
            SendSizeEvent();
 | 
						|
        }
 | 
						|
#endif
 | 
						|
        wxShowEvent showEvent(GetId(), true);
 | 
						|
        showEvent.SetEventObject(this);
 | 
						|
        HandleWindowEvent(showEvent);
 | 
						|
    }
 | 
						|
#endif // GDK_WINDOWING_X11
 | 
						|
}
 | 
						|
 | 
						|
wxTopLevelWindowGTK::DecorSize& wxTopLevelWindowGTK::GetCachedDecorSize()
 | 
						|
{
 | 
						|
    static DecorSize size[8];
 | 
						|
 | 
						|
    int index = 0;
 | 
						|
    // title bar
 | 
						|
    if (m_gdkDecor & (GDK_DECOR_MENU | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE | GDK_DECOR_TITLE))
 | 
						|
        index = 1;
 | 
						|
    // border
 | 
						|
    if (m_gdkDecor & GDK_DECOR_BORDER)
 | 
						|
        index |= 2;
 | 
						|
    // utility window decor can be different
 | 
						|
    if (m_windowStyle & wxFRAME_TOOL_WINDOW)
 | 
						|
        index |= 4;
 | 
						|
    return size[index];
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// frame title/icon
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::SetTitle( const wxString &title )
 | 
						|
{
 | 
						|
    wxCHECK_RET(m_widget, "invalid frame");
 | 
						|
 | 
						|
    if ( title == m_title )
 | 
						|
        return;
 | 
						|
 | 
						|
    m_title = title;
 | 
						|
 | 
						|
    gtk_window_set_title( GTK_WINDOW(m_widget), wxGTK_CONV( title ) );
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::SetIcons( const wxIconBundle &icons )
 | 
						|
{
 | 
						|
    base_type::SetIcons(icons);
 | 
						|
 | 
						|
    // Setting icons before window is realized can cause a GTK assertion if
 | 
						|
    // another TLW is realized before this one, and it has this one as its
 | 
						|
    // transient parent. The life demo exibits this problem.
 | 
						|
    if (m_widget && gtk_widget_get_realized(m_widget))
 | 
						|
    {
 | 
						|
        GList* list = NULL;
 | 
						|
        for (size_t i = icons.GetIconCount(); i--;)
 | 
						|
            list = g_list_prepend(list, icons.GetIconByIndex(i).GetPixbuf());
 | 
						|
        gtk_window_set_icon_list(GTK_WINDOW(m_widget), list);
 | 
						|
        g_list_free(list);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// frame state: maximized/iconized/normal
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::Maximize(bool maximize)
 | 
						|
{
 | 
						|
    if (maximize)
 | 
						|
        gtk_window_maximize( GTK_WINDOW( m_widget ) );
 | 
						|
    else
 | 
						|
        gtk_window_unmaximize( GTK_WINDOW( m_widget ) );
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::IsMaximized() const
 | 
						|
{
 | 
						|
    GdkWindow* window = NULL;
 | 
						|
    if (m_widget)
 | 
						|
        window = gtk_widget_get_window(m_widget);
 | 
						|
    return window && (gdk_window_get_state(window) & GDK_WINDOW_STATE_MAXIMIZED);
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::Restore()
 | 
						|
{
 | 
						|
    // "Present" seems similar enough to "restore"
 | 
						|
    gtk_window_present( GTK_WINDOW( m_widget ) );
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::Iconize( bool iconize )
 | 
						|
{
 | 
						|
    if (iconize)
 | 
						|
        gtk_window_iconify( GTK_WINDOW( m_widget ) );
 | 
						|
    else
 | 
						|
        gtk_window_deiconify( GTK_WINDOW( m_widget ) );
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::IsIconized() const
 | 
						|
{
 | 
						|
    return m_isIconized;
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::SetIconizeState(bool iconize)
 | 
						|
{
 | 
						|
    if ( iconize != m_isIconized )
 | 
						|
    {
 | 
						|
        /*
 | 
						|
            We get a configure-event _before_ the window is iconized with dummy
 | 
						|
            (0, 0) coordinates. Unfortunately we can't just ignore this event
 | 
						|
            when we get it, because we can't know if we're going to be iconized
 | 
						|
            or not: even remembering that we should be soon in Iconize() itself
 | 
						|
            doesn't help due to the crazy sequence of events generated by GTK,
 | 
						|
            e.g. under X11 for the sequence of Iconize() and Show() calls we
 | 
						|
            get the following events with GTK2:
 | 
						|
 | 
						|
            - window-state changes to GDK_WINDOW_STATE_ICONIFIED | WITHDRAWN
 | 
						|
            - window-state changes to just GDK_WINDOW_STATE_ICONIFIED
 | 
						|
            - map event
 | 
						|
            - window-state changes to normal
 | 
						|
            - configure event with normal size
 | 
						|
            - window-state changes to GDK_WINDOW_STATE_ICONIFIED
 | 
						|
            - configure event with normal size
 | 
						|
            - configure event with (0, 0) size
 | 
						|
            - window-state changes to normal (yes, again)
 | 
						|
            - configure event with (0, 0) size
 | 
						|
            - window-state changes to GDK_WINDOW_STATE_ICONIFIED
 | 
						|
 | 
						|
            So even though we could ignore the first (0, 0) configure event, we
 | 
						|
            still wouldn't be able to ignore the second one, happening after
 | 
						|
            the inexplicable switch to normal state.
 | 
						|
 | 
						|
            For completeness, with GTK3 the sequence of events is simpler, but
 | 
						|
            still very unhelpful for our purposes:
 | 
						|
 | 
						|
            - window-state changes to GDK_WINDOW_STATE_ICONIFIED
 | 
						|
            - window-state changes to normal
 | 
						|
            - map event
 | 
						|
            - configure event with normal size
 | 
						|
            - configure event with normal size (yes, because one is not enough)
 | 
						|
            - configure event with (0, 0) size
 | 
						|
            - configure event with (0, 0) size (because why not have 2 of them)
 | 
						|
            - window-state changes to GDK_WINDOW_STATE_ICONIFIED
 | 
						|
 | 
						|
            Here again we have (0, 0) configure events happening _after_ the
 | 
						|
            window-state switch to normal, that would reset our flag.
 | 
						|
 | 
						|
            In conclusion, we have no choice but to assume that the window got
 | 
						|
            really moved, but at least we can remember its previous position in
 | 
						|
            order to restore it here if it turns out that it was just iconized.
 | 
						|
         */
 | 
						|
        if ( iconize )
 | 
						|
        {
 | 
						|
            if ( wxPoint(m_x, m_y) == wxPoint(0, 0) )
 | 
						|
            {
 | 
						|
                m_x = m_lastPos.x;
 | 
						|
                m_y = m_lastPos.y;
 | 
						|
 | 
						|
                // This is not really necessary, but seems tidier.
 | 
						|
                m_lastPos = wxPoint();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        m_isIconized = iconize;
 | 
						|
        (void)SendIconizeEvent(iconize);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::AddGrab()
 | 
						|
{
 | 
						|
    if (!m_grabbedEventLoop)
 | 
						|
    {
 | 
						|
        wxGUIEventLoop eventLoop;
 | 
						|
        m_grabbedEventLoop = &eventLoop;
 | 
						|
        gtk_grab_add( m_widget );
 | 
						|
        eventLoop.Run();
 | 
						|
        gtk_grab_remove( m_widget );
 | 
						|
        m_grabbedEventLoop = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::RemoveGrab()
 | 
						|
{
 | 
						|
    if (m_grabbedEventLoop)
 | 
						|
    {
 | 
						|
        m_grabbedEventLoop->Exit();
 | 
						|
        m_grabbedEventLoop = NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::IsGrabbed() const
 | 
						|
{
 | 
						|
    return m_grabbedEventLoop != NULL;
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::IsActive()
 | 
						|
{
 | 
						|
    return (this == (wxTopLevelWindowGTK*)g_activeFrame);
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::RequestUserAttention(int flags)
 | 
						|
{
 | 
						|
    bool new_hint_value = false;
 | 
						|
 | 
						|
    // FIXME: This is a workaround to focus handling problem
 | 
						|
    // If RequestUserAttention is called for example right after a wxSleep, OnInternalIdle
 | 
						|
    // hasn't yet been processed, and the internal focus system is not up to date yet.
 | 
						|
    // YieldFor(wxEVT_CATEGORY_UI) ensures the processing of it (hopefully it
 | 
						|
    // won't have side effects) - MR
 | 
						|
    wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
 | 
						|
 | 
						|
    if(m_urgency_hint >= 0)
 | 
						|
        g_source_remove(m_urgency_hint);
 | 
						|
 | 
						|
    m_urgency_hint = -2;
 | 
						|
 | 
						|
    if( gtk_widget_get_realized(m_widget) && !IsActive() )
 | 
						|
    {
 | 
						|
        new_hint_value = true;
 | 
						|
 | 
						|
        if (flags & wxUSER_ATTENTION_INFO)
 | 
						|
        {
 | 
						|
            m_urgency_hint = g_timeout_add(5000, (GSourceFunc)gtk_frame_urgency_timer_callback, this);
 | 
						|
        } else {
 | 
						|
            m_urgency_hint = -1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    gtk_window_set_urgency_hint(GTK_WINDOW(m_widget), new_hint_value);
 | 
						|
}
 | 
						|
 | 
						|
void wxTopLevelWindowGTK::SetWindowStyleFlag( long style )
 | 
						|
{
 | 
						|
    // Store which styles were changed
 | 
						|
    long styleChanges = style ^ m_windowStyle;
 | 
						|
 | 
						|
    // Process wxWindow styles. This also updates the internal variable
 | 
						|
    // Therefore m_windowStyle bits carry now the _new_ style values
 | 
						|
    wxWindow::SetWindowStyleFlag(style);
 | 
						|
 | 
						|
    // just return for now if widget does not exist yet
 | 
						|
    if (!m_widget)
 | 
						|
        return;
 | 
						|
 | 
						|
    if ( styleChanges & wxSTAY_ON_TOP )
 | 
						|
    {
 | 
						|
        gtk_window_set_keep_above(GTK_WINDOW(m_widget),
 | 
						|
                                  m_windowStyle & wxSTAY_ON_TOP);
 | 
						|
    }
 | 
						|
 | 
						|
    if ( styleChanges & wxFRAME_NO_TASKBAR )
 | 
						|
    {
 | 
						|
        gtk_window_set_skip_taskbar_hint(GTK_WINDOW(m_widget),
 | 
						|
                                         m_windowStyle & wxFRAME_NO_TASKBAR);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::SetTransparent(wxByte alpha)
 | 
						|
{
 | 
						|
    if (m_widget == NULL)
 | 
						|
        return false;
 | 
						|
 | 
						|
#ifdef __WXGTK3__
 | 
						|
#if GTK_CHECK_VERSION(3,8,0)
 | 
						|
    if (wx_is_at_least_gtk3(8))
 | 
						|
    {
 | 
						|
        gtk_widget_set_opacity(m_widget, alpha / 255.0);
 | 
						|
    }
 | 
						|
    else
 | 
						|
#endif // GTK+ 3.8+
 | 
						|
    {
 | 
						|
#ifndef __WXGTK4__
 | 
						|
        wxGCC_WARNING_SUPPRESS(deprecated-declarations)
 | 
						|
        gtk_window_set_opacity(GTK_WINDOW(m_widget), alpha / 255.0);
 | 
						|
        wxGCC_WARNING_RESTORE()
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
#else // !__WXGTK3__
 | 
						|
 | 
						|
#if GTK_CHECK_VERSION(2,12,0)
 | 
						|
    if (wx_is_at_least_gtk2(12))
 | 
						|
    {
 | 
						|
        gtk_window_set_opacity(GTK_WINDOW(m_widget), alpha / 255.0);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
#endif // GTK_CHECK_VERSION(2,12,0)
 | 
						|
#ifdef GDK_WINDOWING_X11
 | 
						|
    GdkWindow* window = gtk_widget_get_window(m_widget);
 | 
						|
    if (window == NULL)
 | 
						|
        return false;
 | 
						|
 | 
						|
    Display* dpy = GDK_WINDOW_XDISPLAY(window);
 | 
						|
    Window win = GDK_WINDOW_XID(window);
 | 
						|
 | 
						|
    if (alpha == 0xff)
 | 
						|
        XDeleteProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False));
 | 
						|
    else
 | 
						|
    {
 | 
						|
        long opacity = alpha * 0x1010101L;
 | 
						|
        XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False),
 | 
						|
                        XA_CARDINAL, 32, PropModeReplace,
 | 
						|
                        (unsigned char *) &opacity, 1L);
 | 
						|
    }
 | 
						|
    XSync(dpy, False);
 | 
						|
    return true;
 | 
						|
#else // !GDK_WINDOWING_X11
 | 
						|
    return false;
 | 
						|
#endif // GDK_WINDOWING_X11 / !GDK_WINDOWING_X11
 | 
						|
#endif // __WXGTK3__/!__WXGTK3__
 | 
						|
}
 | 
						|
 | 
						|
bool wxTopLevelWindowGTK::CanSetTransparent()
 | 
						|
{
 | 
						|
    // allow to override automatic detection as it's far from perfect
 | 
						|
    const wxString SYSOPT_TRANSPARENT = "gtk.tlw.can-set-transparent";
 | 
						|
    if ( wxSystemOptions::HasOption(SYSOPT_TRANSPARENT) )
 | 
						|
    {
 | 
						|
        return wxSystemOptions::GetOptionInt(SYSOPT_TRANSPARENT) != 0;
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef __WXGTK4__
 | 
						|
    return gdk_display_is_composited(gtk_widget_get_display(m_widget)) != 0;
 | 
						|
#else
 | 
						|
#if GTK_CHECK_VERSION(2,10,0)
 | 
						|
    if (wx_is_at_least_gtk2(10))
 | 
						|
    {
 | 
						|
        wxGCC_WARNING_SUPPRESS(deprecated-declarations)
 | 
						|
        return gtk_widget_is_composited(m_widget) != 0;
 | 
						|
        wxGCC_WARNING_RESTORE()
 | 
						|
    }
 | 
						|
    else
 | 
						|
#endif // In case of lower versions than gtk+-2.10.0 we could look for _NET_WM_CM_Sn ourselves
 | 
						|
    {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
#if 0 // Don't be optimistic here for the sake of wxAUI
 | 
						|
    int opcode, event, error;
 | 
						|
    // Check for the existence of a RGBA visual instead?
 | 
						|
    return XQueryExtension(gdk_x11_get_default_xdisplay (),
 | 
						|
                           "Composite", &opcode, &event, &error);
 | 
						|
#endif
 | 
						|
}
 |