#include "wx/gtk/private.h"
#include "wx/gtk/private/win_gtk.h"
+#include "wx/gtk/private/event.h"
+using namespace wxGTKImpl;
+#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
+#include "wx/x11/private/wrapxkb.h"
+#else
+typedef guint KeySym;
+#endif
#include <gdk/gdkkeysyms.h>
#if GTK_CHECK_VERSION(3,0,0)
#include <gdk/gdkkeysyms-compat.h>
#endif
-#if !GTK_CHECK_VERSION(2,10,0)
- // GTK+ can reliably detect Meta key state only since 2.10 when
- // GDK_META_MASK was introduced -- there wasn't any way to detect it
- // in older versions. wxGTK used GDK_MOD2_MASK for this purpose, but
- // GDK_MOD2_MASK is documented as:
- //
- // the fifth modifier key (it depends on the modifier mapping of the X
- // server which key is interpreted as this modifier)
- //
- // In other words, it isn't guaranteed to map to Meta. This is a real
- // problem: it is common to map NumLock to it (in fact, it's an exception
- // if the X server _doesn't_ use it for NumLock). So the old code caused
- // wxKeyEvent::MetaDown() to always return true as long as NumLock was on
- // on many systems, which broke all applications using
- // wxKeyEvent::GetModifiers() to check modifiers state (see e.g. here:
- // http://tinyurl.com/56lsk2).
- //
- // Because of this, it's better to not detect Meta key state at all than
- // to detect it incorrectly. Hence the following #define, which causes
- // m_metaDown to be always set to false.
- #define GDK_META_MASK 0
-#endif
+// gdk_window_set_composited() is only supported since 2.12
+#define wxGTK_VERSION_REQUIRED_FOR_COMPOSITING 2,12,0
+#define wxGTK_HAS_COMPOSITING_SUPPORT GTK_CHECK_VERSION(2,12,0)
//-----------------------------------------------------------------------------
// documentation on internals
// the trace mask used for the focus debugging messages
#define TRACE_FOCUS wxT("focus")
-//-----------------------------------------------------------------------------
-// missing gdk functions
-//-----------------------------------------------------------------------------
-
-void
-gdk_window_warp_pointer (GdkWindow *window,
- gint x,
- gint y)
-{
- if (!window)
- window = gdk_get_default_root_window();
-
- if (!GDK_WINDOW_DESTROYED(window))
- {
- XWarpPointer (GDK_WINDOW_XDISPLAY(window),
- None, /* not source window -> move from anywhere */
- GDK_WINDOW_XID(window), /* dest window */
- 0, 0, 0, 0, /* not source window -> move from anywhere */
- x, y );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// "size_request" of m_widget
-//-----------------------------------------------------------------------------
-
-extern "C" {
-static void
-wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget),
- GtkRequisition *requisition,
- wxWindow * win)
-{
- int w, h;
- win->GetSize( &w, &h );
- if (w < 2)
- w = 2;
- if (h < 2)
- h = 2;
-
- requisition->height = h;
- requisition->width = w;
-}
-}
-
//-----------------------------------------------------------------------------
// "expose_event" of m_wxwindow
//-----------------------------------------------------------------------------
extern "C" {
static void
-parent_set(GtkWidget* widget, GtkObject* old_parent, wxWindow* win)
+parent_set(GtkWidget* widget, GtkWidget* old_parent, wxWindow* win)
{
if (old_parent)
{
// "key_press_event" from any window
//-----------------------------------------------------------------------------
-// These are used when transforming Ctrl-alpha to ascii values 1-26
-inline bool wxIsLowerChar(int code)
-{
- return (code >= 'a' && code <= 'z' );
-}
-
-inline bool wxIsUpperChar(int code)
-{
- return (code >= 'A' && code <= 'Z' );
-}
-
-
// set WXTRACE to this to see the key event codes on the console
#define TRACE_KEYS wxT("keyevent")
keysym = (KeySym)gdk_event->string[0];
}
+#ifdef GDK_WINDOWING_X11
// we want to always get the same key code when the same key is
// pressed regardless of the state of the modifiers, i.e. on a
// standard US keyboard pressing '5' or '%' ('5' key with
wxLogTrace(TRACE_KEYS, wxT("\t-> keycode %d"), keycode);
+#ifdef HAVE_X11_XKBLIB_H
+ KeySym keysymNormalized = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
+#else
KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0);
+#endif
// use the normalized, i.e. lower register, keysym if we've
// got one
key_code = keysymNormalized ? keysymNormalized : keysym;
+#else
+ key_code = keysym;
+#endif
// as explained above, we want to have lower register key codes
// normally but for the letter keys we want to have the upper ones
namespace
{
-// Send wxEVT_CHAR_HOOK event to the parent of the window and if it wasn't
-// processed, send wxEVT_CHAR to the window itself. Return true if either of
-// them was handled.
-bool
-SendCharHookAndCharEvents(const wxKeyEvent& event, wxWindow *win)
+// Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
+// if it was processed (and not skipped).
+bool SendCharHookEvent(const wxKeyEvent& event, wxWindow *win)
{
- // wxEVT_CHAR_HOOK must be sent to the top level parent window to allow it
+ // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
+ // which typically closes when Esc key is pressed in any of its controls)
// to handle key events in all of its children unless the mouse is captured
// in which case we consider that the keyboard should be "captured" too.
if ( !g_captureWindow )
{
- wxWindow * const parent = wxGetTopLevelParent(win);
- if ( parent )
- {
- // We need to make a copy of the event object because it is
- // modified while it's handled, notably its WasProcessed() flag
- // is set after it had been processed once.
- wxKeyEvent eventCharHook(event);
- eventCharHook.SetEventType(wxEVT_CHAR_HOOK);
- if ( parent->HandleWindowEvent(eventCharHook) )
- return true;
- }
+ wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event);
+ if ( win->HandleWindowEvent(eventCharHook)
+ && !event.IsNextEventAllowed() )
+ return true;
}
- // As above, make a copy of the event first.
- wxKeyEvent eventChar(event);
- eventChar.SetEventType(wxEVT_CHAR);
- return win->HandleWindowEvent(eventChar);
+ return false;
+}
+
+// Adjust wxEVT_CHAR event key code fields. This function takes care of two
+// conventions:
+// (a) Ctrl-letter key presses generate key codes in range 1..26
+// (b) Unicode key codes are same as key codes for the codes in 1..255 range
+void AdjustCharEventKeyCodes(wxKeyEvent& event)
+{
+ const int code = event.m_keyCode;
+
+ // Check for (a) above.
+ if ( event.ControlDown() )
+ {
+ // We intentionally don't use isupper/lower() here, we really need
+ // ASCII letters only as it doesn't make sense to translate any other
+ // ones into this range which has only 26 slots.
+ if ( code >= 'a' && code <= 'z' )
+ event.m_keyCode = code - 'a' + 1;
+ else if ( code >= 'A' && code <= 'Z' )
+ event.m_keyCode = code - 'A' + 1;
+
+#if wxUSE_UNICODE
+ // Adjust the Unicode equivalent in the same way too.
+ if ( event.m_keyCode != code )
+ event.m_uniChar = event.m_keyCode;
+#endif // wxUSE_UNICODE
+ }
+
+#if wxUSE_UNICODE
+ // Check for (b) from above.
+ //
+ // FIXME: Should we do it for key codes up to 255?
+ if ( !event.m_uniChar && code < WXK_DELETE )
+ event.m_uniChar = code;
+#endif // wxUSE_UNICODE
}
} // anonymous namespace
+// If a widget does not handle a key or mouse event, GTK+ sends it up the
+// parent chain until it is handled. These events are not supposed to propagate
+// in wxWidgets, so this code avoids handling them in any parent wxWindow,
+// while still allowing the event to propagate so things like native keyboard
+// navigation will work.
+#define wxPROCESS_EVENT_ONCE(EventType, event) \
+ static EventType eventPrev; \
+ if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
+ return false; \
+ eventPrev = *event
+
extern "C" {
static gboolean
gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
if (g_blockEventsOnDrag)
return FALSE;
+ wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
+
wxKeyEvent event( wxEVT_KEY_DOWN );
bool ret = false;
bool return_after_IM = false;
if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
{
+ // Send the CHAR_HOOK event first
+ if ( SendCharHookEvent(event, win) )
+ {
+ // Don't do anything at all with this event any more.
+ return TRUE;
+ }
+
// Emit KEY_DOWN event
ret = win->HandleWindowEvent( event );
}
// We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
// docs, if IM filter returns true, no further processing should be done.
// we should send the key_down event anyway.
- bool intercepted_by_IM = gtk_im_context_filter_keypress(win->m_imData->context, gdk_event);
+ bool intercepted_by_IM =
+ gtk_im_context_filter_keypress(win->m_imData->context, gdk_event) != 0;
win->m_imData->lastKeyEvent = NULL;
if (intercepted_by_IM)
{
if ( key_code )
{
+ wxKeyEvent eventChar(wxEVT_CHAR, event);
+
wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
- event.m_keyCode = key_code;
+ eventChar.m_keyCode = key_code;
- // To conform to the docs we need to translate Ctrl-alpha
- // characters to values in the range 1-26.
- if ( event.ControlDown() &&
- ( wxIsLowerChar(key_code) || wxIsUpperChar(key_code) ))
- {
- if ( wxIsLowerChar(key_code) )
- event.m_keyCode = key_code - 'a' + 1;
- if ( wxIsUpperChar(key_code) )
- event.m_keyCode = key_code - 'A' + 1;
-#if wxUSE_UNICODE
- event.m_uniChar = event.m_keyCode;
-#endif
- }
+ AdjustCharEventKeyCodes(eventChar);
- ret = SendCharHookAndCharEvents(event, win);
+ ret = win->HandleWindowEvent(eventChar);
}
}
const gchar *str,
wxWindow *window)
{
- wxKeyEvent event( wxEVT_KEY_DOWN );
+ wxKeyEvent event( wxEVT_CHAR );
// take modifiers, cursor position, timestamp etc. from the last
// key_press_event that was fed into Input Method:
event.m_keyCode = (char)*pstr;
#endif // wxUSE_UNICODE
- // To conform to the docs we need to translate Ctrl-alpha
- // characters to values in the range 1-26.
- if ( event.ControlDown() &&
- ( wxIsLowerChar(*pstr) || wxIsUpperChar(*pstr) ))
- {
- if ( wxIsLowerChar(*pstr) )
- event.m_keyCode = *pstr - 'a' + 1;
- if ( wxIsUpperChar(*pstr) )
- event.m_keyCode = *pstr - 'A' + 1;
-
- event.m_keyCode = *pstr - 'a' + 1;
-#if wxUSE_UNICODE
- event.m_uniChar = event.m_keyCode;
-#endif
- }
+ AdjustCharEventKeyCodes(event);
- SendCharHookAndCharEvents(event, window);
+ window->HandleWindowEvent(event);
}
}
}
if (g_blockEventsOnDrag)
return FALSE;
+ wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
+
wxKeyEvent event( wxEVT_KEY_UP );
if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
{
// mouse event processing helpers
// ----------------------------------------------------------------------------
-// init wxMouseEvent with the info from GdkEventXXX struct
-template<typename T> void InitMouseEvent(wxWindowGTK *win,
- wxMouseEvent& event,
- T *gdk_event)
-{
- event.SetTimestamp( gdk_event->time );
- event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
- event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
- event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
- event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
- event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK) != 0;
- event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK) != 0;
- event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK) != 0;
-
- // In gdk/win32 VK_XBUTTON1 is translated to GDK_BUTTON4_MASK
- // and VK_XBUTTON2 to GDK_BUTTON5_MASK. In x11/gdk buttons 4/5
- // are wheel rotation and buttons 8/9 don't change the state.
- event.m_aux1Down = (gdk_event->state & GDK_BUTTON4_MASK) != 0;
- event.m_aux2Down = (gdk_event->state & GDK_BUTTON5_MASK) != 0;
-
- wxPoint pt = win->GetClientAreaOrigin();
- event.m_x = (wxCoord)gdk_event->x - pt.x;
- event.m_y = (wxCoord)gdk_event->y - pt.y;
-
- if ((win->m_wxwindow) && (win->GetLayoutDirection() == wxLayout_RightToLeft))
- {
- // origin in the upper right corner
- GtkAllocation a;
- gtk_widget_get_allocation(win->m_wxwindow, &a);
- int window_width = a.width;
- event.m_x = window_width - event.m_x;
- }
-
- event.SetEventObject( win );
- event.SetId( win->GetId() );
- event.SetTimestamp( gdk_event->time );
-}
-
static void AdjustEventButtonState(wxMouseEvent& event)
{
// GDK reports the old state of the button for a button press event, but
}
}
-// find the window to send the mouse event too
+// find the window to send the mouse event to
static
wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
{
wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
while (node)
{
- wxWindowGTK *child = node->GetData();
+ wxWindow* child = static_cast<wxWindow*>(node->GetData());
node = node->GetNext();
if (!child->IsShown())
else
{
if ((child->m_wxwindow == NULL) &&
+ win->IsClientAreaChild(child) &&
(child->m_x <= xx) &&
(child->m_y <= yy) &&
(child->m_x+child->m_width >= xx) &&
GdkEventButton *gdk_event,
wxWindowGTK *win )
{
+ wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
+
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
g_lastButtonNumber = gdk_event->button;
GdkEventButton *gdk_event,
wxWindowGTK *win )
{
+ wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
+
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
g_lastButtonNumber = 0;
GdkEventMotion *gdk_event,
wxWindowGTK *win )
{
+ wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event);
+
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
if (gdk_event->is_hint)
static gboolean
window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
{
- if (gdk_event->direction != GDK_SCROLL_UP &&
- gdk_event->direction != GDK_SCROLL_DOWN)
- {
- return false;
- }
-
wxMouseEvent event(wxEVT_MOUSEWHEEL);
InitMouseEvent(win, event, gdk_event);
// FIXME: Get these values from GTK or GDK
event.m_linesPerAction = 3;
event.m_wheelDelta = 120;
- if (gdk_event->direction == GDK_SCROLL_UP)
- event.m_wheelRotation = 120;
- else
- event.m_wheelRotation = -120;
+
+ // Determine the scroll direction.
+ switch (gdk_event->direction)
+ {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_RIGHT:
+ event.m_wheelRotation = 120;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ case GDK_SCROLL_LEFT:
+ event.m_wheelRotation = -120;
+ break;
+
+ default:
+ return false; // Unknown/unhandled direction
+ }
+
+ // And the scroll axis.
+ switch (gdk_event->direction)
+ {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_DOWN:
+ event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
+ break;
+
+ case GDK_SCROLL_LEFT:
+ case GDK_SCROLL_RIGHT:
+ event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
+ break;
+ }
if (win->GTKProcessEvent(event))
return TRUE;
//-----------------------------------------------------------------------------
static void
-gtk_window_realized_callback(GtkWidget* widget, wxWindow* win)
+gtk_window_realized_callback(GtkWidget* WXUNUSED(widget), wxWindowGTK* win)
{
- if (win->m_imData)
- {
- gtk_im_context_set_client_window( win->m_imData->context,
- win->m_wxwindow ? win->GTKGetDrawingWindow() : gtk_widget_get_window(widget));
- }
-
- // We cannot set colours and fonts before the widget
- // been realized, so we do this directly after realization
- // or otherwise in idle time
-
- if (win->m_needsStyleChange)
- {
- win->SetBackgroundStyle(win->GetBackgroundStyle());
- win->m_needsStyleChange = false;
- }
+ win->GTKHandleRealized();
+}
- wxWindowCreateEvent event( win );
- event.SetEventObject( win );
- win->GTKProcessEvent( event );
+//-----------------------------------------------------------------------------
+// "unrealize" from m_wxwindow
+//-----------------------------------------------------------------------------
- win->GTKUpdateCursor(true, false);
+static void unrealize(GtkWidget*, wxWindowGTK* win)
+{
+ if (win->m_imData)
+ gtk_im_context_set_client_window(win->m_imData->context, NULL);
}
//-----------------------------------------------------------------------------
{
if (win && previous_style)
{
- wxSysColourChangedEvent event;
- event.SetEventObject(win);
-
- win->GTKProcessEvent( event );
+ if (win->IsTopLevel())
+ {
+ wxSysColourChangedEvent event;
+ event.SetEventObject(win);
+ win->GTKProcessEvent(event);
+ }
+ else
+ {
+ // Border width could change, which will change client size.
+ // Make sure size event occurs for this
+ win->m_oldClientWidth = 0;
+ }
}
}
} // extern "C"
+void wxWindowGTK::GTKHandleRealized()
+{
+ if (m_imData)
+ {
+ gtk_im_context_set_client_window
+ (
+ m_imData->context,
+ m_wxwindow ? GTKGetDrawingWindow()
+ : gtk_widget_get_window(m_widget)
+ );
+ }
+
+ // Use composited window if background is transparent, if supported.
+ if (m_backgroundStyle == wxBG_STYLE_TRANSPARENT)
+ {
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+ if (IsTransparentBackgroundSupported())
+ {
+ GdkWindow* const window = GTKGetDrawingWindow();
+ if (window)
+ gdk_window_set_composited(window, true);
+ }
+ else
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+ {
+ // We revert to erase mode if transparency is not supported
+ m_backgroundStyle = wxBG_STYLE_ERASE;
+ }
+ }
+
+
+ // We cannot set colours and fonts before the widget
+ // been realized, so we do this directly after realization
+ // or otherwise in idle time
+
+ if (m_needsStyleChange)
+ {
+ SetBackgroundStyle(GetBackgroundStyle());
+ m_needsStyleChange = false;
+ }
+
+ wxWindowCreateEvent event(static_cast<wxWindow*>(this));
+ event.SetEventObject( this );
+ GTKProcessEvent( event );
+
+ GTKUpdateCursor(true, false);
+}
+
// ----------------------------------------------------------------------------
// this wxWindowBase function is implemented here (in platform-specific file)
// because it is static and so couldn't be made virtual
wxWindow *wxWindowBase::DoFindFocus()
{
+ // For compatibility with wxMSW, pretend that showing a popup menu doesn't
+ // change the focus and that it remains on the window showing it, even
+ // though the real focus does change in GTK.
+ extern wxMenu *wxCurrentPopupMenu;
+ if ( wxCurrentPopupMenu )
+ return wxCurrentPopupMenu->GetInvokingWindow();
+
wxWindowGTK *focus = gs_pendingFocus ? gs_pendingFocus : gs_currentFocus;
// the cast is necessary when we compile in wxUniversal mode
return static_cast<wxWindow*>(focus);
child->m_x += pizza->m_scroll_x;
child->m_y += pizza->m_scroll_y;
- gtk_widget_set_size_request(
- child->m_widget, child->m_width, child->m_height);
- pizza->put(child->m_widget, child->m_x, child->m_y);
+ pizza->put(child->m_widget,
+ child->m_x, child->m_y, child->m_width, child->m_height);
}
//-----------------------------------------------------------------------------
// delete before the widgets to avoid a crash on solaris
delete m_imData;
+ m_imData = NULL;
// avoid problem with GTK+ 2.18 where a frozen window causes the whole
// TLW to be frozen, and if the window is then destroyed, nothing ever
m_width = WidthDefault(size.x) ;
m_height = HeightDefault(size.y);
- m_x = (int)pos.x;
- m_y = (int)pos.y;
+ if (pos != wxDefaultPosition)
+ {
+ m_x = pos.x;
+ m_y = pos.y;
+ }
return true;
}
{
wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+ // Set RGBA visual as soon as possible to minimize the possibility that
+ // somebody uses the wrong one.
+ if ( m_backgroundStyle == wxBG_STYLE_TRANSPARENT &&
+ IsTransparentBackgroundSupported() )
+ {
+ GdkScreen *screen = gtk_widget_get_screen (m_widget);
+
+ GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen);
+
+ if (rgba_colormap)
+ gtk_widget_set_colormap(m_widget, rgba_colormap);
+ }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+
if (m_wxwindow)
{
if (!m_noExpose)
g_signal_connect (m_imData->context, "commit",
G_CALLBACK (gtk_wxwindow_commit_cb), this);
+ g_signal_connect(m_wxwindow, "unrealize", G_CALLBACK(unrealize), this);
}
// focus handling
ConnectWidget( connect_widget );
- /* We cannot set colours, fonts and cursors before the widget has
- been realized, so we do this directly after realization */
- g_signal_connect (connect_widget, "realize",
- G_CALLBACK (gtk_window_realized_callback), this);
+ // We cannot set colours, fonts and cursors before the widget has been
+ // realized, so we do this directly after realization -- unless the widget
+ // was in fact realized already.
+ if ( gtk_widget_get_realized(connect_widget) )
+ {
+ gtk_window_realized_callback(connect_widget, this);
+ }
+ else
+ {
+ g_signal_connect (connect_widget, "realize",
+ G_CALLBACK (gtk_window_realized_callback), this);
+ }
if (!IsTopLevel())
{
}
#endif // GTK+ >= 2.8
- if ( GTKShouldConnectSizeRequest() )
- {
- // This is needed if we want to add our windows into native
- // GTK controls, such as the toolbar. With this callback, the
- // toolbar gets to know the correct size (the one set by the
- // programmer). Sadly, it misbehaves for wxComboBox.
- g_signal_connect (m_widget, "size_request",
- G_CALLBACK (wxgtk_window_size_request_callback),
- this);
- }
+ if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget)) && !GTK_IS_WINDOW(m_widget))
+ gtk_widget_set_size_request(m_widget, m_width, m_height);
InheritAttributes();
gtk_widget_show( m_widget );
}
-unsigned long wxWindowGTK::GTKConnectWidget(const char *signal, void (*callback)())
+unsigned long
+wxWindowGTK::GTKConnectWidget(const char *signal, wxGTKCallback callback)
{
return g_signal_connect(m_widget, signal, callback, this);
}
g_signal_connect (widget, "leave_notify_event",
G_CALLBACK (gtk_window_leave_callback), this);
- if (IsTopLevel() && m_wxwindow)
+ if (m_wxwindow && (IsTopLevel() || HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME)))
g_signal_connect (m_wxwindow, "style_set",
G_CALLBACK (gtk_window_style_set_callback), this);
}
return wxWindowBase::Destroy();
}
+static GSList* gs_queueResizeList;
+
+extern "C" {
+static gboolean queue_resize(void*)
+{
+ gdk_threads_enter();
+ for (GSList* p = gs_queueResizeList; p; p = p->next)
+ {
+ if (p->data)
+ {
+ gtk_widget_queue_resize(GTK_WIDGET(p->data));
+ g_object_remove_weak_pointer(G_OBJECT(p->data), &p->data);
+ }
+ }
+ g_slist_free(gs_queueResizeList);
+ gs_queueResizeList = NULL;
+ gdk_threads_leave();
+ return false;
+}
+}
+
void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
{
gtk_widget_set_size_request(m_widget, width, height);
+ GtkWidget* parent = gtk_widget_get_parent(m_widget);
+ if (WX_IS_PIZZA(parent))
+ WX_PIZZA(parent)->move(m_widget, x, y, width, height);
- // inform the parent to perform the move
- wxASSERT_MSG(m_parent && m_parent->m_wxwindow,
- "the parent window has no client area?");
- WX_PIZZA(m_parent->m_wxwindow)->move(m_widget, x, y);
+ // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
+ // is in progress. This situation is common in wxWidgets, since
+ // size-allocate can generate wxSizeEvent and size event handlers often
+ // call SetSize(), directly or indirectly. Work around this by deferring
+ // the queue-resize until after size-allocate processing is finished.
+ if (g_slist_find(gs_queueResizeList, m_widget) == NULL)
+ {
+ if (gs_queueResizeList == NULL)
+ g_idle_add_full(GTK_PRIORITY_RESIZE, queue_resize, NULL, NULL);
+ gs_queueResizeList = g_slist_prepend(gs_queueResizeList, m_widget);
+ g_object_add_weak_pointer(G_OBJECT(m_widget), &gs_queueResizeList->data);
+ }
}
void wxWindowGTK::ConstrainSize()
void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
{
- wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
- wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
+ wxCHECK_RET(m_widget, "invalid window");
- int currentX, currentY;
- GetPosition(¤tX, ¤tY);
- if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
- x = currentX;
- if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
- y = currentY;
- AdjustForParentClientOrigin(x, y, sizeFlags);
+ int scrollX = 0, scrollY = 0;
+ GtkWidget* parent = gtk_widget_get_parent(m_widget);
+ if (WX_IS_PIZZA(parent))
+ {
+ wxPizza* pizza = WX_PIZZA(parent);
+ scrollX = pizza->m_scroll_x;
+ scrollY = pizza->m_scroll_y;
+ }
+ if (x != -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
+ x += scrollX;
+ else
+ x = m_x;
+ if (y != -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
+ y += scrollY;
+ else
+ y = m_y;
// calculate the best size if we should auto size the window
if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
height = sizeBest.y;
}
- const wxSize oldSize(m_width, m_height);
- if (width != -1)
- m_width = width;
- if (height != -1)
- m_height = height;
+ if (width == -1)
+ width = m_width;
+ if (height == -1)
+ height = m_height;
- if (m_parent->m_wxwindow)
+ const bool sizeChange = m_width != width || m_height != height;
+ if (sizeChange || m_x != x || m_y != y)
{
- wxPizza* pizza = WX_PIZZA(m_parent->m_wxwindow);
- m_x = x + pizza->m_scroll_x;
- m_y = y + pizza->m_scroll_y;
-
- int left_border = 0;
- int right_border = 0;
- int top_border = 0;
- int bottom_border = 0;
+ m_x = x;
+ m_y = y;
+ m_width = width;
+ m_height = height;
/* the default button has a border around it */
if (gtk_widget_get_can_default(m_widget))
gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
if (default_border)
{
- left_border += default_border->left;
- right_border += default_border->right;
- top_border += default_border->top;
- bottom_border += default_border->bottom;
+ x -= default_border->left;
+ y -= default_border->top;
+ width += default_border->left + default_border->right;
+ height += default_border->top + default_border->bottom;
gtk_border_free( default_border );
}
}
- DoMoveWindow( m_x - left_border,
- m_y - top_border,
- m_width+left_border+right_border,
- m_height+top_border+bottom_border );
+ DoMoveWindow(x, y, width, height);
}
- if (m_width != oldSize.x || m_height != oldSize.y)
+ if ((sizeChange && !m_nativeSizeEvent) || (sizeFlags & wxSIZE_FORCE_EVENT))
{
// update these variables to keep size_allocate handler
// from sending another size event for this change
GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
- gtk_widget_queue_resize(m_widget);
- if (!m_nativeSizeEvent)
- {
- wxSizeEvent event( wxSize(m_width,m_height), GetId() );
- event.SetEventObject( this );
- HandleWindowEvent( event );
- }
- } else
- if (sizeFlags & wxSIZE_FORCE_EVENT)
- {
wxSizeEvent event( wxSize(m_width,m_height), GetId() );
event.SetEventObject( this );
HandleWindowEvent( event );
RealizeTabOrder();
}
- // Update style if the window was not yet realized when
- // SetBackgroundStyle() was called
- if (m_needsStyleChange)
- {
- SetBackgroundStyle(GetBackgroundStyle());
- m_needsStyleChange = false;
- }
-
wxWindowBase::OnInternalIdle();
}
void wxWindowGTK::DoGetSize( int *width, int *height ) const
{
- wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
-
if (width) (*width) = m_width;
if (height) (*height) = m_height;
}
void wxWindowGTK::DoGetPosition( int *x, int *y ) const
{
- wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
-
int dx = 0;
int dy = 0;
- if (!IsTopLevel() && m_parent && m_parent->m_wxwindow)
+ GtkWidget* parent = NULL;
+ if (m_widget)
+ parent = gtk_widget_get_parent(m_widget);
+ if (WX_IS_PIZZA(parent))
{
- wxPizza* pizza = WX_PIZZA(m_parent->m_wxwindow);
+ wxPizza* pizza = WX_PIZZA(parent);
dx = pizza->m_scroll_x;
dy = pizza->m_scroll_y;
}
-
- if (m_x == -1 && m_y == -1)
- {
- GdkWindow *source = NULL;
- if (m_wxwindow)
- source = gtk_widget_get_window(m_wxwindow);
- else
- source = gtk_widget_get_window(m_widget);
-
- if (source)
- {
- int org_x = 0;
- int org_y = 0;
- gdk_window_get_origin( source, &org_x, &org_y );
-
- if (m_parent)
- m_parent->ScreenToClient(&org_x, &org_y);
-
- const_cast<wxWindowGTK*>(this)->m_x = org_x;
- const_cast<wxWindowGTK*>(this)->m_y = org_y;
- }
- }
-
if (x) (*x) = m_x - dx;
if (y) (*y) = m_y - dy;
}
wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
event.SetEventObject( this );
+ event.SetWindow( FindFocus() );
GTKProcessEvent( event );
}
gboolean rc;
g_signal_emit_by_name(parent->m_widget, "focus", dir, &rc);
- return rc == TRUE;
+ return rc != 0;
}
}
{
wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
- // We provide this function ourselves as it is
- // missing in GDK (top of this file).
-
- GdkWindow *window = NULL;
- if (m_wxwindow)
- window = gtk_widget_get_window(m_wxwindow);
- else
- window = gtk_widget_get_window(GetConnectWidget());
-
- if (window)
- gdk_window_warp_pointer( window, x, y );
+ ClientToScreen(&x, &y);
+ GdkDisplay* display = gtk_widget_get_display(m_widget);
+ GdkScreen* screen = gtk_widget_get_screen(m_widget);
+#ifdef __WXGTK30__
+ GdkDeviceManager* manager = gdk_display_get_device_manager(display);
+ gdk_device_warp(gdk_device_manager_get_client_pointer(manager), screen, x, y);
+#else
+#ifdef GDK_WINDOWING_X11
+ XWarpPointer(GDK_DISPLAY_XDISPLAY(display),
+ None,
+ GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
+ 0, 0, 0, 0, x, y);
+#endif
+#endif
}
wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground),
const wxRect *rect)
{
- if ( !m_widget )
- {
- // it is valid to call Refresh() for a window which hasn't been created
- // yet, it simply doesn't do anything in this case
- return;
- }
-
- if (!m_wxwindow)
+ if (m_wxwindow)
{
- if (rect)
- gtk_widget_queue_draw_area( m_widget, rect->x, rect->y, rect->width, rect->height );
- else
- gtk_widget_queue_draw( m_widget );
+ if (gtk_widget_get_mapped(m_wxwindow))
+ {
+ GdkWindow* window = gtk_widget_get_window(m_wxwindow);
+ if (rect)
+ {
+ GdkRectangle r = { rect->x, rect->y, rect->width, rect->height };
+ if (GetLayoutDirection() == wxLayout_RightToLeft)
+ r.x = gdk_window_get_width(window) - r.x - rect->width;
+ gdk_window_invalidate_rect(window, &r, true);
+ }
+ else
+ gdk_window_invalidate_rect(window, NULL, true);
+ }
}
- else
+ else if (m_widget)
{
- // Just return if the widget or one of its ancestors isn't mapped
- GtkWidget *w;
- for (w = m_wxwindow; w != NULL; w = gtk_widget_get_parent(w))
- if (!gtk_widget_get_mapped (w))
- return;
-
- GdkWindow* window = GTKGetDrawingWindow();
- if (rect)
+ if (gtk_widget_get_mapped(m_widget))
{
- int x = rect->x;
- if (GetLayoutDirection() == wxLayout_RightToLeft)
- x = GetClientSize().x - x - rect->width;
- GdkRectangle r;
- r.x = rect->x;
- r.y = rect->y;
- r.width = rect->width;
- r.height = rect->height;
- gdk_window_invalidate_rect(window, &r, true);
+ if (rect)
+ gtk_widget_queue_draw_area(m_widget, rect->x, rect->y, rect->width, rect->height);
+ else
+ gtk_widget_queue_draw(m_widget);
}
- else
- gdk_window_invalidate_rect(window, NULL, true);
}
}
m_updateRegion.Clear();
return;
}
-
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+ cairo_t* cr = NULL;
+#endif
// Clip to paint region in wxClientDC
m_clipPaintRegion = true;
switch ( GetBackgroundStyle() )
{
+ case wxBG_STYLE_TRANSPARENT:
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+ if (IsTransparentBackgroundSupported())
+ {
+ // Set a transparent background, so that overlaying in parent
+ // might indeed let see through where this child did not
+ // explicitly paint.
+ // NB: it works also for top level windows (but this is the
+ // windows manager which then does the compositing job)
+ cr = gdk_cairo_create(m_wxwindow->window);
+ gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
+ cairo_clip(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_surface_flush(cairo_get_target(cr));
+ }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+ break;
+
case wxBG_STYLE_ERASE:
{
wxWindowDC dc( (wxWindow*)this );
paint_event.SetEventObject( this );
HandleWindowEvent( paint_event );
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+ if (IsTransparentBackgroundSupported())
+ { // now composite children which need it
+ // Overlay all our composite children on top of the painted area
+ wxWindowList::compatibility_iterator node;
+ for ( node = m_children.GetFirst(); node ; node = node->GetNext() )
+ {
+ wxWindow *compositeChild = node->GetData();
+ if (compositeChild->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT)
+ {
+ if (cr == NULL)
+ {
+ cr = gdk_cairo_create(m_wxwindow->window);
+ gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
+ cairo_clip(cr);
+ }
+
+ GtkWidget *child = compositeChild->m_wxwindow;
+ GtkAllocation alloc;
+ gtk_widget_get_allocation(child, &alloc);
+
+ // The source data is the (composited) child
+ gdk_cairo_set_source_window(
+ cr, gtk_widget_get_window(child), alloc.x, alloc.y);
+
+ cairo_paint(cr);
+ }
+ }
+ if (cr)
+ cairo_destroy(cr);
+ }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+
m_clipPaintRegion = false;
m_updateRegion.Clear();
bool wxWindowGTK::IsDoubleBuffered() const
{
- return gtk_widget_get_double_buffered( m_wxwindow );
+ return gtk_widget_get_double_buffered( m_wxwindow ) != 0;
}
void wxWindowGTK::ClearBackground()
bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
{
- wxWindowBase::SetBackgroundStyle(style);
+ if (!wxWindowBase::SetBackgroundStyle(style))
+ return false;
- if ( style == wxBG_STYLE_PAINT )
+ GdkWindow *window;
+ if ( m_wxwindow )
{
- GdkWindow *window;
- if ( m_wxwindow )
- {
- window = GTKGetDrawingWindow();
- }
- else
- {
- GtkWidget * const w = GetConnectWidget();
- window = w ? gtk_widget_get_window(w) : NULL;
- }
+ window = GTKGetDrawingWindow();
+ }
+ else
+ {
+ GtkWidget * const w = GetConnectWidget();
+ window = w ? gtk_widget_get_window(w) : NULL;
+ }
+ bool wantNoBackPixmap = style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT;
+
+ if ( wantNoBackPixmap )
+ {
if (window)
{
// Make sure GDK/X11 doesn't refresh the window
// automatically.
- gdk_window_set_back_pixmap( window, None, False );
-#ifdef __X__
- Display* display = GDK_WINDOW_DISPLAY(window);
- XFlush(display);
-#endif
+ gdk_window_set_back_pixmap( window, NULL, FALSE );
m_needsStyleChange = false;
}
else // window not realized yet
{
- // Do in OnIdle, because the window is not yet available
+ // Do when window is realized
m_needsStyleChange = true;
}
return true;
}
+bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const
+{
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+ if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING) != NULL)
+ {
+ if (reason)
+ {
+ *reason = _("GTK+ installed on this machine is too old to "
+ "support screen compositing, please install "
+ "GTK+ 2.12 or later.");
+ }
+
+ return false;
+ }
+
+ // NB: We don't check here if the particular kind of widget supports
+ // transparency, we check only if it would be possible for a generic window
+
+ wxCHECK_MSG ( m_widget, false, "Window must be created first" );
+
+ if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget)))
+ {
+ if (reason)
+ {
+ *reason = _("Compositing not supported by this system, "
+ "please enable it in your Window Manager.");
+ }
+
+ return false;
+ }
+
+ return true;
+#else
+ if (reason)
+ {
+ *reason = _("This program was compiled with a too old version of GTK+, "
+ "please rebuild with GTK+ 2.12 or newer.");
+ }
+
+ return false;
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
+}
+
// ----------------------------------------------------------------------------
// Pop-up menu stuff
// ----------------------------------------------------------------------------
}
}
-void wxWindowGTK::SetWindowStyleFlag( long style )
-{
- // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
- wxWindowBase::SetWindowStyleFlag(style);
-}
-
// Find the wxWindow at the current mouse position, also returning the mouse
// position.
wxWindow* wxFindWindowAtPointer(wxPoint& pt)
// Get the current mouse position.
wxPoint wxGetMousePosition()
{
- /* This crashes when used within wxHelpContext,
- so we have to use the X-specific implementation below.
- gint x, y;
- GdkModifierType *mask;
- (void) gdk_window_get_pointer(NULL, &x, &y, mask);
-
- return wxPoint(x, y);
- */
+ wxWindow* tlw = NULL;
+ if (!wxTopLevelWindows.empty())
+ tlw = wxTopLevelWindows.front();
+ GdkDisplay* display;
+ if (tlw && tlw->m_widget)
+ display = gtk_widget_get_display(tlw->m_widget);
+ else
+ display = gdk_display_get_default();
int x, y;
- GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
-
- Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
- Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
- Window rootReturn, childReturn;
- int rootX, rootY, winX, winY;
- unsigned int maskReturn;
-
- XQueryPointer (display,
- rootWindow,
- &rootReturn,
- &childReturn,
- &rootX, &rootY, &winX, &winY, &maskReturn);
- return wxPoint(rootX, rootY);
-
+ gdk_display_get_pointer(display, NULL, &x, &y, NULL);
+ return wxPoint(x, y);
}
GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const