]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/window.cpp
remove wxMSW-specific FindSuitableParent() and use GetParentForModalDialog() everywhe...
[wxWidgets.git] / src / gtk / window.cpp
index 5313dd72a0ba7ae2835eb4a63ec183baa6a07c7f..eac2874bea98cec3f6042654be3a3b87fbc2ecc9 100644 (file)
@@ -1,6 +1,6 @@
 /////////////////////////////////////////////////////////////////////////////
 // Name:        src/gtk/window.cpp
-// Purpose:
+// Purpose:     wxWindowGTK implementation
 // Author:      Robert Roebling
 // Id:          $Id$
 // Copyright:   (c) 1998 Robert Roebling, Julian Smart
 #include "wx/fontutil.h"
 #include "wx/sysopt.h"
 
-#ifdef __WXDEBUG__
-    #include "wx/thread.h"
-#endif
-
 #include <ctype.h>
 
 #include "wx/gtk/private.h"
-#include "wx/gtk/win_gtk.h"
+#include "wx/gtk/private/win_gtk.h"
 #include <gdk/gdkkeysyms.h>
 #include <gdk/gdkx.h>
 
+#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
+
 //-----------------------------------------------------------------------------
 // documentation on internals
 //-----------------------------------------------------------------------------
@@ -186,42 +205,28 @@ extern wxCursor   g_globalCursor;
 
 // mouse capture state: the window which has it and if the mouse is currently
 // inside it
-static wxWindowGTK  *g_captureWindow = (wxWindowGTK*) NULL;
+static wxWindowGTK  *g_captureWindow = NULL;
 static bool g_captureWindowHasMouse = false;
 
-wxWindowGTK  *g_focusWindow = (wxWindowGTK*) NULL;
+// The window that currently has focus:
+static wxWindowGTK *gs_currentFocus = NULL;
+// The window that is scheduled to get focus in the next event loop iteration
+// or NULL if there's no pending focus change:
+static wxWindowGTK *gs_pendingFocus = NULL;
 
-// the last window which had the focus - this is normally never NULL (except
-// if we never had focus at all) as even when g_focusWindow is NULL it still
-// keeps its previous value
-wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL;
-
-// If a window get the focus set but has not been realized
-// yet, defer setting the focus to idle time.
-wxWindowGTK *g_delayedFocus = (wxWindowGTK*) NULL;
+// the window that has deferred focus-out event pending, if any (see
+// GTKAddDeferredFocusOut() for details)
+static wxWindowGTK *gs_deferredFocusOut = NULL;
 
 // global variables because GTK+ DnD want to have the
 // mouse event that caused it
-GdkEvent    *g_lastMouseEvent = (GdkEvent*) NULL;
+GdkEvent    *g_lastMouseEvent = NULL;
 int          g_lastButtonNumber = 0;
 
-extern bool g_mainThreadLocked;
-
 //-----------------------------------------------------------------------------
 // debug
 //-----------------------------------------------------------------------------
 
-#ifdef __WXDEBUG__
-
-#if wxUSE_THREADS
-#   define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
-#else
-#   define DEBUG_MAIN_THREAD
-#endif
-#else
-#define DEBUG_MAIN_THREAD
-#endif // Debug
-
 // the trace mask used for the focus debugging messages
 #define TRACE_FOCUS _T("focus")
 
@@ -247,72 +252,16 @@ gdk_window_warp_pointer (GdkWindow      *window,
   }
 }
 
-//-----------------------------------------------------------------------------
-// local code (see below)
-//-----------------------------------------------------------------------------
-
-// returns the child of win which currently has focus or NULL if not found
-//
-// Note: can't be static, needed by textctrl.cpp.
-wxWindow *wxFindFocusedChild(wxWindowGTK *win)
-{
-    wxWindow *winFocus = wxWindowGTK::FindFocus();
-    if ( !winFocus )
-        return (wxWindow *)NULL;
-
-    if ( winFocus == win )
-        return (wxWindow *)win;
-
-    for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
-          node;
-          node = node->GetNext() )
-    {
-        wxWindow *child = wxFindFocusedChild(node->GetData());
-        if ( child )
-            return child;
-    }
-
-    return (wxWindow *)NULL;
-}
-
-static void GetScrollbarWidth(GtkWidget* widget, int& w, int& h)
-{
-    GtkScrolledWindow* scroll_window = GTK_SCROLLED_WINDOW(widget);
-    GtkScrolledWindowClass* scroll_class = GTK_SCROLLED_WINDOW_CLASS(GTK_OBJECT_GET_CLASS(scroll_window));
-    GtkRequisition scroll_req;
-
-    w = 0;
-    if (scroll_window->vscrollbar_visible)
-    {
-        scroll_req.width = 2;
-        scroll_req.height = 2;
-        (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
-            (scroll_window->vscrollbar, &scroll_req );
-        w = scroll_req.width +
-            scroll_class->scrollbar_spacing;
-    }
-
-    h = 0;
-    if (scroll_window->hscrollbar_visible)
-    {
-        scroll_req.width = 2;
-        scroll_req.height = 2;
-        (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
-            (scroll_window->hscrollbar, &scroll_req );
-        h = scroll_req.height +
-            scroll_class->scrollbar_spacing;
-    }
-}
 
 //-----------------------------------------------------------------------------
 // "size_request" of m_widget
 //-----------------------------------------------------------------------------
 
-// make it extern because wxStaticText needs to disconnect this one
 extern "C" {
-void wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget),
-                                        GtkRequisition *requisition,
-                                        wxWindow * win)
+static void
+wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget),
+                                   GtkRequisition *requisition,
+                                   wxWindow * win)
 {
     int w, h;
     win->GetSize( &w, &h );
@@ -332,78 +281,45 @@ void wxgtk_window_size_request_callback(GtkWidget * WXUNUSED(widget),
 
 extern "C" {
 static gboolean
-gtk_window_expose_callback( GtkWidget*,
+gtk_window_expose_callback( GtkWidget* widget,
                             GdkEventExpose *gdk_event,
                             wxWindow *win )
 {
-    DEBUG_MAIN_THREAD
-
-#if 0
-    if (win->GetName())
+    if (gdk_event->window == widget->window)
     {
-        wxPrintf( wxT("OnExpose from ") );
-        if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
-            wxPrintf( win->GetClassInfo()->GetClassName() );
-        wxPrintf( wxT(" %d %d %d %d\n"), (int)gdk_event->area.x,
-                                         (int)gdk_event->area.y,
-                                         (int)gdk_event->area.width,
-                                         (int)gdk_event->area.height );
+        win->GetUpdateRegion() = wxRegion( gdk_event->region );
+        win->GtkSendPaintEvents();
     }
-
-    gtk_paint_box
-    (
-        win->m_wxwindow->style,
-        pizza->bin_window,
-        GTK_STATE_NORMAL,
-        GTK_SHADOW_OUT,
-        (GdkRectangle*) NULL,
-        win->m_wxwindow,
-        (char *)"button", // const_cast
-        20,20,24,24
-    );
-#endif
-
-    win->GetUpdateRegion() = wxRegion( gdk_event->region );
-
-    win->GtkSendPaintEvents();
-
     // Let parent window draw window-less widgets
     return FALSE;
 }
 }
 
+#ifndef __WXUNIVERSAL__
 //-----------------------------------------------------------------------------
-// "expose_event" from m_widget, for drawing border
+// "expose_event" from m_wxwindow->parent, for drawing border
 //-----------------------------------------------------------------------------
 
-#ifndef __WXUNIVERSAL__
-
-GtkWidget* GetEntryWidget();
-
 extern "C" {
 static gboolean
 expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
 {
-    // if this event is not for the GdkWindow the border is drawn on
-    if (win->m_wxwindow == win->m_widget && gdk_event->window == widget->window)
+    if (gdk_event->window != widget->window)
+        return false;
+
+    const GtkAllocation& alloc = win->m_wxwindow->allocation;
+    const int x = alloc.x;
+    const int y = alloc.y;
+    const int w = alloc.width;
+    const int h = alloc.height;
+
+    if (w <= 0 || h <= 0)
         return false;
 
-    int x = 0;
-    int y = 0;
-    // GtkScrolledWindow is GTK_NO_WINDOW
-    if (GTK_WIDGET_NO_WINDOW(widget))
-    {
-        x = widget->allocation.x;
-        y = widget->allocation.y;
-    }
-    int w = win->m_wxwindow->allocation.width;
-    int h = win->m_wxwindow->allocation.height;
     if (win->HasFlag(wxBORDER_SIMPLE))
     {
-        GdkGC* gc = gdk_gc_new(gdk_event->window);
-        gdk_gc_set_foreground(gc, &widget->style->black);
-        gdk_draw_rectangle(gdk_event->window, gc, false, x, y, w - 1, h - 1);
-        g_object_unref(gc);
+        gdk_draw_rectangle(gdk_event->window,
+            widget->style->black_gc, false, x, y, w - 1, h - 1);
     }
     else
     {
@@ -413,21 +329,39 @@ expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
 
         // Style detail to use
         const char* detail;
-        if (widget == win->m_wxwindow)
+        if (win->m_widget == win->m_wxwindow)
             // for non-scrollable wxWindows
             detail = "entry";
         else
             // for scrollable ones
             detail = "viewport";
 
-        GtkWidget* styleWidget = GetEntryWidget();
         gtk_paint_shadow(
-           styleWidget->style, gdk_event->window, GTK_STATE_NORMAL,
-           shadow, NULL, styleWidget, detail, x, y, w, h);
+           win->m_wxwindow->style, gdk_event->window, GTK_STATE_NORMAL,
+           shadow, NULL, wxGTKPrivate::GetEntryWidget(), detail, x, y, w, h);
     }
+    return false;
+}
+}
+
+//-----------------------------------------------------------------------------
+// "parent_set" from m_wxwindow
+//-----------------------------------------------------------------------------
 
-    // no further painting is needed for border-only GdkWindow
-    return win->m_wxwindow == win->m_widget;
+extern "C" {
+static void
+parent_set(GtkWidget* widget, GtkObject* old_parent, wxWindow* win)
+{
+    if (old_parent)
+    {
+        g_signal_handlers_disconnect_by_func(
+            old_parent, (void*)expose_event_border, win);
+    }
+    if (widget->parent)
+    {
+        g_signal_connect_after(widget->parent, "expose_event",
+            G_CALLBACK(expose_event_border), win);
+    }
 }
 }
 #endif // !__WXUNIVERSAL__
@@ -600,11 +534,11 @@ static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar)
         case GDK_KP_7:
         case GDK_KP_8:
         case GDK_KP_9:
-            key_code = (isChar ? '0' : WXK_NUMPAD0) + keysym - GDK_KP_0;
+            key_code = (isChar ? '0' : int(WXK_NUMPAD0)) + keysym - GDK_KP_0;
             break;
 
         case GDK_KP_Space:
-            key_code = isChar ? ' ' : WXK_NUMPAD_SPACE;
+            key_code = isChar ? ' ' : int(WXK_NUMPAD_SPACE);
             break;
 
         case GDK_KP_Tab:
@@ -676,32 +610,32 @@ static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar)
             break;
 
         case GDK_KP_Equal:
-            key_code = isChar ? '=' : WXK_NUMPAD_EQUAL;
+            key_code = isChar ? '=' : int(WXK_NUMPAD_EQUAL);
             break;
 
         case GDK_KP_Multiply:
-            key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY;
+            key_code = isChar ? '*' : int(WXK_NUMPAD_MULTIPLY);
             break;
 
         case GDK_KP_Add:
-            key_code = isChar ? '+' : WXK_NUMPAD_ADD;
+            key_code = isChar ? '+' : int(WXK_NUMPAD_ADD);
             break;
 
         case GDK_KP_Separator:
             // FIXME: what is this?
-            key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR;
+            key_code = isChar ? '.' : int(WXK_NUMPAD_SEPARATOR);
             break;
 
         case GDK_KP_Subtract:
-            key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT;
+            key_code = isChar ? '-' : int(WXK_NUMPAD_SUBTRACT);
             break;
 
         case GDK_KP_Decimal:
-            key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL;
+            key_code = isChar ? '.' : int(WXK_NUMPAD_DECIMAL);
             break;
 
         case GDK_KP_Divide:
-            key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE;
+            key_code = isChar ? '/' : int(WXK_NUMPAD_DIVIDE);
             break;
 
 
@@ -748,8 +682,7 @@ static void wxFillOtherKeyEventFields(wxKeyEvent& event,
     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_MOD2_MASK) != 0;
-    event.m_scanCode = gdk_event->keyval;
+    event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
     event.m_rawCode = (wxUint32) gdk_event->keyval;
     event.m_rawFlags = 0;
 #if wxUSE_UNICODE
@@ -890,23 +823,15 @@ struct wxGtkIMData
 
 extern "C" {
 static gboolean
-gtk_window_key_press_callback( GtkWidget *widget,
+gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
                                GdkEventKey *gdk_event,
                                wxWindow *win )
 {
-    DEBUG_MAIN_THREAD
-
     if (!win->m_hasVMT)
         return FALSE;
     if (g_blockEventsOnDrag)
         return FALSE;
 
-    // GTK+ sends keypress events to the focus widget and then
-    // to all its parent and grandparent widget. We only want
-    // the key events from the focus widget.
-    if (!GTK_WIDGET_HAS_FOCUS(widget))
-        return FALSE;
-
     wxKeyEvent event( wxEVT_KEY_DOWN );
     bool ret = false;
     bool return_after_IM = false;
@@ -949,8 +874,18 @@ gtk_window_key_press_callback( GtkWidget *widget,
             int command = ancestor->GetAcceleratorTable()->GetCommand( event );
             if (command != -1)
             {
-                wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
-                ret = ancestor->HandleWindowEvent( command_event );
+                wxCommandEvent menu_event( wxEVT_COMMAND_MENU_SELECTED, command );
+                ret = ancestor->HandleWindowEvent( menu_event );
+
+                if ( !ret )
+                {
+                    // if the accelerator wasn't handled as menu event, try
+                    // it as button click (for compatibility with other
+                    // platforms):
+                    wxCommandEvent button_event( wxEVT_COMMAND_BUTTON_CLICKED, command );
+                    ret = ancestor->HandleWindowEvent( button_event );
+                }
+
                 break;
             }
             if (ancestor->IsTopLevel())
@@ -1108,8 +1043,6 @@ gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
                                  GdkEventKey *gdk_event,
                                  wxWindowGTK *win )
 {
-    DEBUG_MAIN_THREAD
-
     if (!win->m_hasVMT)
         return FALSE;
 
@@ -1141,15 +1074,15 @@ template<typename T> void InitMouseEvent(wxWindowGTK *win,
                                          T *gdk_event)
 {
     event.SetTimestamp( gdk_event->time );
-    event.m_shiftDown = gdk_event->state & GDK_SHIFT_MASK;
-    event.m_controlDown = gdk_event->state & GDK_CONTROL_MASK;
-    event.m_altDown = gdk_event->state & GDK_MOD1_MASK;
-    event.m_metaDown = gdk_event->state & GDK_MOD2_MASK;
-    event.m_leftDown = gdk_event->state & GDK_BUTTON1_MASK;
-    event.m_middleDown = gdk_event->state & GDK_BUTTON2_MASK;
-    event.m_rightDown = gdk_event->state & GDK_BUTTON3_MASK;
-    event.m_aux1Down = gdk_event->state & GDK_BUTTON4_MASK;
-    event.m_aux2Down = gdk_event->state & GDK_BUTTON5_MASK;
+    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;
+    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;
@@ -1222,7 +1155,7 @@ wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
         if (!child->IsShown())
             continue;
 
-        if (child->IsTransparentForMouse())
+        if (child->GTKIsTransparentForMouse())
         {
             // wxStaticBox is transparent in the box itself
             int xx1 = child->m_x;
@@ -1248,7 +1181,7 @@ wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
         }
         else
         {
-            if ((child->m_wxwindow == (GtkWidget*) NULL) &&
+            if ((child->m_wxwindow == NULL) &&
                 (child->m_x <= xx) &&
                 (child->m_y <= yy) &&
                 (child->m_x+child->m_width  >= xx) &&
@@ -1277,8 +1210,6 @@ bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
 
 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
 {
-    DEBUG_MAIN_THREAD
-
     if (!m_hasVMT)
         return FALSE;
     if (g_blockEventsOnDrag)
@@ -1312,21 +1243,6 @@ wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
     if ( rc != -1 )                                                           \
         return rc
 
-// send the wxChildFocusEvent and wxFocusEvent, common code of
-// gtk_window_focus_in_callback() and SetFocus()
-static bool DoSendFocusEvents(wxWindow *win)
-{
-    // Notify the parent keeping track of focus for the kbd navigation
-    // purposes that we got it.
-    wxChildFocusEvent eventChildFocus(win);
-    (void)win->HandleWindowEvent(eventChildFocus);
-
-    wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
-    eventFocus.SetEventObject(win);
-
-    return win->HandleWindowEvent(eventFocus);
-}
-
 // all event handlers must have C linkage as they're called from GTK+ C code
 extern "C"
 {
@@ -1451,10 +1367,6 @@ gtk_window_button_press_callback( GtkWidget *widget,
 
     AdjustEventButtonState(event);
 
-    // wxListBox actually gets mouse events from the item, so we need to give it
-    // a chance to correct this
-    win->FixUpMouseEvent(widget, event.m_x, event.m_y);
-
     // find the correct window to send the event to: it may be a different one
     // from the one which got it at GTK+ level because some controls don't have
     // their own X window and thus cannot get any events.
@@ -1471,7 +1383,7 @@ gtk_window_button_press_callback( GtkWidget *widget,
         return TRUE;
 
     if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
-        (g_focusWindow != win) /* && win->IsFocusable() */)
+        (gs_currentFocus != win) /* && win->IsFocusable() */)
     {
         win->SetFocus();
     }
@@ -1501,7 +1413,7 @@ gtk_window_button_press_callback( GtkWidget *widget,
 //-----------------------------------------------------------------------------
 
 static gboolean
-gtk_window_button_release_callback( GtkWidget *widget,
+gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget),
                                     GdkEventButton *gdk_event,
                                     wxWindowGTK *win )
 {
@@ -1537,9 +1449,6 @@ gtk_window_button_release_callback( GtkWidget *widget,
 
     AdjustEventButtonState(event);
 
-    // same wxListBox hack as above
-    win->FixUpMouseEvent(widget, event.m_x, event.m_y);
-
     if ( !g_captureWindow )
         win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
 
@@ -1631,10 +1540,40 @@ gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
 //-----------------------------------------------------------------------------
 
 static gboolean
-window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
+window_scroll_event_hscrollbar(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
 {
-    DEBUG_MAIN_THREAD
+    if (gdk_event->direction != GDK_SCROLL_LEFT &&
+        gdk_event->direction != GDK_SCROLL_RIGHT)
+    {
+        return false;
+    }
+
+    wxMouseEvent event(wxEVT_MOUSEWHEEL);
+    InitMouseEvent(win, event, gdk_event);
+
+    GtkRange *range = win->m_scrollBar[wxWindow::ScrollDir_Horz];
+    if (!range) return FALSE;
+
+    if (range && GTK_WIDGET_VISIBLE (range))
+    {
+        GtkAdjustment *adj = range->adjustment;
+        gdouble delta = adj->step_increment * 3;
+        if (gdk_event->direction == GDK_SCROLL_LEFT)
+            delta = -delta;
+
+        gdouble new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
+
+        gtk_adjustment_set_value (adj, new_value);
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
 
+static gboolean
+window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
+{
     if (gdk_event->direction != GDK_SCROLL_UP &&
         gdk_event->direction != GDK_SCROLL_DOWN)
     {
@@ -1643,6 +1582,8 @@ window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
 
     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)
@@ -1650,7 +1591,27 @@ window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
     else
         event.m_wheelRotation = -120;
 
-    return win->GTKProcessEvent(event);
+    if (win->GTKProcessEvent(event))
+      return TRUE;
+
+    GtkRange *range = win->m_scrollBar[wxWindow::ScrollDir_Vert];
+    if (!range) return FALSE;
+
+    if (range && GTK_WIDGET_VISIBLE (range))
+    {
+        GtkAdjustment *adj = range->adjustment;
+        gdouble delta = adj->step_increment * 3;
+        if (gdk_event->direction == GDK_SCROLL_UP)
+          delta = -delta;
+
+        gdouble new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
+
+        gtk_adjustment_set_value (adj, new_value);
+
+        return TRUE;
+    }
+
+    return FALSE;
 }
 
 //-----------------------------------------------------------------------------
@@ -1671,47 +1632,9 @@ static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
 static gboolean
 gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
                               GdkEventFocus *WXUNUSED(event),
-                              wxWindow *win )
+                              wxWindowGTK *win )
 {
-    DEBUG_MAIN_THREAD
-
-    if (win->m_imData)
-        gtk_im_context_focus_in(win->m_imData->context);
-
-    g_focusWindowLast =
-    g_focusWindow = win;
-
-    wxLogTrace(TRACE_FOCUS,
-               _T("%s: focus in"), win->GetName().c_str());
-
-#if wxUSE_CARET
-    // caret needs to be informed about focus change
-    wxCaret *caret = win->GetCaret();
-    if ( caret )
-    {
-        caret->OnSetFocus();
-    }
-#endif // wxUSE_CARET
-
-    gboolean ret = FALSE;
-
-    // does the window itself think that it has the focus?
-    if ( !win->m_hasFocus )
-    {
-        // not yet, notify it
-        win->m_hasFocus = true;
-
-        (void)DoSendFocusEvents(win);
-
-        ret = TRUE;
-    }
-
-    // Disable default focus handling for custom windows
-    // since the default GTK+ handler issues a repaint
-    if (win->m_wxwindow)
-        return ret;
-
-    return FALSE;
+    return win->GTKHandleFocusIn();
 }
 
 //-----------------------------------------------------------------------------
@@ -1723,55 +1646,13 @@ gtk_window_focus_out_callback( GtkWidget * WXUNUSED(widget),
                                GdkEventFocus * WXUNUSED(gdk_event),
                                wxWindowGTK *win )
 {
-    DEBUG_MAIN_THREAD
-
-    if (win->m_imData)
-        gtk_im_context_focus_out(win->m_imData->context);
-
-    wxLogTrace( TRACE_FOCUS,
-                _T("%s: focus out"), win->GetName().c_str() );
-
-
-    wxWindowGTK *winFocus = wxFindFocusedChild(win);
-    if ( winFocus )
-        win = winFocus;
-
-    g_focusWindow = (wxWindowGTK *)NULL;
-
-#if wxUSE_CARET
-    // caret needs to be informed about focus change
-    wxCaret *caret = win->GetCaret();
-    if ( caret )
-    {
-        caret->OnKillFocus();
-    }
-#endif // wxUSE_CARET
-
-    // don't send the window a kill focus event if it thinks that it doesn't
-    // have focus already
-    if ( win->m_hasFocus )
-    {
-        // the event handler might delete the window when it loses focus, so
-        // check whether this is a custom window before calling it
-        const bool has_wxwindow = win->m_wxwindow != NULL;
-
-        win->m_hasFocus = false;
-
-        wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
-        event.SetEventObject( win );
-
-        (void)win->GTKProcessEvent( event );
-
-        // Disable default focus handling for custom windows
-        // since the default GTK+ handler issues a repaint
-        if ( has_wxwindow )
-            return TRUE;
-    }
-
-    // continue with normal processing
-    return FALSE;
+    return win->GTKHandleFocusOut();
 }
 
+//-----------------------------------------------------------------------------
+// "focus"
+//-----------------------------------------------------------------------------
+
 static gboolean
 wx_window_focus_callback(GtkWidget *widget,
                          GtkDirectionType WXUNUSED(direction),
@@ -1862,7 +1743,7 @@ gtk_window_leave_callback( GtkWidget *widget,
 static void
 gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
 {
-    wxEventType eventType = win->GetScrollEventType(range);
+    wxEventType eventType = win->GTKGetScrollEventType(range);
     if (eventType != wxEVT_NULL)
     {
         // Convert scroll event type to scrollwin event type
@@ -1887,8 +1768,6 @@ gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
 static gboolean
 gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
 {
-    DEBUG_MAIN_THREAD
-
     g_blockEventsOnScroll = true;
     win->m_mouseButtonDown = true;
 
@@ -1922,8 +1801,6 @@ gtk_scrollbar_event_after(GtkRange* range, GdkEvent* event, wxWindow* win)
 static gboolean
 gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
 {
-    DEBUG_MAIN_THREAD
-
     g_blockEventsOnScroll = false;
     win->m_mouseButtonDown = false;
     // If thumb tracking
@@ -1946,14 +1823,12 @@ gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* w
 static void
 gtk_window_realized_callback(GtkWidget* widget, wxWindow* win)
 {
-    DEBUG_MAIN_THREAD
-
     if (win->m_imData)
     {
         gtk_im_context_set_client_window( win->m_imData->context,
                                           widget->window);
     }
-
+    
     // We cannot set colours and fonts before the widget
     // been realized, so we do this directly after realization
     // or otherwise in idle time
@@ -2076,33 +1951,25 @@ public:
 
 wxWindow *wxWindowBase::DoFindFocus()
 {
+    wxWindowGTK *focus = gs_pendingFocus ? gs_pendingFocus : gs_currentFocus;
     // the cast is necessary when we compile in wxUniversal mode
-    return (wxWindow *)g_focusWindow;
+    return static_cast<wxWindow*>(focus);
 }
 
-//-----------------------------------------------------------------------------
-// InsertChild for wxWindowGTK.
-//-----------------------------------------------------------------------------
-
-/* Callback for wxWindowGTK. This very strange beast has to be used because
- * C++ has no virtual methods in a constructor. We have to emulate a
- * virtual function here as wxNotebook requires a different way to insert
- * a child in it. I had opted for creating a wxNotebookPage window class
- * which would have made this superfluous (such in the MDI window system),
- * but no-one was listening to me... */
-
-static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
+void wxWindowGTK::AddChildGTK(wxWindowGTK* child)
 {
-    /* the window might have been scrolled already, do we
-       have to adapt the position */
-    wxPizza* pizza = WX_PIZZA(parent->m_wxwindow);
+    wxASSERT_MSG(m_wxwindow, "Cannot add a child to a window without a client area");
+    
+    // the window might have been scrolled already, we
+    // have to adapt the position
+    wxPizza* pizza = WX_PIZZA(m_wxwindow);
     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);
     gtk_fixed_put(
-        GTK_FIXED(parent->m_wxwindow), child->m_widget, child->m_x, child->m_y);
+        GTK_FIXED(m_wxwindow), child->m_widget, child->m_x, child->m_y);
 }
 
 //-----------------------------------------------------------------------------
@@ -2127,16 +1994,16 @@ wxMouseState wxGetMouseState()
 
     ms.SetX(x);
     ms.SetY(y);
-    ms.SetLeftDown(mask & GDK_BUTTON1_MASK);
-    ms.SetMiddleDown(mask & GDK_BUTTON2_MASK);
-    ms.SetRightDown(mask & GDK_BUTTON3_MASK);
-    ms.SetAux1Down(mask & GDK_BUTTON4_MASK);
-    ms.SetAux2Down(mask & GDK_BUTTON5_MASK);
+    ms.SetLeftDown((mask & GDK_BUTTON1_MASK) != 0);
+    ms.SetMiddleDown((mask & GDK_BUTTON2_MASK) != 0);
+    ms.SetRightDown((mask & GDK_BUTTON3_MASK) != 0);
+    ms.SetAux1Down((mask & GDK_BUTTON4_MASK) != 0);
+    ms.SetAux2Down((mask & GDK_BUTTON5_MASK) != 0);
 
-    ms.SetControlDown(mask & GDK_CONTROL_MASK);
-    ms.SetShiftDown(mask & GDK_SHIFT_MASK);
-    ms.SetAltDown(mask & GDK_MOD1_MASK);
-    ms.SetMetaDown(mask & GDK_MOD2_MASK);
+    ms.SetControlDown((mask & GDK_CONTROL_MASK) != 0);
+    ms.SetShiftDown((mask & GDK_SHIFT_MASK) != 0);
+    ms.SetAltDown((mask & GDK_MOD1_MASK) != 0);
+    ms.SetMetaDown((mask & GDK_META_MASK) != 0);
 
     return ms;
 }
@@ -2156,9 +2023,9 @@ wxMouseState wxGetMouseState()
 void wxWindowGTK::Init()
 {
     // GTK specific
-    m_widget = (GtkWidget *) NULL;
-    m_wxwindow = (GtkWidget *) NULL;
-    m_focusWidget = (GtkWidget *) NULL;
+    m_widget = NULL;
+    m_wxwindow = NULL;
+    m_focusWidget = NULL;
 
     // position/size
     m_x = 0;
@@ -2167,14 +2034,12 @@ void wxWindowGTK::Init()
     m_height = 0;
 
     m_hasVMT = false;
-    m_isBeingDeleted = false;
 
-    m_showOnIdle= false;
+    m_showOnIdle = false;
 
     m_noExpose = false;
     m_nativeSizeEvent = false;
 
-    m_hasScrolling = false;
     m_isScrolling = false;
     m_mouseButtonDown = false;
 
@@ -2188,10 +2053,6 @@ void wxWindowGTK::Init()
     m_oldClientWidth =
     m_oldClientHeight = 0;
 
-    m_insertCallback = wxInsertChildInWindow;
-
-    m_hasFocus = false;
-
     m_clipPaintRegion = false;
 
     m_needsStyleChange = false;
@@ -2228,6 +2089,7 @@ bool wxWindowGTK::Create( wxWindow *parent,
 {
     // Get default border
     wxBorder border = GetBorder(style);
+
     style &= ~wxBORDER_MASK;
     style |= border;
 
@@ -2238,19 +2100,29 @@ bool wxWindowGTK::Create( wxWindow *parent,
         return false;
     }
 
+        // We should accept the native look
+#if 0
+        GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
+        scroll_class->scrollbar_spacing = 0;
+#endif
+
 
-    m_wxwindow = wxPizza::New(m_windowStyle);
+    m_wxwindow = wxPizza::New(m_windowStyle,this);
+#ifndef __WXUNIVERSAL__
+    if (HasFlag(wxPizza::BORDER_STYLES))
+    {
+        g_signal_connect(m_wxwindow, "parent_set",
+            G_CALLBACK(parent_set), this);
+    }
+#endif
     if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
         m_widget = m_wxwindow;
     else
     {
-        m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
+        m_widget = gtk_scrolled_window_new( NULL, NULL );
 
         GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
 
-        GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
-        scroll_class->scrollbar_spacing = 0;
-
         // 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
@@ -2309,12 +2181,15 @@ bool wxWindowGTK::Create( wxWindow *parent,
 
         gtk_widget_show( m_wxwindow );
     }
+    g_object_ref(m_widget);
 
     if (m_parent)
         m_parent->DoAddChild( this );
 
     m_focusWidget = m_wxwindow;
 
+    SetCanFocus(AcceptsFocus());
+
     PostCreation();
 
     return true;
@@ -2324,13 +2199,14 @@ wxWindowGTK::~wxWindowGTK()
 {
     SendDestroyEvent();
 
-    if (g_focusWindow == this)
-        g_focusWindow = NULL;
+    if (gs_currentFocus == this)
+        gs_currentFocus = NULL;
+    if (gs_pendingFocus == this)
+        gs_pendingFocus = NULL;
 
-    if ( g_delayedFocus == this )
-        g_delayedFocus = NULL;
+    if ( gs_deferredFocusOut == this )
+        gs_deferredFocusOut = NULL;
 
-    m_isBeingDeleted = true;
     m_hasVMT = false;
 
     // destroy children before destroying this window itself
@@ -2354,17 +2230,17 @@ wxWindowGTK::~wxWindowGTK()
     // delete before the widgets to avoid a crash on solaris
     delete m_imData;
 
-    if (m_wxwindow && (m_wxwindow != m_widget))
-    {
-        gtk_widget_destroy( m_wxwindow );
-        m_wxwindow = (GtkWidget*) NULL;
-    }
-
     if (m_widget)
     {
-        gtk_widget_destroy( m_widget );
-        m_widget = (GtkWidget*) NULL;
+        // Note that gtk_widget_destroy() does not destroy the widget, it just
+        // emits the "destroy" signal. The widget is not actually destroyed
+        // until its reference count drops to zero.
+        gtk_widget_destroy(m_widget);
+        // Release our reference, should be the last one
+        g_object_unref(m_widget);
+        m_widget = NULL;
     }
+    m_wxwindow = NULL;
 }
 
 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos,  const wxSize &size )
@@ -2410,15 +2286,6 @@ void wxWindowGTK::PostCreation()
 
         g_signal_connect (m_imData->context, "commit",
                           G_CALLBACK (gtk_wxwindow_commit_cb), this);
-
-        // border drawing
-#ifndef __WXUNIVERSAL__
-        if (HasFlag(wxPizza::BORDER_STYLES))
-        {
-            g_signal_connect(m_widget, "expose_event",
-                G_CALLBACK(expose_event_border), this);
-        }
-#endif
     }
 
     // focus handling
@@ -2537,8 +2404,16 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget )
                       G_CALLBACK (gtk_window_button_release_callback), this);
     g_signal_connect (widget, "motion_notify_event",
                       G_CALLBACK (gtk_window_motion_notify_callback), this);
+
     g_signal_connect (widget, "scroll_event",
                       G_CALLBACK (window_scroll_event), this);
+    if (m_scrollBar[ScrollDir_Horz])
+        g_signal_connect (m_scrollBar[ScrollDir_Horz], "scroll_event",
+                      G_CALLBACK (window_scroll_event_hscrollbar), this);
+    if (m_scrollBar[ScrollDir_Vert])
+        g_signal_connect (m_scrollBar[ScrollDir_Vert], "scroll_event",
+                      G_CALLBACK (window_scroll_event), this);
+
     g_signal_connect (widget, "popup_menu",
                      G_CALLBACK (wxgtk_window_popup_menu_callback), this);
     g_signal_connect (widget, "enter_notify_event",
@@ -2563,7 +2438,10 @@ bool wxWindowGTK::Destroy()
 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
 {
     gtk_widget_set_size_request(m_widget, 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);
 }
 
@@ -2614,8 +2492,6 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags
     if (height != -1)
         m_height = height;
 
-    ConstrainSize();
-
     if (m_parent->m_wxwindow)
     {
         wxPizza* pizza = WX_PIZZA(m_parent->m_wxwindow);
@@ -2661,10 +2537,16 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags
             event.SetEventObject( this );
             HandleWindowEvent( event );
         }
+    } else 
+    if (sizeFlags & wxSIZE_FORCE_EVENT)
+    {
+        wxSizeEvent event( wxSize(m_width,m_height), GetId() );
+        event.SetEventObject( this );
+        HandleWindowEvent( event );
     }
 }
 
-bool wxWindowGTK::GtkShowFromOnIdle()
+bool wxWindowGTK::GTKShowFromOnIdle()
 {
     if (IsShown() && m_showOnIdle && !GTK_WIDGET_VISIBLE (m_widget))
     {
@@ -2687,8 +2569,11 @@ bool wxWindowGTK::GtkShowFromOnIdle()
 
 void wxWindowGTK::OnInternalIdle()
 {
+    if ( gs_deferredFocusOut )
+        GTKHandleDeferredFocusOut();
+
     // Check if we have to show window now
-    if (GtkShowFromOnIdle()) return;
+    if (GTKShowFromOnIdle()) return;
 
     if ( m_dirtyTabOrder )
     {
@@ -2736,7 +2621,7 @@ void wxWindowGTK::OnInternalIdle()
         }
     }
 
-    if (wxUpdateUIEvent::CanUpdate(this) && IsShown())
+    if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen())
         UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
 }
 
@@ -2764,21 +2649,58 @@ void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
     int w = m_width;
     int h = m_height;
 
-    if (m_wxwindow)
+    if ( m_wxwindow )
     {
-        int dw = 0;
-        int dh = 0;
+        // if window is scrollable, account for scrollbars
+        if ( GTK_IS_SCROLLED_WINDOW(m_widget) )
+        {
+            GtkPolicyType policy[ScrollDir_Max];
+            gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget),
+                                           &policy[ScrollDir_Horz],
+                                           &policy[ScrollDir_Vert]);
+
+            for ( int i = 0; i < ScrollDir_Max; i++ )
+            {
+                // don't account for the scrollbars we don't have
+                GtkRange * const range = m_scrollBar[i];
+                if ( !range )
+                    continue;
+
+                // nor for the ones we have but don't current show
+                switch ( policy[i] )
+                {
+                    case GTK_POLICY_NEVER:
+                        // never shown so doesn't take any place
+                        continue;
+
+                    case GTK_POLICY_ALWAYS:
+                        // no checks necessary
+                        break;
+
+                    case GTK_POLICY_AUTOMATIC:
+                        // may be shown or not, check
+                        GtkAdjustment *adj = gtk_range_get_adjustment(range);
+                        if ( adj->upper <= adj->page_size )
+                            continue;
+                }
+
+                GtkScrolledWindowClass *scroll_class =
+                    GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
 
-        if (m_hasScrolling)
-            GetScrollbarWidth(m_widget, dw, dh);
+                GtkRequisition req;
+                gtk_widget_size_request(GTK_WIDGET(range), &req);
+                if (i == ScrollDir_Horz)
+                    h -= req.height + scroll_class->scrollbar_spacing;
+                else
+                    w -= req.width + scroll_class->scrollbar_spacing;
+            }
+        }
 
         int border_x, border_y;
         WX_PIZZA(m_wxwindow)->get_border_widths(border_x, border_y);
-        dw += 2 * border_x;
-        dh += 2 * border_y;
+        w -= 2 * border_x;
+        h -= 2 * border_y;
 
-        w -= dw;
-        h -= dh;
         if (w < 0)
             w = 0;
         if (h < 0)
@@ -2804,7 +2726,7 @@ void wxWindowGTK::DoGetPosition( int *x, int *y ) const
 
     if (m_x == -1 && m_y == -1)
     {
-        GdkWindow *source = (GdkWindow *) NULL;
+        GdkWindow *source = NULL;
         if (m_wxwindow)
             source = m_wxwindow->window;
         else
@@ -2819,8 +2741,8 @@ void wxWindowGTK::DoGetPosition( int *x, int *y ) const
             if (m_parent)
                 m_parent->ScreenToClient(&org_x, &org_y);
 
-            wx_const_cast(wxWindowGTK*, this)->m_x = org_x;
-            wx_const_cast(wxWindowGTK*, this)->m_y = org_y;
+            const_cast<wxWindowGTK*>(this)->m_x = org_x;
+            const_cast<wxWindowGTK*>(this)->m_y = org_y;
         }
     }
 
@@ -2834,7 +2756,7 @@ void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
 
     if (!m_widget->window) return;
 
-    GdkWindow *source = (GdkWindow *) NULL;
+    GdkWindow *source = NULL;
     if (m_wxwindow)
         source = m_wxwindow->window;
     else
@@ -2871,7 +2793,7 @@ void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
 
     if (!m_widget->window) return;
 
-    GdkWindow *source = (GdkWindow *) NULL;
+    GdkWindow *source = NULL;
     if (m_wxwindow)
         source = m_wxwindow->window;
     else
@@ -3044,90 +2966,204 @@ void wxWindowGTK::GetTextExtent( const wxString& string,
     g_object_unref (layout);
 }
 
-bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
+void wxWindowGTK::GTKDisableFocusOutEvent()
 {
-    if ( g_delayedFocus == this )
+    g_signal_handlers_block_by_func( m_focusWidget,
+                                (gpointer) gtk_window_focus_out_callback, this);
+}
+
+void wxWindowGTK::GTKEnableFocusOutEvent()
+{
+    g_signal_handlers_unblock_by_func( m_focusWidget,
+                                (gpointer) gtk_window_focus_out_callback, this);
+}
+
+bool wxWindowGTK::GTKHandleFocusIn()
+{
+    // Disable default focus handling for custom windows since the default GTK+
+    // handler issues a repaint
+    const bool retval = m_wxwindow ? true : false;
+
+
+    // NB: if there's still unprocessed deferred focus-out event (see
+    //     GTKHandleFocusOut() for explanation), we need to process it first so
+    //     that the order of focus events -- focus-out first, then focus-in
+    //     elsewhere -- is preserved
+    if ( gs_deferredFocusOut )
     {
-        if ( GTK_WIDGET_REALIZED(m_widget) )
+        if ( GTKNeedsToFilterSameWindowFocus() &&
+             gs_deferredFocusOut == this )
         {
-            gtk_widget_grab_focus(m_widget);
-            g_delayedFocus = NULL;
-
-            return true;
+            // GTK+ focus changed from this wxWindow back to itself, so don't
+            // emit any events at all
+            wxLogTrace(TRACE_FOCUS,
+                       "filtered out spurious focus change within %s(%p, %s)",
+                       GetClassInfo()->GetClassName(), this, GetLabel());
+            gs_deferredFocusOut = NULL;
+            return retval;
         }
+
+        // otherwise we need to send focus-out first
+        wxASSERT_MSG ( gs_deferredFocusOut != this,
+                       "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
+        GTKHandleDeferredFocusOut();
     }
 
-    return false;
+
+    wxLogTrace(TRACE_FOCUS,
+               "handling focus_in event for %s(%p, %s)",
+               GetClassInfo()->GetClassName(), this, GetLabel());
+
+    if (m_imData)
+        gtk_im_context_focus_in(m_imData->context);
+
+    gs_currentFocus = this;
+    gs_pendingFocus = NULL;
+
+#if wxUSE_CARET
+    // caret needs to be informed about focus change
+    wxCaret *caret = GetCaret();
+    if ( caret )
+    {
+        caret->OnSetFocus();
+    }
+#endif // wxUSE_CARET
+
+    // Notify the parent keeping track of focus for the kbd navigation
+    // purposes that we got it.
+    wxChildFocusEvent eventChildFocus(static_cast<wxWindow*>(this));
+    GTKProcessEvent(eventChildFocus);
+
+    wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId());
+    eventFocus.SetEventObject(this);
+    GTKProcessEvent(eventFocus);
+
+    return retval;
 }
 
-void wxWindowGTK::SetFocus()
+bool wxWindowGTK::GTKHandleFocusOut()
 {
-    wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
-    if ( m_hasFocus )
+    // Disable default focus handling for custom windows since the default GTK+
+    // handler issues a repaint
+    const bool retval = m_wxwindow ? true : false;
+
+
+    // NB: If a control is composed of several GtkWidgets and when focus
+    //     changes from one of them to another within the same wxWindow, we get
+    //     a focus-out event followed by focus-in for another GtkWidget owned
+    //     by the same wx control. We don't want to generate two spurious
+    //     wxEVT_SET_FOCUS events in this case, so we defer sending wx events
+    //     from GTKHandleFocusOut() until we know for sure it's not coming back
+    //     (i.e. in GTKHandleFocusIn() or at idle time).
+    if ( GTKNeedsToFilterSameWindowFocus() )
     {
-        // don't do anything if we already have focus
-        return;
+        wxASSERT_MSG( gs_deferredFocusOut == NULL,
+                      "deferred focus out event already pending" );
+        wxLogTrace(TRACE_FOCUS,
+                   "deferring focus_out event for %s(%p, %s)",
+                   GetClassInfo()->GetClassName(), this, GetLabel());
+        gs_deferredFocusOut = this;
+        return retval;
     }
 
-    if (m_wxwindow)
+    GTKHandleFocusOutNoDeferring();
+
+    return retval;
+}
+
+void wxWindowGTK::GTKHandleFocusOutNoDeferring()
+{
+    wxLogTrace(TRACE_FOCUS,
+               "handling focus_out event for %s(%p, %s)",
+               GetClassInfo()->GetClassName(), this, GetLabel());
+
+    if (m_imData)
+        gtk_im_context_focus_out(m_imData->context);
+
+    if ( gs_currentFocus != this )
     {
-        // wxWindow::SetFocus() should really set the focus to
-        // this control, whatever the flags are
-        if (!GTK_WIDGET_CAN_FOCUS(m_wxwindow))
-            GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
+        // Something is terribly wrong, gs_currentFocus is out of sync with the
+        // real focus. We will reset it to NULL anyway, because after this
+        // focus-out event is handled, one of the following with happen:
+        //
+        // * either focus will go out of the app altogether, in which case
+        //   gs_currentFocus _should_ be NULL
+        //
+        // * or it goes to another control, in which case focus-in event will
+        //   follow immediately and it will set gs_currentFocus to the right
+        //   value
+        wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
+                   GetClassInfo()->GetClassName(), this, GetLabel());
+    }
+    gs_currentFocus = NULL;
 
-        if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
-        {
-            gtk_widget_grab_focus (m_wxwindow);
-        }
+#if wxUSE_CARET
+    // caret needs to be informed about focus change
+    wxCaret *caret = GetCaret();
+    if ( caret )
+    {
+        caret->OnKillFocus();
     }
-    else
+#endif // wxUSE_CARET
+
+    wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
+    event.SetEventObject( this );
+    GTKProcessEvent( event );
+}
+
+/*static*/
+void wxWindowGTK::GTKHandleDeferredFocusOut()
+{
+    // NB: See GTKHandleFocusOut() for explanation. This function is called
+    //     from either GTKHandleFocusIn() or OnInternalIdle() to process
+    //     deferred event.
+    if ( gs_deferredFocusOut )
     {
-        // wxWindow::SetFocus() should really set the focus to
-        // this control, whatever the flags are
-        if (!GTK_WIDGET_CAN_FOCUS(m_widget))
-            GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS);
+        wxWindowGTK *win = gs_deferredFocusOut;
+        gs_deferredFocusOut = NULL;
 
-        if (GTK_IS_CONTAINER(m_widget))
-        {
-            if (GTK_IS_RADIO_BUTTON(m_widget))
-            {
-                gtk_widget_grab_focus (m_widget);
-                return;
-            }
+        wxLogTrace(TRACE_FOCUS,
+                   "processing deferred focus_out event for %s(%p, %s)",
+                   win->GetClassInfo()->GetClassName(), win, win->GetLabel());
 
-            gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
-        }
-        else
-        if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
-        {
+        win->GTKHandleFocusOutNoDeferring();
+    }
+}
 
-            if (!GTK_WIDGET_REALIZED(m_widget))
-            {
-                // we can't set the focus to the widget now so we remember that
-                // it should be focused and will do it later, during the idle
-                // time, as soon as we can
-                wxLogTrace(TRACE_FOCUS,
-                           _T("Delaying setting focus to %s(%s)"),
-                           GetClassInfo()->GetClassName(), GetLabel().c_str());
-
-                g_delayedFocus = this;
-            }
-            else
-            {
-                wxLogTrace(TRACE_FOCUS,
-                           _T("Setting focus to %s(%s)"),
-                           GetClassInfo()->GetClassName(), GetLabel().c_str());
+void wxWindowGTK::SetFocus()
+{
+    wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
 
-                gtk_widget_grab_focus (m_widget);
-            }
-        }
-        else
-        {
-           wxLogTrace(TRACE_FOCUS,
-                      _T("Can't set focus to %s(%s)"),
-                      GetClassInfo()->GetClassName(), GetLabel().c_str());
-        }
+    // Setting "physical" focus is not immediate in GTK+ and while
+    // gtk_widget_is_focus ("determines if the widget is the focus widget
+    // within its toplevel", i.e. returns true for one widget per TLW, not
+    // globally) returns true immediately after grabbing focus,
+    // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
+    // has focus at the moment) takes affect only after the window is shown
+    // (if it was hidden at the moment of the call) or at the next event loop
+    // iteration.
+    //
+    // Because we want to FindFocus() call immediately following
+    // foo->SetFocus() to return foo, we have to keep track of "pending" focus
+    // ourselves.
+    gs_pendingFocus = this;
+
+    GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget;
+
+    if ( GTK_IS_CONTAINER(widget) &&
+         !GTK_WIDGET_CAN_FOCUS(widget) )
+    {
+        wxLogTrace(TRACE_FOCUS,
+                   _T("Setting focus to a child of %s(%p, %s)"),
+                   GetClassInfo()->GetClassName(), this, GetLabel().c_str());
+        gtk_widget_child_focus(widget, GTK_DIR_TAB_FORWARD);
+    }
+    else
+    {
+        wxLogTrace(TRACE_FOCUS,
+                   _T("Setting focus to %s(%p, %s)"),
+                   GetClassInfo()->GetClassName(), this, GetLabel().c_str());
+        gtk_widget_grab_focus(widget);
     }
 }
 
@@ -3161,13 +3197,8 @@ bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
 
     wxASSERT( GTK_IS_WIDGET(m_widget) );
 
-    /* prevent GTK from deleting the widget arbitrarily */
-    gtk_widget_ref( m_widget );
-
     if (oldParent)
-    {
         gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
-    }
 
     wxASSERT( GTK_IS_WIDGET(m_widget) );
 
@@ -3178,14 +3209,10 @@ bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
             m_showOnIdle = true;
             gtk_widget_hide( m_widget );
         }
-
         /* insert GTK representation */
-        (*(newParent->m_insertCallback))(newParent, this);
+        newParent->AddChildGTK(this);
     }
 
-    /* reverse: prevent GTK from deleting the widget arbitrarily */
-    gtk_widget_unref( m_widget );
-
     SetLayoutDirection(wxLayout_Default);
 
     return true;
@@ -3200,7 +3227,7 @@ void wxWindowGTK::DoAddChild(wxWindowGTK *child)
     AddChild( child );
 
     /* insert GTK representation */
-    (*m_insertCallback)(this, child);
+    AddChildGTK(child);
 }
 
 void wxWindowGTK::AddChild(wxWindowBase *child)
@@ -3451,7 +3478,7 @@ void wxWindowGTK::WarpPointer( int x, int y )
     // We provide this function ourselves as it is
     // missing in GDK (top of this file).
 
-    GdkWindow *window = (GdkWindow*) NULL;
+    GdkWindow *window = NULL;
     if (m_wxwindow)
         window = m_wxwindow->window;
     else
@@ -3507,63 +3534,58 @@ bool wxWindowGTK::ScrollPages(int pages)
 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground),
                           const wxRect *rect)
 {
-    if (!m_widget)
-        return;
-    if (!m_widget->window)
+    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 );
+    }
+    else
     {
-        if (m_wxwindow->window == NULL) return;
+        // Just return if the widget or one of its ancestors isn't mapped
+        GtkWidget *w;
+        for (w = m_wxwindow; w != NULL; w = w->parent)
+            if (!GTK_WIDGET_MAPPED (w))
+                return;
 
-        GdkRectangle gdk_rect,
-                    *p;
         if (rect)
         {
-            gdk_rect.x = rect->x;
-            gdk_rect.y = rect->y;
-            gdk_rect.width = rect->width;
-            gdk_rect.height = rect->height;
+            int x = rect->x;
             if (GetLayoutDirection() == wxLayout_RightToLeft)
-                gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
-
-            p = &gdk_rect;
-        }
-        else // invalidate everything
-        {
-            p = NULL;
+                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( m_wxwindow->window, &r, TRUE );
         }
-
-        gdk_window_invalidate_rect(m_wxwindow->window, p, true);
+        else
+            gdk_window_invalidate_rect( m_wxwindow->window, NULL, TRUE );
     }
 }
 
 void wxWindowGTK::Update()
 {
-    GtkUpdate();
-
-    // when we call Update() we really want to update the window immediately on
-    // screen, even if it means flushing the entire queue and hence slowing down
-    // everything -- but it should still be done, it's just that Update() should
-    // be called very rarely
-    gdk_flush();
-}
-
-void wxWindowGTK::GtkUpdate()
-{
-    if (m_wxwindow && m_wxwindow->window)
-        gdk_window_process_updates(m_wxwindow->window, false);
-    if (m_widget && m_widget->window && (m_wxwindow != m_widget))
-        gdk_window_process_updates( m_widget->window, FALSE );
-
-    // for consistency with other platforms (and also because it's convenient
-    // to be able to update an entire TLW by calling Update() only once), we
-    // should also update all our children here
-    for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
-          node;
-          node = node->GetNext() )
+    if (m_widget && m_widget->window)
     {
-        node->GetData()->GtkUpdate();
+        GdkDisplay* display = gtk_widget_get_display(m_widget);
+        // Flush everything out to the server, and wait for it to finish.
+        // This ensures nothing will overwrite the drawing we are about to do.
+        gdk_display_sync(display);
+
+        gdk_window_process_updates(m_widget->window, TRUE);
+        
+        // Flush again, but no need to wait for it to finish
+        gdk_display_flush(display);
     }
 }
 
@@ -3572,7 +3594,6 @@ bool wxWindowGTK::DoIsExposed( int x, int y ) const
     return m_updateRegion.Contains(x, y) != wxOutRegion;
 }
 
-
 bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
 {
     if (GetLayoutDirection() == wxLayout_RightToLeft)
@@ -3652,7 +3673,7 @@ void wxWindowGTK::GtkSendPaintEvents()
     else
     {
         wxWindowDC dc( (wxWindow*)this );
-        dc.SetClippingRegion( m_updateRegion );
+        dc.SetDeviceClippingRegion( m_updateRegion );
 
         // Work around gtk-qt <= 0.60 bug whereby the window colour
         // remains grey
@@ -3706,10 +3727,10 @@ void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
     wxWindowBase::DoSetToolTip(tip);
 
     if (m_tooltip)
-        m_tooltip->Apply( (wxWindow *)this );
+        m_tooltip->GTKApply( (wxWindow *)this );
 }
 
-void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const gchar *tip )
+void wxWindowGTK::GTKApplyToolTip( GtkTooltips *tips, const gchar *tip )
 {
     gtk_tooltips_set_tip(tips, GetConnectWidget(), tip, NULL);
 }
@@ -3731,7 +3752,7 @@ bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
     // apply style change (forceStyle=true so that new style is applied
     // even if the bg colour changed from valid to wxNullColour)
     if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
-        ApplyWidgetStyle(true);
+        GTKApplyWidgetStyle(true);
 
     return true;
 }
@@ -3753,17 +3774,17 @@ bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
 
     // apply style change (forceStyle=true so that new style is applied
     // even if the bg colour changed from valid to wxNullColour):
-    ApplyWidgetStyle(true);
+    GTKApplyWidgetStyle(true);
 
     return true;
 }
 
-PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
+PangoContext *wxWindowGTK::GTKGetPangoDefaultContext()
 {
     return gtk_widget_get_pango_context( m_widget );
 }
 
-GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
+GtkRcStyle *wxWindowGTK::GTKCreateWidgetStyle(bool forceStyle)
 {
     // do we need to apply any changes at all?
     if ( !forceStyle &&
@@ -3832,9 +3853,9 @@ GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
     return style;
 }
 
-void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
+void wxWindowGTK::GTKApplyWidgetStyle(bool forceStyle)
 {
-    GtkRcStyle *style = CreateWidgetStyle(forceStyle);
+    GtkRcStyle *style = GTKCreateWidgetStyle(forceStyle);
     if ( style )
     {
         DoApplyWidgetStyle(style);
@@ -3895,11 +3916,103 @@ bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
     {
         // apply style change (forceStyle=true so that new style is applied
         // even if the bg colour changed from valid to wxNullColour):
-        ApplyWidgetStyle(true);
+        GTKApplyWidgetStyle(true);
+    }
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// Pop-up menu stuff
+// ----------------------------------------------------------------------------
+
+#if wxUSE_MENUS_NATIVE
+
+static void SetInvokingWindow( wxMenu *menu, wxWindow* win )
+{
+    menu->SetInvokingWindow( win );
+
+    wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
+    while (node)
+    {
+        wxMenuItem *menuitem = node->GetData();
+        if (menuitem->IsSubMenu())
+        {
+            SetInvokingWindow( menuitem->GetSubMenu(), win );
+        }
+
+        node = node->GetNext();
+    }
+}
+
+extern "C" {
+static
+void wxPopupMenuPositionCallback( GtkMenu *menu,
+                                  gint *x, gint *y,
+                                  gboolean * WXUNUSED(whatever),
+                                  gpointer user_data )
+{
+    // ensure that the menu appears entirely on screen
+    GtkRequisition req;
+    gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
+
+    wxSize sizeScreen = wxGetDisplaySize();
+    wxPoint *pos = (wxPoint*)user_data;
+
+    gint xmax = sizeScreen.x - req.width,
+         ymax = sizeScreen.y - req.height;
+
+    *x = pos->x < xmax ? pos->x : xmax;
+    *y = pos->y < ymax ? pos->y : ymax;
+}
+}
+
+bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
+{
+    wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
+
+    wxCHECK_MSG( menu != NULL, false, wxT("invalid popup-menu") );
+
+    SetInvokingWindow( menu, this );
+
+    menu->UpdateUI();
+
+    wxPoint pos;
+    gpointer userdata;
+    GtkMenuPositionFunc posfunc;
+    if ( x == -1 && y == -1 )
+    {
+        // use GTK's default positioning algorithm
+        userdata = NULL;
+        posfunc = NULL;
+    }
+    else
+    {
+        pos = ClientToScreen(wxPoint(x, y));
+        userdata = &pos;
+        posfunc = wxPopupMenuPositionCallback;
+    }
+
+    menu->m_popupShown = true;
+    gtk_menu_popup(
+                  GTK_MENU(menu->m_menu),
+                  NULL,           // parent menu shell
+                  NULL,           // parent menu item
+                  posfunc,                      // function to position it
+                  userdata,                     // client data
+                  0,                            // button used to activate it
+                  gtk_get_current_event_time()
+                );
+
+    while (menu->m_popupShown)
+    {
+        gtk_main_iteration();
     }
+
     return true;
 }
 
+#endif // wxUSE_MENUS_NATIVE
+
 #if wxUSE_DRAG_AND_DROP
 
 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
@@ -3908,12 +4021,12 @@ void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
 
     GtkWidget *dnd_widget = GetConnectWidget();
 
-    if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
+    if (m_dropTarget) m_dropTarget->GtkUnregisterWidget( dnd_widget );
 
     if (m_dropTarget) delete m_dropTarget;
     m_dropTarget = dropTarget;
 
-    if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
+    if (m_dropTarget) m_dropTarget->GtkRegisterWidget( dnd_widget );
 }
 
 #endif // wxUSE_DRAG_AND_DROP
@@ -3949,7 +4062,7 @@ bool wxWindowGTK::SetFont( const wxFont &font )
 
     // apply style change (forceStyle=true so that new style is applied
     // even if the font changed from valid to wxNullFont):
-    ApplyWidgetStyle(true);
+    GTKApplyWidgetStyle(true);
 
     return true;
 }
@@ -3958,7 +4071,7 @@ void wxWindowGTK::DoCaptureMouse()
 {
     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
 
-    GdkWindow *window = (GdkWindow*) NULL;
+    GdkWindow *window = NULL;
     if (m_wxwindow)
         window = m_wxwindow->window;
     else
@@ -3976,7 +4089,7 @@ void wxWindowGTK::DoCaptureMouse()
                           GDK_BUTTON_RELEASE_MASK |
                           GDK_POINTER_MOTION_HINT_MASK |
                           GDK_POINTER_MOTION_MASK),
-                      (GdkWindow *) NULL,
+                      NULL,
                       cursor->GetCursor(),
                       (guint32)GDK_CURRENT_TIME );
     g_captureWindow = this;
@@ -3989,9 +4102,9 @@ void wxWindowGTK::DoReleaseMouse()
 
     wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
 
-    g_captureWindow = (wxWindowGTK*) NULL;
+    g_captureWindow = NULL;
 
-    GdkWindow *window = (GdkWindow*) NULL;
+    GdkWindow *window = NULL;
     if (m_wxwindow)
         window = m_wxwindow->window;
     else
@@ -4032,11 +4145,7 @@ void wxWindowGTK::SetScrollbar(int orient,
     GtkRange* const sb = m_scrollBar[dir];
     wxCHECK_RET( sb, _T("this window is not scrollable") );
 
-    if (range > 0)
-    {
-        m_hasScrolling = true;
-    }
-    else
+    if (range <= 0)
     {
         // GtkRange requires upper > lower
         range =
@@ -4085,7 +4194,7 @@ int wxWindowGTK::GetScrollThumb(int orient) const
     GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
     wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
 
-    return int(sb->adjustment->page_size);
+    return wxRound(sb->adjustment->page_size);
 }
 
 int wxWindowGTK::GetScrollPos( int orient ) const
@@ -4093,7 +4202,7 @@ int wxWindowGTK::GetScrollPos( int orient ) const
     GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
     wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
 
-    return int(sb->adjustment->value + 0.5);
+    return wxRound(sb->adjustment->value);
 }
 
 int wxWindowGTK::GetScrollRange( int orient ) const
@@ -4101,7 +4210,7 @@ int wxWindowGTK::GetScrollRange( int orient ) const
     GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
     wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
 
-    return int(sb->adjustment->upper);
+    return wxRound(sb->adjustment->upper);
 }
 
 // Determine if increment is the same as +/-x, allowing for some small
@@ -4113,23 +4222,21 @@ static inline bool IsScrollIncrement(double increment, double x)
     return fabs(increment - fabs(x)) < tolerance;
 }
 
-wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
+wxEventType wxWindowGTK::GTKGetScrollEventType(GtkRange* range)
 {
-    DEBUG_MAIN_THREAD
-
     wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
 
     const int barIndex = range == m_scrollBar[1];
     GtkAdjustment* adj = range->adjustment;
 
-    const int value = int(adj->value + 0.5);
+    const int value = wxRound(adj->value);
 
     // save previous position
     const double oldPos = m_scrollPos[barIndex];
     // update current position
     m_scrollPos[barIndex] = adj->value;
     // If event should be ignored, or integral position has not changed
-    if (!m_hasVMT || g_blockEventsOnDrag || value == int(oldPos + 0.5))
+    if (!m_hasVMT || g_blockEventsOnDrag || value == wxRound(oldPos))
     {
         return wxEVT_NULL;
     }
@@ -4196,7 +4303,7 @@ void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
 #endif // wxUSE_CARET
 }
 
-void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
+void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
 {
     //RN: Note that static controls usually have no border on gtk, so maybe
     //it makes sense to treat that as simply no border at the wx level
@@ -4274,3 +4381,85 @@ GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
         window = m_wxwindow->window;
     return window;
 }
+
+// ----------------------------------------------------------------------------
+// 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, void* WXUNUSED(data))
+{
+    wxASSERT( w && !GTK_WIDGET_NO_WINDOW(w) );
+    wxASSERT( GTK_WIDGET_REALIZED(w) );
+
+    g_signal_handlers_disconnect_by_func
+    (
+        w,
+        (void*)wx_frozen_widget_realize,
+        NULL
+    );
+
+    gdk_window_freeze_updates(w->window);
+}
+
+} // extern "C"
+
+void wxWindowGTK::GTKFreezeWidget(GtkWidget *w)
+{
+    if ( !w || GTK_WIDGET_NO_WINDOW(w) )
+        return; // window-less widget, cannot be frozen
+
+    if ( !GTK_WIDGET_REALIZED(w) )
+    {
+        // 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),
+            NULL
+        );
+        return;
+    }
+
+    gdk_window_freeze_updates(w->window);
+}
+
+void wxWindowGTK::GTKThawWidget(GtkWidget *w)
+{
+    if ( !w || GTK_WIDGET_NO_WINDOW(w) )
+        return; // window-less widget, cannot be frozen
+
+    if ( !GTK_WIDGET_REALIZED(w) )
+    {
+        // the widget wasn't realized yet, no need to thaw
+        g_signal_handlers_disconnect_by_func
+        (
+            w,
+            (void*)wx_frozen_widget_realize,
+            NULL
+        );
+        return;
+    }
+
+    gdk_window_thaw_updates(w->window);
+}
+
+void wxWindowGTK::DoFreeze()
+{
+    GTKFreezeWidget(m_widget);
+    if ( m_wxwindow && m_widget != m_wxwindow )
+        GTKFreezeWidget(m_wxwindow);
+}
+
+void wxWindowGTK::DoThaw()
+{
+    GTKThawWidget(m_widget);
+    if ( m_wxwindow && m_widget != m_wxwindow )
+        GTKThawWidget(m_wxwindow);
+}