#include "wx/tooltip.h"
#include "wx/caret.h"
#include "wx/fontutil.h"
-#include "wx/scopeguard.h"
#include "wx/sysopt.h"
+#ifdef __WXGTK3__
+ #include "wx/gtk/dc.h"
+#endif
#include <ctype.h>
+#include <gtk/gtk.h>
#include "wx/gtk/private.h"
-#include "wx/gtk/private/win_gtk.h"
+#include "wx/gtk/private/gtk2-compat.h"
#include "wx/gtk/private/event.h"
+#include "wx/gtk/private/win_gtk.h"
+#include "wx/private/textmeasure.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)
+#ifdef __WXGTK3__
#include <gdk/gdkkeysyms-compat.h>
#endif
// the trace mask used for the focus debugging messages
#define TRACE_FOCUS wxT("focus")
-//-----------------------------------------------------------------------------
-// "size_request" of m_widget
-//-----------------------------------------------------------------------------
-
-extern "C" {
-static void
-wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget),
- GtkRequisition *requisition,
- wxWindow * win)
+// A handy function to run from under gdb to show information about the given
+// GtkWidget. Right now it only shows its type, we could enhance it to show
+// more information later but this is already pretty useful.
+const char* wxDumpGtkWidget(GtkWidget* w)
{
- int w, h;
- win->GetSize( &w, &h );
- if (w < 2)
- w = 2;
- if (h < 2)
- h = 2;
+ static wxString s;
+ s.Printf("GtkWidget %p, type \"%s\"", w, G_OBJECT_TYPE_NAME(w));
- requisition->height = h;
- requisition->width = w;
-}
+ return s.c_str();
}
//-----------------------------------------------------------------------------
-// "expose_event" of m_wxwindow
+// "expose_event"/"draw" from m_wxwindow
//-----------------------------------------------------------------------------
extern "C" {
-static gboolean
-gtk_window_expose_callback( GtkWidget*,
- GdkEventExpose *gdk_event,
- wxWindow *win )
+#ifdef __WXGTK3__
+static gboolean draw(GtkWidget*, cairo_t* cr, wxWindow* win)
+{
+ if (gtk_cairo_should_draw_window(cr, win->GTKGetDrawingWindow()))
+ win->GTKSendPaintEvents(cr);
+
+ return false;
+}
+#else // !__WXGTK3__
+static gboolean expose_event(GtkWidget*, GdkEventExpose* gdk_event, wxWindow* win)
{
if (gdk_event->window == win->GTKGetDrawingWindow())
- {
- win->GetUpdateRegion() = wxRegion( gdk_event->region );
- win->GtkSendPaintEvents();
- }
- // Let parent window draw window-less widgets
- return FALSE;
+ win->GTKSendPaintEvents(gdk_event->region);
+
+ return false;
}
+#endif // !__WXGTK3__
}
#ifndef __WXUNIVERSAL__
//-----------------------------------------------------------------------------
-// "expose_event" from m_wxwindow->parent, for drawing border
+// "expose_event"/"draw" from m_wxwindow->parent, for drawing border
//-----------------------------------------------------------------------------
extern "C" {
static gboolean
-expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
+#ifdef __WXGTK3__
+draw_border(GtkWidget*, cairo_t* cr, wxWindow* win)
+#else
+draw_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
+#endif
{
+#ifdef __WXGTK3__
+ if (!gtk_cairo_should_draw_window(cr, gtk_widget_get_parent_window(win->m_wxwindow)))
+#else
if (gdk_event->window != gtk_widget_get_parent_window(win->m_wxwindow))
+#endif
return false;
if (!win->IsShown())
if (win->HasFlag(wxBORDER_SIMPLE))
{
+#ifdef __WXGTK3__
+ GtkStyleContext* sc = gtk_widget_get_style_context(win->m_wxwindow);
+ GdkRGBA c;
+ gtk_style_context_get_border_color(sc, GTK_STATE_FLAG_NORMAL, &c);
+ gdk_cairo_set_source_rgba(cr, &c);
+ cairo_set_line_width(cr, 1);
+ cairo_rectangle(cr, x + 0.5, y + 0.5, w - 1, h - 1);
+ cairo_stroke(cr);
+#else
gdk_draw_rectangle(gdk_event->window,
gtk_widget_get_style(widget)->black_gc, false, x, y, w - 1, h - 1);
+#endif
}
- else
+ else if (win->HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME))
{
+#ifdef __WXGTK3__
+ //TODO: wxBORDER_RAISED/wxBORDER_SUNKEN
+ GtkStyleContext* sc;
+ if (win->HasFlag(wxHSCROLL | wxVSCROLL))
+ sc = gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
+ else
+ sc = gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
+
+ gtk_render_frame(sc, cr, x, y, w, h);
+#else // !__WXGTK3__
GtkShadowType shadow = GTK_SHADOW_IN;
if (win->HasFlag(wxBORDER_RAISED))
shadow = GTK_SHADOW_OUT;
- // Style detail to use
+ GtkStyle* style;
const char* detail;
- if (win->m_widget == win->m_wxwindow)
- // for non-scrollable wxWindows
- detail = "entry";
- else
- // for scrollable ones
+ if (win->HasFlag(wxHSCROLL | wxVSCROLL))
+ {
+ style = gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
detail = "viewport";
+ }
+ else
+ {
+ style = gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
+ detail = "entry";
+ }
// clip rect is required to avoid painting background
// over upper left (w,h) of parent window
GdkRectangle clipRect = { x, y, w, h };
gtk_paint_shadow(
- gtk_widget_get_style(win->m_wxwindow), gdk_event->window, GTK_STATE_NORMAL,
- shadow, &clipRect, wxGTKPrivate::GetEntryWidget(), detail, x, y, w, h);
+ style, gdk_event->window, GTK_STATE_NORMAL,
+ shadow, &clipRect, widget, detail, x, y, w, h);
+#endif // !__WXGTK3__
}
return false;
}
if (old_parent)
{
g_signal_handlers_disconnect_by_func(
- old_parent, (void*)expose_event_border, win);
+ old_parent, (void*)draw_border, win);
}
GtkWidget* parent = gtk_widget_get_parent(widget);
if (parent)
{
- g_signal_connect_after(parent, "expose_event",
- G_CALLBACK(expose_event_border), win);
+#ifdef __WXGTK3__
+ g_signal_connect_after(parent, "draw", G_CALLBACK(draw_border), win);
+#else
+ g_signal_connect_after(parent, "expose_event", G_CALLBACK(draw_border), win);
+#endif
}
}
}
event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
+ // At least with current Linux systems, MOD5 corresponds to AltGr key and
+ // we represent it, for consistency with Windows, which really allows to
+ // use Ctrl+Alt as a replacement for AltGr if this key is not present, as a
+ // combination of these two modifiers.
+ if ( gdk_event->state & GDK_MOD5_MASK )
+ {
+ event.m_controlDown =
+ event.m_altDown = true;
+ }
+
// Normally we take the state of modifiers directly from the low level GDK
// event but unfortunately GDK uses a different convention from MSW for the
// key events corresponding to the modifier keys themselves: in it, when
event.m_rawCode = (wxUint32) gdk_event->keyval;
event.m_rawFlags = gdk_event->hardware_keycode;
- wxGetMousePosition(&event.m_x, &event.m_y);
- win->ScreenToClient(&event.m_x, &event.m_y);
event.SetEventObject( win );
}
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
} // 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),
GdkEventKey *gdk_event,
wxWindow *win )
{
- if (!win->m_hasVMT)
- return FALSE;
if (g_blockEventsOnDrag)
return FALSE;
+ wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
+
wxKeyEvent event( wxEVT_KEY_DOWN );
bool ret = false;
bool return_after_IM = false;
return TRUE;
}
- // Emit KEY_DOWN event
- ret = win->HandleWindowEvent( event );
- }
- else
- {
- // Return after IM processing as we cannot do
- // anything with it anyhow.
- return_after_IM = true;
- }
-
- if (!ret && win->m_imData)
- {
- win->m_imData->lastKeyEvent = gdk_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);
- win->m_imData->lastKeyEvent = NULL;
- if (intercepted_by_IM)
- {
- wxLogTrace(TRACE_KEYS, wxT("Key event intercepted by IM"));
- return TRUE;
- }
- }
-
- if (return_after_IM)
- return FALSE;
-
+ // Next check for accelerators.
#if wxUSE_ACCEL
- if (!ret)
- {
wxWindowGTK *ancestor = win;
while (ancestor)
{
break;
ancestor = ancestor->GetParent();
}
- }
#endif // wxUSE_ACCEL
+ // If not an accelerator, then emit KEY_DOWN event
+ if ( !ret )
+ ret = win->HandleWindowEvent( event );
+ }
+ else
+ {
+ // Return after IM processing as we cannot do
+ // anything with it anyhow.
+ return_after_IM = true;
+ }
+
+ if (!ret && win->m_imData)
+ {
+ win->m_imData->lastKeyEvent = gdk_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) != 0;
+ win->m_imData->lastKeyEvent = NULL;
+ if (intercepted_by_IM)
+ {
+ wxLogTrace(TRACE_KEYS, wxT("Key event intercepted by IM"));
+ return TRUE;
+ }
+ }
+
+ if (return_after_IM)
+ return FALSE;
+
// Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
// will only be sent if it is not in an accelerator table.
if (!ret)
{
- long key_code;
KeySym keysym = gdk_event->keyval;
// Find key code for EVT_CHAR and EVT_CHAR_HOOK events
- key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
+ long key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
if ( !key_code )
{
if ( wxIsAsciiKeysym(keysym) )
wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
eventChar.m_keyCode = key_code;
+#if wxUSE_UNICODE
+ eventChar.m_uniChar = gdk_keyval_to_unicode(key_code);
+#endif // wxUSE_UNICODE
AdjustCharEventKeyCodes(eventChar);
GdkEventKey *gdk_event,
wxWindowGTK *win )
{
- if (!win->m_hasVMT)
- return FALSE;
-
if (g_blockEventsOnDrag)
return FALSE;
+ wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
+
wxKeyEvent event( wxEVT_KEY_UP );
if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
{
}
}
-//-----------------------------------------------------------------------------
-// key and mouse events, after, from m_widget
-//-----------------------------------------------------------------------------
-
-extern "C" {
-static gboolean key_and_mouse_event_after(GtkWidget* widget, GdkEventKey*, wxWindow*)
-{
- // 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 prevent it unless widget is in a native
- // container.
- return WX_IS_PIZZA(gtk_widget_get_parent(widget));
-}
-}
-
// ============================================================================
// the mouse events
// ============================================================================
}
}
-// 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) &&
bool wxWindowGTK::GTKShouldIgnoreEvent() const
{
- return !m_hasVMT || g_blockEventsOnDrag;
+ return g_blockEventsOnDrag;
}
int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
{
- if (!m_hasVMT)
- return FALSE;
if (g_blockEventsOnDrag)
return TRUE;
if (g_blockEventsOnScroll)
//-----------------------------------------------------------------------------
static gboolean
-gtk_window_button_press_callback( GtkWidget *widget,
+gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget),
GdkEventButton *gdk_event,
wxWindowGTK *win )
{
+ wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
+
wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
g_lastButtonNumber = gdk_event->button;
- // GDK sends surplus button down events
- // before a double click event. We
- // need to filter these out.
- if ((gdk_event->type == GDK_BUTTON_PRESS) && (win->m_wxwindow))
+ wxEventType event_type;
+ wxEventType down;
+ wxEventType dclick;
+ switch (gdk_event->button)
{
- GdkEvent *peek_event = gdk_event_peek();
- if (peek_event)
- {
- if ((peek_event->type == GDK_2BUTTON_PRESS) ||
- (peek_event->type == GDK_3BUTTON_PRESS))
+ case 1:
+ down = wxEVT_LEFT_DOWN;
+ dclick = wxEVT_LEFT_DCLICK;
+ break;
+ case 2:
+ down = wxEVT_MIDDLE_DOWN;
+ dclick = wxEVT_MIDDLE_DCLICK;
+ break;
+ case 3:
+ down = wxEVT_RIGHT_DOWN;
+ dclick = wxEVT_RIGHT_DCLICK;
+ break;
+ case 8:
+ down = wxEVT_AUX1_DOWN;
+ dclick = wxEVT_AUX1_DCLICK;
+ break;
+ case 9:
+ down = wxEVT_AUX2_DOWN;
+ dclick = wxEVT_AUX2_DCLICK;
+ break;
+ default:
+ return false;
+ }
+ switch (gdk_event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ event_type = down;
+ // GDK sends surplus button down events
+ // before a double click event. We
+ // need to filter these out.
+ if (win->m_wxwindow)
{
- gdk_event_free( peek_event );
- return TRUE;
+ GdkEvent* peek_event = gdk_event_peek();
+ if (peek_event)
+ {
+ const GdkEventType peek_event_type = peek_event->type;
+ gdk_event_free(peek_event);
+ if (peek_event_type == GDK_2BUTTON_PRESS ||
+ peek_event_type == GDK_3BUTTON_PRESS)
+ {
+ return true;
+ }
+ }
}
- else
+ break;
+ case GDK_2BUTTON_PRESS:
+ event_type = dclick;
+#ifndef __WXGTK3__
+ if (gdk_event->button >= 1 && gdk_event->button <= 3)
{
- gdk_event_free( peek_event );
+ // Reset GDK internal timestamp variables in order to disable GDK
+ // triple click events. GDK will then next time believe no button has
+ // been clicked just before, and send a normal button click event.
+ GdkDisplay* display = gtk_widget_get_display(widget);
+ display->button_click_time[1] = 0;
+ display->button_click_time[0] = 0;
}
- }
- }
-
- wxEventType event_type = wxEVT_NULL;
-
- if ( gdk_event->type == GDK_2BUTTON_PRESS &&
- gdk_event->button >= 1 && gdk_event->button <= 3 )
- {
- // Reset GDK internal timestamp variables in order to disable GDK
- // triple click events. GDK will then next time believe no button has
- // been clicked just before, and send a normal button click event.
- GdkDisplay* display = gtk_widget_get_display (widget);
- display->button_click_time[1] = 0;
- display->button_click_time[0] = 0;
- }
-
- if (gdk_event->button == 1)
- {
- // note that GDK generates triple click events which are not supported
- // by wxWidgets but still have to be passed to the app as otherwise
- // clicks would simply go missing
- switch (gdk_event->type)
- {
- // we shouldn't get triple clicks at all for GTK2 because we
- // suppress them artificially using the code above but we still
- // should map them to something for GTK1 and not just ignore them
- // as this would lose clicks
- case GDK_3BUTTON_PRESS: // we could also map this to DCLICK...
- case GDK_BUTTON_PRESS:
- event_type = wxEVT_LEFT_DOWN;
- break;
-
- case GDK_2BUTTON_PRESS:
- event_type = wxEVT_LEFT_DCLICK;
- break;
-
- default:
- // just to silence gcc warnings
- ;
- }
- }
- else if (gdk_event->button == 2)
- {
- switch (gdk_event->type)
- {
- case GDK_3BUTTON_PRESS:
- case GDK_BUTTON_PRESS:
- event_type = wxEVT_MIDDLE_DOWN;
- break;
-
- case GDK_2BUTTON_PRESS:
- event_type = wxEVT_MIDDLE_DCLICK;
- break;
-
- default:
- ;
- }
- }
- else if (gdk_event->button == 3)
- {
- switch (gdk_event->type)
- {
- case GDK_3BUTTON_PRESS:
- case GDK_BUTTON_PRESS:
- event_type = wxEVT_RIGHT_DOWN;
- break;
-
- case GDK_2BUTTON_PRESS:
- event_type = wxEVT_RIGHT_DCLICK;
- break;
-
- default:
- ;
- }
- }
-
- else if (gdk_event->button == 8)
- {
- switch (gdk_event->type)
- {
- case GDK_3BUTTON_PRESS:
- case GDK_BUTTON_PRESS:
- event_type = wxEVT_AUX1_DOWN;
- break;
-
- case GDK_2BUTTON_PRESS:
- event_type = wxEVT_AUX1_DCLICK;
- break;
-
- default:
- ;
- }
- }
-
- else if (gdk_event->button == 9)
- {
- switch (gdk_event->type)
- {
- case GDK_3BUTTON_PRESS:
- case GDK_BUTTON_PRESS:
- event_type = wxEVT_AUX2_DOWN;
- break;
-
- case GDK_2BUTTON_PRESS:
- event_type = wxEVT_AUX2_DCLICK;
- break;
-
- default:
- ;
- }
- }
-
- if ( event_type == wxEVT_NULL )
- {
- // unknown mouse button or click type
- return FALSE;
+#endif // !__WXGTK3__
+ break;
+ // we shouldn't get triple clicks at all for GTK2 because we
+ // suppress them artificially using the code above but we still
+ // should map them to something for GTK3 and not just ignore them
+ // as this would lose clicks
+ case GDK_3BUTTON_PRESS:
+ event_type = down;
+ break;
+ default:
+ return false;
}
g_lastMouseEvent = (GdkEvent*) gdk_event;
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;
win->GTKHandleRealized();
}
-//-----------------------------------------------------------------------------
-// "unrealize" from m_wxwindow
-//-----------------------------------------------------------------------------
-
-static void unrealize(GtkWidget*, wxWindowGTK* win)
-{
- if (win->m_imData)
- gtk_im_context_set_client_window(win->m_imData->context, NULL);
-}
-
//-----------------------------------------------------------------------------
// "size_allocate" from m_wxwindow or m_widget
//-----------------------------------------------------------------------------
int h = alloc->height;
if (win->m_wxwindow)
{
- int border_x, border_y;
- WX_PIZZA(win->m_wxwindow)->get_border_widths(border_x, border_y);
- w -= 2 * border_x;
- h -= 2 * border_y;
+ GtkBorder border;
+ WX_PIZZA(win->m_wxwindow)->get_border(border);
+ w -= border.left + border.right;
+ h -= border.top + border.bottom;
if (w < 0) w = 0;
if (h < 0) h = 0;
}
#endif
//-----------------------------------------------------------------------------
-// "style_set"
+// "style_set"/"style_updated"
//-----------------------------------------------------------------------------
-static
-void gtk_window_style_set_callback( GtkWidget *WXUNUSED(widget),
- GtkStyle *previous_style,
- wxWindow* win )
+#ifdef __WXGTK3__
+static void style_updated(GtkWidget*, wxWindow* win)
+#else
+static void style_updated(GtkWidget*, GtkStyle*, wxWindow* win)
+#endif
{
- if (win && previous_style)
+ if (win->IsTopLevel())
{
- 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;
- }
+ 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;
}
}
+//-----------------------------------------------------------------------------
+// "unrealize"
+//-----------------------------------------------------------------------------
+
+static void unrealize(GtkWidget*, wxWindow* win)
+{
+ win->GTKHandleUnrealize();
+}
+
} // extern "C"
void wxWindowGTK::GTKHandleRealized()
{
+ if (IsFrozen())
+ DoFreeze();
+
if (m_imData)
{
gtk_im_context_set_client_window
GTKProcessEvent( event );
GTKUpdateCursor(true, false);
+
+ if (m_wxwindow &&
+ (IsTopLevel() || HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME)))
+ {
+ // attaching to style changed signal after realization avoids initial
+ // changes we don't care about
+ const gchar *detailed_signal =
+#ifdef __WXGTK3__
+ "style_updated";
+#else
+ "style_set";
+#endif
+ g_signal_connect(m_wxwindow,
+ detailed_signal,
+ G_CALLBACK(style_updated), this);
+ }
+}
+
+void wxWindowGTK::GTKHandleUnrealize()
+{
+ // unrealizing a frozen window seems to have some lingering effect
+ // preventing updates to the affected area
+ if (IsFrozen())
+ DoThaw();
+
+ if (m_wxwindow)
+ {
+ if (m_imData)
+ gtk_im_context_set_client_window(m_imData->context, NULL);
+
+ g_signal_handlers_disconnect_by_func(
+ m_wxwindow, (void*)style_updated, this);
+ }
}
// ----------------------------------------------------------------------------
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);
}
//-----------------------------------------------------------------------------
}
+// Under Unix this is implemented using X11 functions in utilsx11.cpp but we
+// need to have this function under Windows too, so provide at least a stub.
+#ifndef GDK_WINDOWING_X11
+bool wxGetKeyState(wxKeyCode WXUNUSED(key))
+{
+ wxFAIL_MSG(wxS("Not implemented under Windows"));
+ return false;
+}
+#endif // __WINDOWS__
+
+static void GetMouseState(int& x, int& y, GdkModifierType& mask)
+{
+ 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();
+ gdk_display_get_pointer(display, NULL, &x, &y, &mask);
+}
+
wxMouseState wxGetMouseState()
{
wxMouseState ms;
gint y;
GdkModifierType mask;
- gdk_window_get_pointer(NULL, &x, &y, &mask);
+ GetMouseState(x, y, mask);
ms.SetX(x);
ms.SetY(y);
m_width = 0;
m_height = 0;
- m_hasVMT = false;
-
m_showOnIdle = false;
m_noExpose = false;
m_nativeSizeEvent = false;
+#ifdef __WXGTK3__
+ m_paintContext = NULL;
+#endif
m_isScrolling = false;
m_mouseButtonDown = false;
Create( parent, id, pos, size, style, name );
}
+void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget* view)
+{
+ wxASSERT_MSG( HasFlag(wxHSCROLL) || HasFlag(wxVSCROLL),
+ wxS("Must not be called if scrolling is not needed.") );
+
+ m_widget = gtk_scrolled_window_new( NULL, NULL );
+
+ GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
+
+ // There is a conflict with default bindings at GTK+
+ // level between scrolled windows and notebooks both of which want to use
+ // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
+ // direction and notebooks for changing pages -- we decide that if we don't
+ // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
+ // means we can get working keyboard navigation in notebooks
+ if ( !HasFlag(wxHSCROLL) )
+ {
+ GtkBindingSet *
+ bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
+ if ( bindings )
+ {
+ gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
+ gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
+ }
+ }
+
+ if (HasFlag(wxALWAYS_SHOW_SB))
+ {
+ gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
+ }
+ else
+ {
+ gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+ }
+
+ m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
+ m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
+ if (GetLayoutDirection() == wxLayout_RightToLeft)
+ gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
+
+ gtk_container_add( GTK_CONTAINER(m_widget), view );
+
+ // connect various scroll-related events
+ for ( int dir = 0; dir < ScrollDir_Max; dir++ )
+ {
+ // these handlers block mouse events to any window during scrolling
+ // such as motion events and prevent GTK and wxWidgets from fighting
+ // over where the slider should be
+ g_signal_connect(m_scrollBar[dir], "button_press_event",
+ G_CALLBACK(gtk_scrollbar_button_press_event), this);
+ g_signal_connect(m_scrollBar[dir], "button_release_event",
+ G_CALLBACK(gtk_scrollbar_button_release_event), this);
+
+ gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
+ G_CALLBACK(gtk_scrollbar_event_after), this);
+ g_signal_handler_block(m_scrollBar[dir], handler_id);
+
+ // these handlers get notified when scrollbar slider moves
+ g_signal_connect_after(m_scrollBar[dir], "value_changed",
+ G_CALLBACK(gtk_scrollbar_value_changed), this);
+ }
+
+ gtk_widget_show( view );
+}
+
bool wxWindowGTK::Create( wxWindow *parent,
wxWindowID id,
const wxPoint &pos,
if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
m_widget = m_wxwindow;
else
- {
- m_widget = gtk_scrolled_window_new( NULL, NULL );
-
- GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
-
- // There is a conflict with default bindings at GTK+
- // level between scrolled windows and notebooks both of which want to use
- // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
- // direction and notebooks for changing pages -- we decide that if we don't
- // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
- // means we can get working keyboard navigation in notebooks
- if ( !HasFlag(wxHSCROLL) )
- {
- GtkBindingSet *
- bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
- if ( bindings )
- {
- gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
- gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
- }
- }
-
- if (HasFlag(wxALWAYS_SHOW_SB))
- {
- gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
- }
- else
- {
- gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
- }
-
- m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
- m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
- if (GetLayoutDirection() == wxLayout_RightToLeft)
- gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
-
- gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
-
- // connect various scroll-related events
- for ( int dir = 0; dir < ScrollDir_Max; dir++ )
- {
- // these handlers block mouse events to any window during scrolling
- // such as motion events and prevent GTK and wxWidgets from fighting
- // over where the slider should be
- g_signal_connect(m_scrollBar[dir], "button_press_event",
- G_CALLBACK(gtk_scrollbar_button_press_event), this);
- g_signal_connect(m_scrollBar[dir], "button_release_event",
- G_CALLBACK(gtk_scrollbar_button_release_event), this);
-
- gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
- G_CALLBACK(gtk_scrollbar_event_after), this);
- g_signal_handler_block(m_scrollBar[dir], handler_id);
-
- // these handlers get notified when scrollbar slider moves
- g_signal_connect_after(m_scrollBar[dir], "value_changed",
- G_CALLBACK(gtk_scrollbar_value_changed), this);
- }
-
- gtk_widget_show( m_wxwindow );
- }
+ GTKCreateScrolledWindowWith(m_wxwindow);
g_object_ref(m_widget);
if (m_parent)
return true;
}
+void wxWindowGTK::GTKDisconnect(void* instance)
+{
+ g_signal_handlers_disconnect_matched(instance,
+ GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, this);
+}
+
wxWindowGTK::~wxWindowGTK()
{
SendDestroyEvent();
if ( gs_deferredFocusOut == this )
gs_deferredFocusOut = NULL;
- m_hasVMT = false;
+ if (m_widget)
+ GTKDisconnect(m_widget);
+ if (m_wxwindow && m_wxwindow != m_widget)
+ GTKDisconnect(m_wxwindow);
// destroy children before destroying this window itself
DestroyChildren();
- // unhook focus handlers to prevent stray events being
- // propagated to this (soon to be) dead object
- if (m_focusWidget != NULL)
- {
- g_signal_handlers_disconnect_by_func (m_focusWidget,
- (gpointer) gtk_window_focus_in_callback,
- this);
- g_signal_handlers_disconnect_by_func (m_focusWidget,
- (gpointer) gtk_window_focus_out_callback,
- this);
- }
-
if (m_widget)
Show( false );
// 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
// gets painted again
- if (IsFrozen())
- DoThaw();
+ while (IsFrozen())
+ Thaw();
if (m_widget)
{
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;
}
IsTransparentBackgroundSupported() )
{
GdkScreen *screen = gtk_widget_get_screen (m_widget);
-
+#ifdef __WXGTK3__
+ gtk_widget_set_visual(m_widget, gdk_screen_get_rgba_visual(screen));
+#else
GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen);
if (rgba_colormap)
gtk_widget_set_colormap(m_widget, rgba_colormap);
+#endif
}
#endif // wxGTK_HAS_COMPOSITING_SUPPORT
if (!m_noExpose)
{
// these get reported to wxWidgets -> wxPaintEvent
-
- g_signal_connect (m_wxwindow, "expose_event",
- G_CALLBACK (gtk_window_expose_callback), this);
+#ifdef __WXGTK3__
+ g_signal_connect(m_wxwindow, "draw", G_CALLBACK(draw), this);
+#else
+ g_signal_connect(m_wxwindow, "expose_event", G_CALLBACK(expose_event), this);
+#endif
if (GetLayoutDirection() == wxLayout_LeftToRight)
gtk_widget_set_redraw_on_allocate(m_wxwindow, HasFlag(wxFULL_REPAINT_ON_RESIZE));
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 );
- // connect handler to prevent events from propagating up parent chain
- g_signal_connect_after(m_widget,
- "key_press_event", G_CALLBACK(key_and_mouse_event_after), this);
- g_signal_connect_after(m_widget,
- "key_release_event", G_CALLBACK(key_and_mouse_event_after), this);
- g_signal_connect_after(m_widget,
- "button_press_event", G_CALLBACK(key_and_mouse_event_after), this);
- g_signal_connect_after(m_widget,
- "button_release_event", G_CALLBACK(key_and_mouse_event_after), this);
- g_signal_connect_after(m_widget,
- "motion_notify_event", G_CALLBACK(key_and_mouse_event_after), 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);
+ GTKHandleRealized();
}
else
{
g_signal_connect (connect_widget, "realize",
G_CALLBACK (gtk_window_realized_callback), this);
}
+ g_signal_connect(connect_widget, "unrealize", G_CALLBACK(unrealize), this);
if (!IsTopLevel())
{
}
#if GTK_CHECK_VERSION(2, 8, 0)
+#ifndef __WXGTK3__
if ( gtk_check_version(2,8,0) == NULL )
+#endif
{
// Make sure we can notify the app when mouse capture is lost
if ( m_wxwindow )
}
#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();
- m_hasVMT = true;
-
SetLayoutDirection(wxLayout_Default);
// unless the window was created initially hidden (i.e. Hide() had been
G_CALLBACK (gtk_window_enter_callback), this);
g_signal_connect (widget, "leave_notify_event",
G_CALLBACK (gtk_window_leave_callback), this);
-
- 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);
}
-bool wxWindowGTK::Destroy()
-{
- m_hasVMT = false;
+static GSList* gs_queueResizeList;
- return wxWindowBase::Destroy();
+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");
- if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0 && (x == -1 || y == -1))
+ int scrollX = 0, scrollY = 0;
+ GtkWidget* parent = gtk_widget_get_parent(m_widget);
+ if (WX_IS_PIZZA(parent))
{
- int currentX, currentY;
- GetPosition(¤tX, ¤tY);
- if (x == -1)
- x = currentX;
- if (y == -1)
- y = currentY;
+ wxPizza* pizza = WX_PIZZA(parent);
+ scrollX = pizza->m_scroll_x;
+ scrollY = pizza->m_scroll_y;
}
- AdjustForParentClientOrigin(x, y, sizeFlags);
+ 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 );
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;
}
gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget),
&policy[ScrollDir_Horz],
&policy[ScrollDir_Vert]);
+ const int scrollbar_spacing =
+ GTK_SCROLLED_WINDOW_GET_CLASS(m_widget)->scrollbar_spacing;
for ( int i = 0; i < ScrollDir_Max; i++ )
{
continue;
}
- GtkScrolledWindowClass *scroll_class =
- GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
-
GtkRequisition req;
+#ifdef __WXGTK3__
+ GtkWidget* widget = GTK_WIDGET(range);
+ if (i == ScrollDir_Horz)
+ {
+ if (height)
+ {
+ gtk_widget_get_preferred_height(widget, NULL, &req.height);
+ h -= req.height + scrollbar_spacing;
+ }
+ }
+ else
+ {
+ if (width)
+ {
+ gtk_widget_get_preferred_width(widget, NULL, &req.width);
+ w -= req.width + scrollbar_spacing;
+ }
+ }
+#else // !__WXGTK3__
gtk_widget_size_request(GTK_WIDGET(range), &req);
if (i == ScrollDir_Horz)
- h -= req.height + scroll_class->scrollbar_spacing;
+ h -= req.height + scrollbar_spacing;
else
- w -= req.width + scroll_class->scrollbar_spacing;
+ w -= req.width + scrollbar_spacing;
+#endif // !__WXGTK3__
}
}
if ( !m_wxwindow )
return wxWindowBase::DoGetBorderSize();
- int x, y;
- WX_PIZZA(m_wxwindow)->get_border_widths(x, y);
-
- return 2*wxSize(x, y);
+ GtkBorder border;
+ WX_PIZZA(m_wxwindow)->get_border(border);
+ return wxSize(border.left + border.right, border.top + border.bottom);
}
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;
}
int *externalLeading,
const wxFont *theFont ) const
{
- wxFont fontToUse = theFont ? *theFont : GetFont();
+ // ensure we work with a valid font
+ wxFont fontToUse;
+ if ( !theFont || !theFont->IsOk() )
+ fontToUse = GetFont();
+ else
+ fontToUse = *theFont;
wxCHECK_RET( fontToUse.IsOk(), wxT("invalid font") );
- if (string.empty())
- {
- if (x) (*x) = 0;
- if (y) (*y) = 0;
- return;
- }
-
- PangoContext *context = NULL;
- if (m_widget)
- context = gtk_widget_get_pango_context( m_widget );
-
- if (!context)
- {
- if (x) (*x) = 0;
- if (y) (*y) = 0;
- return;
- }
-
- PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
- PangoLayout *layout = pango_layout_new(context);
- pango_layout_set_font_description(layout, desc);
- {
- const wxCharBuffer data = wxGTK_CONV( string );
- if ( data )
- pango_layout_set_text(layout, data, strlen(data));
- }
-
- PangoRectangle rect;
- pango_layout_get_extents(layout, NULL, &rect);
-
- if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
- if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
- if (descent)
- {
- PangoLayoutIter *iter = pango_layout_get_iter(layout);
- int baseline = pango_layout_iter_get_baseline(iter);
- pango_layout_iter_free(iter);
- *descent = *y - PANGO_PIXELS(baseline);
- }
- if (externalLeading) (*externalLeading) = 0; // ??
-
- g_object_unref (layout);
+ const wxWindow* win = static_cast<const wxWindow*>(this);
+ wxTextMeasure txm(win, &fontToUse);
+ txm.GetTextExtent(string, x, y, descent, externalLeading);
}
void wxWindowGTK::GTKDisableFocusOutEvent()
gboolean rc;
g_signal_emit_by_name(parent->m_widget, "focus", dir, &rc);
- return rc == TRUE;
+ return rc != 0;
}
}
ClientToScreen(&x, &y);
GdkDisplay* display = gtk_widget_get_display(m_widget);
GdkScreen* screen = gtk_widget_get_screen(m_widget);
-#ifdef __WXGTK30__
+#ifdef __WXGTK3__
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
return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
}
-void wxWindowGTK::GtkSendPaintEvents()
+#ifdef __WXGTK3__
+void wxWindowGTK::GTKSendPaintEvents(cairo_t* cr)
+#else
+void wxWindowGTK::GTKSendPaintEvents(const GdkRegion* region)
+#endif
{
- if (!m_wxwindow)
- {
- m_updateRegion.Clear();
- return;
- }
+#ifdef __WXGTK3__
+ m_paintContext = cr;
+ double x1, y1, x2, y2;
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+ m_updateRegion = wxRegion(int(x1), int(y1), int(x2 - x1), int(y2 - y1));
+#else // !__WXGTK3__
+ m_updateRegion = wxRegion(region);
#if wxGTK_HAS_COMPOSITING_SUPPORT
cairo_t* cr = NULL;
#endif
+#endif // !__WXGTK3__
// Clip to paint region in wxClientDC
m_clipPaintRegion = true;
// Transform m_updateRegion under RTL
m_updateRegion.Clear();
- gint width;
- gdk_drawable_get_size(gtk_widget_get_window(m_wxwindow), &width, NULL);
+ const int width = gdk_window_get_width(GTKGetDrawingWindow());
wxRegionIterator upd( m_nativeUpdateRegion );
while (upd)
// explicitly paint.
// NB: it works also for top level windows (but this is the
// windows manager which then does the compositing job)
+#ifndef __WXGTK3__
cr = gdk_cairo_create(m_wxwindow->window);
gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
cairo_clip(cr);
-
+#endif
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+#ifndef __WXGTK3__
cairo_surface_flush(cairo_get_target(cr));
+#endif
}
#endif // wxGTK_HAS_COMPOSITING_SUPPORT
break;
case wxBG_STYLE_ERASE:
{
+#ifdef __WXGTK3__
+ wxGTKCairoDC dc(cr);
+#else
wxWindowDC dc( (wxWindow*)this );
dc.SetDeviceClippingRegion( m_updateRegion );
dc.SetBackground(GetBackgroundColour());
dc.Clear();
}
-
+#endif // !__WXGTK3__
wxEraseEvent erase_event( GetId(), &dc );
erase_event.SetEventObject( this );
case wxBG_STYLE_SYSTEM:
if ( GetThemeEnabled() )
{
+ GdkWindow* gdkWindow = GTKGetDrawingWindow();
+ const int w = gdk_window_get_width(gdkWindow);
+ const int h = gdk_window_get_height(gdkWindow);
+#ifdef __WXGTK3__
+ GtkStyleContext* sc = gtk_widget_get_style_context(m_wxwindow);
+ gtk_render_background(sc, cr, 0, 0, w, h);
+#else
// find ancestor from which to steal background
wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
if (!parent)
parent = (wxWindow*)this;
-
- if (gtk_widget_get_mapped(parent->m_widget))
- {
- wxRegionIterator upd( m_nativeUpdateRegion );
- while (upd)
- {
- GdkRectangle rect;
- rect.x = upd.GetX();
- rect.y = upd.GetY();
- rect.width = upd.GetWidth();
- rect.height = upd.GetHeight();
-
- gtk_paint_flat_box(gtk_widget_get_style(parent->m_widget),
- GTKGetDrawingWindow(),
+ GdkRectangle rect;
+ m_nativeUpdateRegion.GetBox(rect.x, rect.y, rect.width, rect.height);
+ gtk_paint_flat_box(gtk_widget_get_style(parent->m_widget),
+ gdkWindow,
gtk_widget_get_state(m_wxwindow),
GTK_SHADOW_NONE,
&rect,
parent->m_widget,
(char *)"base",
- 0, 0, -1, -1 );
-
- ++upd;
- }
- }
+ 0, 0, w, h);
+#endif // !__WXGTK3__
}
break;
wxWindow *compositeChild = node->GetData();
if (compositeChild->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT)
{
+#ifndef __WXGTK3__
if (cr == NULL)
{
cr = gdk_cairo_create(m_wxwindow->window);
gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
cairo_clip(cr);
}
-
+#endif // !__WXGTK3__
GtkWidget *child = compositeChild->m_wxwindow;
GtkAllocation alloc;
gtk_widget_get_allocation(child, &alloc);
cairo_paint(cr);
}
}
+#ifndef __WXGTK3__
if (cr)
cairo_destroy(cr);
+#endif
}
#endif // wxGTK_HAS_COMPOSITING_SUPPORT
m_clipPaintRegion = false;
-
+#ifdef __WXGTK3__
+ m_paintContext = NULL;
+#endif
m_updateRegion.Clear();
m_nativeUpdateRegion.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()
if (!wxWindowBase::SetBackgroundColour(colour))
return false;
+#ifndef __WXGTK3__
if (colour.IsOk())
{
// We need the pixel value e.g. for background clearing.
m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
}
+#endif
// apply style change (forceStyle=true so that new style is applied
// even if the bg colour changed from valid to wxNullColour)
return false;
}
+#ifndef __WXGTK3__
if (colour.IsOk())
{
// We need the pixel value e.g. for background clearing.
m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
}
+#endif
// apply style change (forceStyle=true so that new style is applied
// even if the bg colour changed from valid to wxNullColour):
return gtk_widget_get_pango_context( m_widget );
}
+#ifndef __WXGTK3__
GtkRcStyle *wxWindowGTK::GTKCreateWidgetStyle(bool forceStyle)
{
// do we need to apply any changes at all?
return style;
}
+#endif // !__WXGTK3__
-void wxWindowGTK::GTKApplyWidgetStyle(bool forceStyle)
+void wxWindowGTK::GTKApplyWidgetStyle(bool WXUNUSED_IN_GTK3(forceStyle))
{
+#ifdef __WXGTK3__
+ DoApplyWidgetStyle(NULL);
+#else
GtkRcStyle *style = GTKCreateWidgetStyle(forceStyle);
if ( style )
{
DoApplyWidgetStyle(style);
g_object_unref(style);
}
+#endif
// Style change may affect GTK+'s size calculation:
InvalidateBestSize();
void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
{
- if ( m_wxwindow )
+ GtkWidget* widget = m_wxwindow ? m_wxwindow : m_widget;
+
+ // block the signal temporarily to avoid sending
+ // wxSysColourChangedEvents when we change the colours ourselves
+ bool unblock = false;
+ if (m_wxwindow && IsTopLevel())
{
- // block the signal temporarily to avoid sending
- // wxSysColourChangedEvents when we change the colours ourselves
- bool unblock = false;
- if ( IsTopLevel() )
- {
- unblock = true;
- g_signal_handlers_block_by_func(
- m_wxwindow, (void *)gtk_window_style_set_callback, this);
- }
+ unblock = true;
+ g_signal_handlers_block_by_func(
+ m_wxwindow, (void*)style_updated, this);
+ }
- gtk_widget_modify_style(m_wxwindow, style);
+ GTKApplyStyle(widget, style);
- if ( unblock )
- {
- g_signal_handlers_unblock_by_func(
- m_wxwindow, (void *)gtk_window_style_set_callback, this);
- }
- }
- else
+ if (unblock)
{
- gtk_widget_modify_style(m_widget, style);
+ g_signal_handlers_unblock_by_func(
+ m_wxwindow, (void*)style_updated, this);
}
}
+void wxWindowGTK::GTKApplyStyle(GtkWidget* widget, GtkRcStyle* WXUNUSED_IN_GTK3(style))
+{
+#ifdef __WXGTK3__
+ const PangoFontDescription* pfd = NULL;
+ if (m_font.IsOk())
+ pfd = pango_font_description_copy(m_font.GetNativeFontInfo()->description);
+ gtk_widget_override_font(widget, pfd);
+ gtk_widget_override_color(widget, GTK_STATE_FLAG_NORMAL, m_foregroundColour);
+ gtk_widget_override_background_color(widget, GTK_STATE_FLAG_NORMAL, m_backgroundColour);
+#else
+ gtk_widget_modify_style(widget, style);
+#endif
+}
+
bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
{
if (!wxWindowBase::SetBackgroundStyle(style))
return false;
+#ifndef __WXGTK3__
GdkWindow *window;
if ( m_wxwindow )
{
{
// Make sure GDK/X11 doesn't refresh the window
// automatically.
- gdk_window_set_back_pixmap( window, None, False );
+ gdk_window_set_back_pixmap( window, NULL, FALSE );
m_needsStyleChange = false;
}
else // window not realized yet
// even if the bg colour changed from valid to wxNullColour):
GTKApplyWidgetStyle(true);
}
+#endif // !__WXGTK3__
return true;
}
bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const
{
#if wxGTK_HAS_COMPOSITING_SUPPORT
+#ifndef __WXGTK3__
if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING) != NULL)
{
if (reason)
return false;
}
+#endif // !__WXGTK3__
// 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
*reason = _("This program was compiled with a too old version of GTK+, "
"please rebuild with GTK+ 2.12 or newer.");
}
-#endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
return false;
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
}
// ----------------------------------------------------------------------------
{
wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
- // For compatibility with other ports, pretend that the window showing the
- // menu has focus while the menu is shown. This is needed because the popup
- // menu actually steals the focus from the window it's associated it in
- // wxGTK unlike, say, wxMSW.
- wxWindowGTK* const oldPendingFocus = gs_pendingFocus;
- gs_pendingFocus = this;
- wxON_BLOCK_EXIT_SET( gs_pendingFocus, oldPendingFocus );
-
menu->UpdateUI();
wxPoint pos;
// update current position
m_scrollPos[barIndex] = value;
// If event should be ignored, or integral position has not changed
- if (!m_hasVMT || g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos))
+ if (g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos))
{
return wxEVT_NULL;
}
// Get the current mouse position.
wxPoint wxGetMousePosition()
{
- 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;
- gdk_display_get_pointer(display, NULL, &x, &y, NULL);
+ GdkModifierType unused;
+ GetMouseState(x, y, unused);
return wxPoint(x, y);
}
// freeze/thaw
// ----------------------------------------------------------------------------
-extern "C"
-{
-
-// this is called if we attempted to freeze unrealized widget when it finally
-// is realized (and so can be frozen):
-static void wx_frozen_widget_realize(GtkWidget* w, wxWindowGTK* win)
+void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget)
{
- wxASSERT( w && gtk_widget_get_has_window(w) );
- wxASSERT( gtk_widget_get_realized(w) );
-
- g_signal_handlers_disconnect_by_func
- (
- w,
- (void*)wx_frozen_widget_realize,
- win
- );
-
- GdkWindow* window;
- if (w == win->m_wxwindow)
- window = win->GTKGetDrawingWindow();
- else
- window = gtk_widget_get_window(w);
- gdk_window_freeze_updates(window);
-}
-
-} // extern "C"
-
-void wxWindowGTK::GTKFreezeWidget(GtkWidget *w)
-{
- if ( !w || !gtk_widget_get_has_window(w) )
- return; // window-less widget, cannot be frozen
-
- GdkWindow* window = gtk_widget_get_window(w);
- if (window == NULL)
+ if (widget && gtk_widget_get_has_window(widget))
{
- // we can't thaw unrealized widgets because they don't have GdkWindow,
- // so set it up to be done immediately after realization:
- g_signal_connect_after
- (
- w,
- "realize",
- G_CALLBACK(wx_frozen_widget_realize),
- this
- );
- return;
+ GdkWindow* window = gtk_widget_get_window(widget);
+ if (window)
+ gdk_window_freeze_updates(window);
}
-
- if (w == m_wxwindow)
- window = GTKGetDrawingWindow();
- gdk_window_freeze_updates(window);
}
-void wxWindowGTK::GTKThawWidget(GtkWidget *w)
+void wxWindowGTK::GTKThawWidget(GtkWidget* widget)
{
- if ( !w || !gtk_widget_get_has_window(w) )
- return; // window-less widget, cannot be frozen
-
- GdkWindow* window = gtk_widget_get_window(w);
- if (window == NULL)
+ if (widget && gtk_widget_get_has_window(widget))
{
- // the widget wasn't realized yet, no need to thaw
- g_signal_handlers_disconnect_by_func
- (
- w,
- (void*)wx_frozen_widget_realize,
- this
- );
- return;
+ GdkWindow* window = gtk_widget_get_window(widget);
+ if (window)
+ gdk_window_thaw_updates(window);
}
-
- if (w == m_wxwindow)
- window = GTKGetDrawingWindow();
- gdk_window_thaw_updates(window);
}
void wxWindowGTK::DoFreeze()
{
- GTKFreezeWidget(m_widget);
- if ( m_wxwindow && m_widget != m_wxwindow )
- GTKFreezeWidget(m_wxwindow);
+ GtkWidget* widget = m_wxwindow;
+ if (widget == NULL)
+ widget = m_widget;
+ GTKFreezeWidget(widget);
}
void wxWindowGTK::DoThaw()
{
- GTKThawWidget(m_widget);
- if ( m_wxwindow && m_widget != m_wxwindow )
- GTKThawWidget(m_wxwindow);
+ GtkWidget* widget = m_wxwindow;
+ if (widget == NULL)
+ widget = m_widget;
+ GTKThawWidget(widget);
}