mirror of
https://github.com/Retropex/dolphin.git
synced 2025-08-04 14:04:47 +02:00

From wxWidgets master 81570ae070b35c9d52de47b1f14897f3ff1a66c7. include/wx/defs.h -- __w64 warning disable patch by comex brought forward. include/wx/msw/window.h -- added GetContentScaleFactor() which was not implemented on Windows but is necessary for wxBitmap scaling on Mac OS X so it needs to work to avoid #ifdef-ing the code. src/gtk/window.cpp -- Modified DoSetClientSize() to direct call wxWindowGTK::DoSetSize() instead of using public wxWindowBase::SetSize() which now prevents derived classes (like wxAuiToolbar) intercepting the call and breaking it. This matches Windows which does NOT need to call DoSetSize internally. End result is this fixes Dolphin's debug tools toolbars on Linux. src/osx/window_osx.cpp -- Same fix as for GTK since it has the same issue. src/msw/radiobox.cpp -- Hacked to fix display in HiDPI (was clipping off end of text). Updated CMakeLists for Linux and Mac OS X. Small code changes to Dolphin to fix debug error boxes, deprecation warnings, and retain previous UI behavior on Windows.
1618 lines
50 KiB
C++
1618 lines
50 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 <gtk/gtk.h>
|
|
#ifdef GDK_WINDOWING_X11
|
|
#include <gdk/gdkx.h>
|
|
#include <X11/Xatom.h> // XA_CARDINAL
|
|
#include "wx/unix/utilsx11.h"
|
|
#endif
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
#include <gdk/gdkwayland.h>
|
|
#define HAS_CLIENT_DECOR
|
|
#endif
|
|
#ifdef GDK_WINDOWING_MIR
|
|
extern "C" {
|
|
#include <gdk/gdkmir.h>
|
|
}
|
|
#define HAS_CLIENT_DECOR
|
|
#endif
|
|
#ifdef GDK_WINDOWING_BROADWAY
|
|
#include <gdk/gdkbroadway.h>
|
|
#define HAS_CLIENT_DECOR
|
|
#endif
|
|
|
|
#include "wx/gtk/private.h"
|
|
#include "wx/gtk/private/gtk2-compat.h"
|
|
#include "wx/gtk/private/win_gtk.h"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// 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 HAS_CLIENT_DECOR
|
|
static bool HasClientDecor(GtkWidget* widget)
|
|
{
|
|
GdkDisplay* display = gtk_widget_get_display(widget);
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
if (GDK_IS_WAYLAND_DISPLAY(display))
|
|
return true;
|
|
#endif
|
|
#ifdef GDK_WINDOWING_MIR
|
|
if (GDK_IS_MIR_DISPLAY(display))
|
|
return true;
|
|
#endif
|
|
#ifdef GDK_WINDOWING_BROADWAY
|
|
if (GDK_IS_BROADWAY_DISPLAY(display))
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
#endif // HAS_CLIENT_DECOR
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// RequestUserAttention related functions
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef __WXGTK3__
|
|
static void wxgtk_window_set_urgency_hint (GtkWindow *win,
|
|
gboolean setting)
|
|
{
|
|
#if GTK_CHECK_VERSION(2,7,0)
|
|
if (gtk_check_version(2,7,0) == NULL)
|
|
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
|
|
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;
|
|
|
|
if (GTK_WIDGET_GET_CLASS(widget)->key_press_event(widget, event))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// "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);
|
|
#ifdef HAS_CLIENT_DECOR
|
|
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
|
|
#endif
|
|
{
|
|
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_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;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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;
|
|
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)
|
|
{
|
|
long* p = (long*)data;
|
|
if (left) *left = int(p[0]);
|
|
if (right) *right = int(p[1]);
|
|
if (top) *top = int(p[2]);
|
|
if (bottom) *bottom = int(p[3]);
|
|
}
|
|
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_grabbed = false;
|
|
m_deferShow = true;
|
|
m_deferShowAllowed = true;
|
|
m_updateDecorSize = true;
|
|
m_netFrameExtentsTimerId = 0;
|
|
m_incWidth = m_incHeight = 0;
|
|
memset(&m_decorSize, 0, sizeof(m_decorSize));
|
|
|
|
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();
|
|
|
|
#ifndef __WXGTK3__
|
|
if (pos != wxDefaultPosition)
|
|
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 defined(GDK_WINDOWING_WAYLAND) && GTK_CHECK_VERSION(3,10,0)
|
|
else if (
|
|
GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(m_widget)) &&
|
|
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);
|
|
}
|
|
|
|
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_grabbed)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
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 ) );
|
|
}
|
|
#ifdef GDK_WINDOWING_X11
|
|
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 );
|
|
|
|
const int screen_width = gdk_screen_get_width(screen);
|
|
const int screen_height = gdk_screen_get_height(screen);
|
|
|
|
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_deferShow;
|
|
if (deferShow)
|
|
{
|
|
deferShow = m_deferShowAllowed &&
|
|
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.
|
|
wxSizeEvent event(GetSize(), GetId());
|
|
event.SetEventObject(this);
|
|
HandleWindowEvent(event);
|
|
|
|
#ifdef __WXGTK3__
|
|
GTKSizeRevalidate();
|
|
#endif
|
|
}
|
|
|
|
bool change = base_type::Show(show);
|
|
|
|
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);
|
|
#ifdef HAS_CLIENT_DECOR
|
|
if (!HasClientDecor(m_widget))
|
|
#endif
|
|
{
|
|
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);
|
|
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, reset_size_request, m_wxwindow, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
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;
|
|
hints.max_width = INT_MAX;
|
|
hints.max_height = INT_MAX;
|
|
int decorSize_x;
|
|
int decorSize_y;
|
|
#ifdef HAS_CLIENT_DECOR
|
|
if (HasClientDecor(m_widget))
|
|
{
|
|
decorSize_x = 0;
|
|
decorSize_y = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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;
|
|
|
|
#ifdef HAS_CLIENT_DECOR
|
|
if (HasClientDecor(m_widget))
|
|
{
|
|
m_decorSize = decorSize;
|
|
return;
|
|
}
|
|
#endif
|
|
#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_height) m_minWidth += diff.x;
|
|
if (m_maxWidth == m_height) 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);
|
|
wxSizeEvent sizeEvent(GetSize(), GetId());
|
|
sizeEvent.SetEventObject(this);
|
|
HandleWindowEvent(sizeEvent);
|
|
|
|
#ifdef __WXGTK3__
|
|
GTKSizeRevalidate();
|
|
#endif
|
|
gtk_widget_show(m_widget);
|
|
|
|
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 )
|
|
{
|
|
m_isIconized = iconize;
|
|
(void)SendIconizeEvent(iconize);
|
|
}
|
|
}
|
|
|
|
void wxTopLevelWindowGTK::AddGrab()
|
|
{
|
|
if (!m_grabbed)
|
|
{
|
|
m_grabbed = true;
|
|
gtk_grab_add( m_widget );
|
|
wxGUIEventLoop().Run();
|
|
gtk_grab_remove( m_widget );
|
|
}
|
|
}
|
|
|
|
void wxTopLevelWindowGTK::RemoveGrab()
|
|
{
|
|
if (m_grabbed)
|
|
{
|
|
gtk_main_quit();
|
|
m_grabbed = false;
|
|
}
|
|
}
|
|
|
|
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;
|
|
#if GTK_CHECK_VERSION(2,12,0)
|
|
#ifndef __WXGTK3__
|
|
if (gtk_check_version(2,12,0) == NULL)
|
|
#endif
|
|
{
|
|
#if GTK_CHECK_VERSION(3,8,0)
|
|
if(gtk_check_version(3,8,0) == NULL)
|
|
{
|
|
gtk_widget_set_opacity(m_widget, alpha / 255.0);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// Can't avoid using this deprecated function with older GTK+.
|
|
wxGCC_WARNING_SUPPRESS(deprecated-declarations);
|
|
gtk_window_set_opacity(GTK_WINDOW(m_widget), alpha / 255.0);
|
|
wxGCC_WARNING_RESTORE();
|
|
}
|
|
return true;
|
|
}
|
|
#endif // GTK_CHECK_VERSION(2,12,0)
|
|
#ifndef __WXGTK3__
|
|
#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__
|
|
}
|
|
|
|
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 __WXGTK3__
|
|
return gtk_widget_is_composited(m_widget) != 0;
|
|
#else
|
|
#if GTK_CHECK_VERSION(2,10,0)
|
|
if (!gtk_check_version(2,10,0))
|
|
{
|
|
return gtk_widget_is_composited(m_widget) != 0;
|
|
}
|
|
else
|
|
#endif // In case of lower versions than gtk+-2.10.0 we could look for _NET_WM_CM_Sn ourselves
|
|
{
|
|
return false;
|
|
}
|
|
#endif // !__WXGTK3__
|
|
|
|
#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
|
|
}
|