]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk1/window.cpp
Doc tweaks
[wxWidgets.git] / src / gtk1 / window.cpp
index e805789afc1399c9208392c92d730341a4b55370..b4f55985b8139a96122d1c3d0f83eaedebc76e2f 100644 (file)
@@ -28,6 +28,7 @@
 #include "wx/dialog.h"
 #include "wx/msgdlg.h"
 #include "wx/module.h"
+#include "wx/combobox.h"
 
 #if wxUSE_DRAG_AND_DROP
     #include "wx/dnd.h"
@@ -56,7 +57,7 @@
     #include "wx/thread.h"
 #endif
 
-#include <math.h>
+#include "wx/math.h"
 #include <ctype.h>
 
 #include "wx/gtk/private.h"
 #include <pango/pangox.h>
 #endif
 
-#ifdef __WXGTK20__
-    #define SET_CONTAINER_FOCUS(w, d) gtk_widget_child_focus((w), (d))
-#else
-    #define SET_CONTAINER_FOCUS(w, d) gtk_container_focus(GTK_CONTAINER(w), (d))
-#endif
 
 #ifdef __WXGTK20__
     #ifdef HAVE_XIM
@@ -235,28 +231,17 @@ static GdkGC *g_eraseGC = NULL;
 static wxWindowGTK  *g_captureWindow = (wxWindowGTK*) NULL;
 static bool g_captureWindowHasMouse = FALSE;
 
-/* extern */ wxWindowGTK  *g_focusWindow = (wxWindowGTK*) NULL;
+wxWindowGTK  *g_focusWindow = (wxWindowGTK*) 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
-static wxWindowGTK *g_focusWindowLast = (wxWindowGTK*) NULL;
-
-// the frame that is currently active (i.e. its child has focus). It is
-// used to generate wxActivateEvents
-static wxWindowGTK *g_activeFrame = (wxWindowGTK*) NULL;
-static bool g_activeFrameLostFocus = FALSE;
+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;
 
-// if we detect that the app has got/lost the focus, we set this variable to
-// either TRUE or FALSE and an activate event will be sent during the next
-// OnIdle() call and it is reset to -1: this value means that we shouldn't
-// send any activate events at all
-static int        g_sendActivateEvent = -1;
-
 // hack: we need something to pass to gtk_menu_popup, so we store the time of
 // the last click here
 static guint32 gs_timeLastClick = 0; 
@@ -492,6 +477,34 @@ void wxgtk_window_size_request_callback(GtkWidget *widget,
     requisition->width = w;
 }
 
+
+extern "C"
+void wxgtk_combo_size_request_callback(GtkWidget *widget,
+                                       GtkRequisition *requisition,
+                                       wxComboBox *win)
+{
+    // This callback is actually hooked into the text entry
+    // of the combo box, not the GtkHBox.
+    
+    int w, h;
+    win->GetSize( &w, &h );
+    if (w < 2)
+        w = 2;
+    if (h < 2)
+        h = 2;
+
+    GtkCombo *gcombo = GTK_COMBO(win->m_widget);
+    
+    GtkRequisition entry_req;
+    entry_req.width = 2;
+    entry_req.height = 2;
+    (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo->button) )->size_request )
+        (gcombo->button, &entry_req );
+    
+    requisition->width = w - entry_req.width;
+    requisition->height = entry_req.height+4;  // TODO: why +4?
+}
+
 //-----------------------------------------------------------------------------
 // "expose_event" of m_wxwindow
 //-----------------------------------------------------------------------------
@@ -632,7 +645,7 @@ static void gtk_window_draw_callback( GtkWidget *widget,
 #ifndef __WXUNIVERSAL__
     GtkPizza *pizza = GTK_PIZZA (widget);
 
-    if (win->GetThemeEnabled())
+    if (win->GetThemeEnabled() && win->GetBackgroundStyle() == wxBG_STYLE_SYSTEM)
     {
         wxWindow *parent = win->GetParent();
         while (parent && !parent->IsTopLevel())
@@ -965,6 +978,36 @@ static inline bool wxIsAsciiKeysym(KeySym ks)
     return ks < 256;
 }
 
+static void wxFillOtherKeyEventFields(wxKeyEvent& event,
+                                      wxWindowGTK *win,
+                                      GdkEventKey *gdk_event)
+{
+    int x = 0;
+    int y = 0;
+    GdkModifierType state;
+    if (gdk_event->window)
+        gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
+
+    event.SetTimestamp( gdk_event->time );
+    event.SetId(win->GetId());
+    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_rawCode = (wxUint32) gdk_event->keyval;
+    event.m_rawFlags = 0;
+#if wxUSE_UNICODE
+    event.m_uniChar = gdk_keyval_to_unicode(gdk_event->keyval);
+#endif
+    wxGetMousePosition( &x, &y );
+    win->ScreenToClient( &x, &y );
+    event.m_x = x;
+    event.m_y = y;
+    event.SetEventObject( win );
+}
+
+
 static bool
 wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
                            wxWindowGTK *win,
@@ -1060,29 +1103,32 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
         return FALSE;
 
     // now fill all the other fields
-    int x = 0;
-    int y = 0;
-    GdkModifierType state;
-    if (gdk_event->window)
-        gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
-
-    event.SetTimestamp( gdk_event->time );
-    event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
-    event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
-    event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
-    event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0;
+    wxFillOtherKeyEventFields(event, win, gdk_event);
+    
     event.m_keyCode = key_code;
-    event.m_scanCode = gdk_event->keyval;
-    event.m_rawCode = (wxUint32) gdk_event->keyval;
-    event.m_rawFlags = 0;
-    event.m_x = x;
-    event.m_y = y;
-    event.SetEventObject( win );
 
     return TRUE;
 }
 
 
+#ifdef __WXGTK20__
+struct wxGtkIMData
+{
+    GtkIMContext *context;
+    GdkEventKey  *lastKeyEvent;
+
+    wxGtkIMData()
+    {
+        context = gtk_im_multicontext_new();
+        lastKeyEvent = NULL;
+    }
+    ~wxGtkIMData()
+    {
+        g_object_unref(context);
+    }
+};
+#endif
+
 static gint gtk_window_key_press_callback( GtkWidget *widget,
                                            GdkEventKey *gdk_event,
                                            wxWindow *win )
@@ -1097,27 +1143,112 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
     if (g_blockEventsOnDrag)
         return FALSE;
 
-#ifdef __WXGTK20__
-    if (win->m_imContext)
-    {
-        // In GTK 2.0, we need to hand over the key event to an input method
-        // and the IM will emit a "commit" event containing the actual utf8
-        // character.  In that case the EVT_CHAR events will be sent from
-        // there.
-        if ( gtk_im_context_filter_keypress(win->m_imContext, gdk_event) )
-            return TRUE;
-    }
-#endif
 
     wxKeyEvent event( wxEVT_KEY_DOWN );
-    if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
+    bool ret = false;
+    bool return_after_IM = false;
+
+    if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
     {
-        // unknown key pressed, ignore (the event would be useless anyhow)
-        return FALSE;
+        // Emit KEY_DOWN event
+        ret = win->GetEventHandler()->ProcessEvent( event );
+    }
+    else 
+    {
+        // Return after IM processing as we cannot do
+        // anything with it anyhow.
+        return_after_IM = true;
+    }
+
+#ifdef __WXGTK20__
+    // 2005.01.26 modified by Hong Jen Yee (hzysoft@sina.com.tw):
+    // When we get a key_press event here, it could be originate
+    // from the current widget or its child widgets.  However, only the widget
+    // with the INPUT FOCUS can generate the INITIAL key_press event.  That is, 
+    // if the CURRENT widget doesn't have the FOCUS at all, this event definitely
+    // originated from its child widgets and shouldn't be passed to IM context.
+    // In fact, what a GTK+ IM should do is filtering keyEvents and convert them
+    // into text input ONLY WHEN THE WIDGET HAS INPUT FOCUS.  Besides, when current
+    // widgets has both IM context and input focus, the event should be filtered 
+    // by gtk_im_context_filter_keypress().
+    // Then, we should, according to GTK+ 2.0 API doc, return whatever it returns.
+    if ((!ret) && (win->m_imData != NULL) && ( wxWindow::FindFocus() == win ))
+    {
+        // 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, _T("Key event intercepted by IM"));
+            return true;
+        }
     }
+#endif
+    if (return_after_IM)
+        return false;
 
-    // Emit KEY_DOWN event
-    bool ret = win->GetEventHandler()->ProcessEvent( event );
+#ifndef __WXGTK20__     
+    // This is for GTK+ 1.2 only. The char event generatation for GTK+ 2.0 is done
+    // in the "commit" handler.
+    
+    // 2005.02.02 modified by Hong Jen Yee (hzysoft@sina.com.tw).
+    // In GTK+ 1.2, strings sent by IMs are also regarded as key_press events whose 
+    // keyCodes cannot be recognized by wxWidgets. These MBCS strings, however, are 
+    // composed of more than one character, which means gdk_event->length will always 
+    // greater than one. When gtk_event->length == 1, this may be an ASCII character
+    // and can be translated by wx.  However, when MBCS characters are sent by IM, 
+    // gdk_event->length will >= 2. So neither should we pass it to accelerator table,
+    // nor should we pass it to controls. The following explanation was excerpted
+    // from GDK documentation.
+    // gint length : the length of string.
+    // gchar *string : a null-terminated multi-byte string containing the composed
+    // characters resulting from the key press. When text is being input, in a GtkEntry
+    // for example, it is these characters which should be added to the input buffer.
+    // When using Input Methods to support internationalized text input, the composed
+    // characters appear here after the pre-editing has been completed.
+
+    if ( (!ret) && (gdk_event->length > 1) ) // If this event contains a pre-edited string from IM.
+    {
+        // We should translate this key event into wxEVT_CHAR not wxEVT_KEY_DOWN.
+        #if wxUSE_UNICODE   // GTK+ 1.2 is not UTF-8 based.
+            const wxWCharBuffer string = wxConvLocal.cMB2WC( gdk_event->string );
+            if( !string )
+                return false;
+        #else
+            const char* string = gdk_event->string;
+        #endif
+
+        // Implement OnCharHook by checking ancesteror top level windows
+        wxWindow *parent = win;
+        while (parent && !parent->IsTopLevel())
+            parent = parent->GetParent();
+
+        for( const wxChar* pstr = string; *pstr; pstr++ )
+        {
+        #if wxUSE_UNICODE
+            event.m_uniChar = *pstr;
+            // Backward compatible for ISO-8859-1
+            event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
+        #else
+            event.m_keyCode = *pstr;
+        #endif
+            if (parent)
+            {
+                event.SetEventType( wxEVT_CHAR_HOOK );
+                ret = parent->GetEventHandler()->ProcessEvent( event );
+            }
+            if (!ret)
+            {
+                event.SetEventType(wxEVT_CHAR);
+                win->GetEventHandler()->ProcessEvent( event );
+            }
+        }
+        return true;
+    }
+    
+#endif  // #ifndef  __WXGTK20__
 
 #if wxUSE_ACCEL
     if (!ret)
@@ -1149,15 +1280,16 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
         key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */);
         if ( !key_code )
         {
-            if ( gdk_event->length == 1 )
-            {
-                key_code = (unsigned char)gdk_event->string[0];
-            }
-            else if ( wxIsAsciiKeysym(keysym) )
+            if ( wxIsAsciiKeysym(keysym) )
             {
                 // ASCII key
                 key_code = (unsigned char)keysym;
             }
+            // gdk_event->string is actually deprecated
+            else if ( gdk_event->length == 1 )
+            {
+                key_code = (unsigned char)gdk_event->string[0];
+            }
         }
 
         if ( key_code )
@@ -1184,6 +1316,10 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
         }
     }
 
+
+
+
+
     // win is a control: tab can be propagated up
     if ( !ret &&
          ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) &&
@@ -1259,47 +1395,53 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context,
                            const gchar  *str,
                            wxWindow     *window)
 {
-    bool ret = FALSE;
-
     wxKeyEvent event( wxEVT_KEY_DOWN );
 
-#if wxUSE_UNICODE
-    event.m_uniChar = g_utf8_get_char( str );
+    // take modifiers, cursor position, timestamp etc. from the last
+    // key_press_event that was fed into Input Method:
+    if (window->m_imData->lastKeyEvent)
+    {
+        wxFillOtherKeyEventFields(event,
+                                  window, window->m_imData->lastKeyEvent);
+    }
 
-    // Backward compatible for ISO-8859
-    if (event.m_uniChar < 256)
-        event.m_keyCode = event.m_uniChar;
+#if wxUSE_UNICODE
+    const wxWCharBuffer data = wxConvUTF8.cMB2WC( (char*)str );
 #else
-    wchar_t unistr[2];
-    unistr[0] = g_utf8_get_char(str);
-    unistr[1] = 0;
-    wxCharBuffer ansistr(wxConvLocal.cWC2MB(unistr));
-    // We cannot handle characters that cannot be represented in 
-    // current locale's charset in non-Unicode mode:
-    if (ansistr.data() == NULL) return;
-
-    event.m_keyCode = ansistr[0u];
-#endif
-
-
-    // TODO:  We still need to set all the extra attributes of the
-    //        event, modifiers and such...
+    const wxWCharBuffer wdata = wxConvUTF8.cMB2WC( (char*)str );
+    const wxCharBuffer data = wxConvLocal.cWC2MB( wdata );
+#endif // wxUSE_UNICODE
+    if( !(const wxChar*)data )
+        return;
 
+    bool ret = false;
 
     // Implement OnCharHook by checking ancestor top level windows
     wxWindow *parent = window;
     while (parent && !parent->IsTopLevel())
         parent = parent->GetParent();
-    if (parent)
-    {
-        event.SetEventType( wxEVT_CHAR_HOOK );
-        ret = parent->GetEventHandler()->ProcessEvent( event );
-    }
 
-    if (!ret)
+    for( const wxChar* pstr = data; *pstr; pstr++ )
     {
-        event.SetEventType(wxEVT_CHAR);
-        ret = window->GetEventHandler()->ProcessEvent( event );
+#if wxUSE_UNICODE
+        event.m_uniChar = *pstr;
+        // Backward compatible for ISO-8859-1
+        event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
+        wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
+#else
+        event.m_keyCode = *pstr;
+#endif  // wxUSE_UNICODE
+        if (parent)
+        {
+            event.SetEventType( wxEVT_CHAR_HOOK );
+            ret = parent->GetEventHandler()->ProcessEvent( event );
+        }
+
+        if (!ret)
+        {
+            event.SetEventType(wxEVT_CHAR);
+            ret = window->GetEventHandler()->ProcessEvent( event );
+        }
     }
 }
 #endif
@@ -1362,6 +1504,7 @@ template<typename T> void InitMouseEvent(wxWindowGTK *win,
     if (event.GetEventType() == wxEVT_MOUSEWHEEL)
     {
        event.m_linesPerAction = 3;
+       event.m_wheelDelta = 120;
        if (((GdkEventButton*)gdk_event)->button == 4)
            event.m_wheelRotation = 120;
        else if (((GdkEventButton*)gdk_event)->button == 5)
@@ -1638,13 +1781,6 @@ static gint gtk_window_button_press_callback( GtkWidget *widget,
 
     gs_timeLastClick = gdk_event->time;
 
-/*
-    wxPrintf( wxT("2) OnButtonPress from ") );
-    if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
-        wxPrintf( win->GetClassInfo()->GetClassName() );
-    wxPrintf( wxT(".\n") );
-*/
-
 #ifndef __WXGTK20__
     if (event_type == wxEVT_LEFT_DCLICK)
     {
@@ -1665,6 +1801,23 @@ static gint gtk_window_button_press_callback( GtkWidget *widget,
         return TRUE;
     }
 
+    if (event_type == wxEVT_RIGHT_DOWN)
+    {
+        // generate a "context menu" event: this is similar to right mouse
+        // click under many GUIs except that it is generated differently
+        // (right up under MSW, ctrl-click under Mac, right down here) and
+        //
+        // (a) it's a command event and so is propagated to the parent
+        // (b) under some ports it can be generated from kbd too
+        // (c) it uses screen coords (because of (a))
+        wxContextMenuEvent evtCtx(
+            wxEVT_CONTEXT_MENU,
+            win->GetId(),
+            win->ClientToScreen(event.GetPosition()));
+        evtCtx.SetEventObject(win);
+        return win->GetEventHandler()->ProcessEvent(evtCtx);
+    }
+
     return FALSE;
 }
 
@@ -1716,20 +1869,6 @@ static gint gtk_window_button_release_callback( GtkWidget *widget,
     // same wxListBox hack as above
     win->FixUpMouseEvent(widget, event.m_x, event.m_y);
 
-    if ( event_type == wxEVT_RIGHT_UP )
-    {
-        // generate a "context menu" event: this is similar to wxEVT_RIGHT_UP
-        // except that:
-        //
-        // (a) it's a command event and so is propagated to the parent
-        // (b) under MSW it can be generated from kbd too
-        // (c) it uses screen coords (because of (a))
-        wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU,
-                                  win->GetId(),
-                                  win->ClientToScreen(event.GetPosition()));
-        (void)win->GetEventHandler()->ProcessEvent(evtCtx);
-    }
-
     if ( !g_captureWindow )
         win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
 
@@ -1849,6 +1988,7 @@ static gint gtk_window_wheel_callback (GtkWidget * widget,
     event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
     event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
     event.m_linesPerAction = 3;
+    event.m_wheelDelta = 120;
     if (gdk_event->direction == GDK_SCROLL_UP)
         event.m_wheelRotation = 120;
     else
@@ -1870,7 +2010,20 @@ static gint gtk_window_wheel_callback (GtkWidget * widget,
 
     return FALSE;
 }
-#endif
+
+//-----------------------------------------------------------------------------
+// "popup-menu"
+//-----------------------------------------------------------------------------
+static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
+{
+    wxContextMenuEvent event(
+        wxEVT_CONTEXT_MENU,
+        win->GetId(),
+        wxPoint(-1, -1));
+    event.SetEventObject(win);
+    return win->GetEventHandler()->ProcessEvent(event);
+}
+#endif // __WXGTK20__
 
 //-----------------------------------------------------------------------------
 // "focus_in_event"
@@ -1896,26 +2049,14 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget,
                                           wxWindow *win )
 {
     DEBUG_MAIN_THREAD
-
+    
     if (g_isIdle)
         wxapp_install_idle_handler();
 
-    if (!win->m_hasVMT) return FALSE;
-    if (g_blockEventsOnDrag) return FALSE;
-
-    switch ( g_sendActivateEvent )
-    {
-        case -1:
-            // we've got focus from outside, synthetize wxActivateEvent
-            g_sendActivateEvent = 1;
-            break;
-
-        case 0:
-            // another our window just lost focus, it was already ours before
-            // - don't send any wxActivateEvent
-            g_sendActivateEvent = -1;
-            break;
-    }
+#ifdef __WXGTK20__
+    if (win->m_imData)
+        gtk_im_context_focus_in(win->m_imData->context);
+#endif
 
     g_focusWindowLast =
     g_focusWindow = win;
@@ -1937,36 +2078,12 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget,
     }
 #endif // wxUSE_CARET
 
-    g_activeFrameLostFocus = FALSE;
-
-    wxWindowGTK *active = wxGetTopLevelParent(win);
-    if ( active != g_activeFrame )
-    {
-        if ( g_activeFrame )
-        {
-            wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from focus_in)"), g_activeFrame);
-            wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
-            event.SetEventObject(g_activeFrame);
-            g_activeFrame->GetEventHandler()->ProcessEvent(event);
-        }
-
-        wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), active);
-        g_activeFrame = active;
-        wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId());
-        event.SetEventObject(g_activeFrame);
-        g_activeFrame->GetEventHandler()->ProcessEvent(event);
-
-        // Don't send focus events in addition to activate
-        // if (win == g_activeFrame)
-        //    return TRUE;
-    }
-
     // does the window itself think that it has the focus?
     if ( !win->m_hasFocus )
     {
         // not yet, notify it
         win->m_hasFocus = TRUE;
-
+        
         if ( DoSendFocusEvents(win) )
         {
            gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
@@ -1988,29 +2105,14 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk
     if (g_isIdle)
         wxapp_install_idle_handler();
 
-    if (!win->m_hasVMT) return FALSE;
-    if (g_blockEventsOnDrag) return FALSE;
+#ifdef __WXGTK20__
+    if (win->m_imData)
+        gtk_im_context_focus_out(win->m_imData->context);
+#endif
 
     wxLogTrace( TRACE_FOCUS,
                 _T("%s: focus out"), win->GetName().c_str() );
 
-    if ( !g_activeFrameLostFocus && g_activeFrame )
-    {
-        // VZ: commenting this out because it does happen (although not easy
-        //     to reproduce, I only see it when using wxMiniFrame and not
-        //     always) and makes using Mahogany quite annoying
-#if 0
-        wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame,
-                        wxT("unfocusing window that hasn't gained focus properly") );
-#endif // 0
-
-        g_activeFrameLostFocus = TRUE;
-    }
-
-    // if the focus goes out of our app alltogether, OnIdle() will send
-    // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
-    // g_sendActivateEvent to -1
-    g_sendActivateEvent = 0;
 
     wxWindowGTK *winFocus = wxFindFocusedChild(win);
     if ( winFocus )
@@ -2041,11 +2143,11 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk
         wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
         event.SetEventObject( win );
 
-        if (win->GetEventHandler()->ProcessEvent( event ))
-        {
-            gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
-            return TRUE;
-        }
+        // even if we did process the event in wx code, still let GTK itself
+        // process it too as otherwise bad things happen, especially in GTK2
+        // where the text control simply aborts the program if it doesn't get
+        // the matching focus out event
+        (void)win->GetEventHandler()->ProcessEvent( event );
     }
 
     return FALSE;
@@ -2285,7 +2387,7 @@ static gint gtk_scrollbar_button_release_callback( GtkRange *widget,
 // because it is static and so couldn't be made virtual
 // ----------------------------------------------------------------------------
 
-wxWindow *wxWindowBase::FindFocus()
+wxWindow *wxWindowBase::DoFindFocus()
 {
     // the cast is necessary when we compile in wxUniversal mode
     return (wxWindow *)g_focusWindow;
@@ -2308,10 +2410,11 @@ gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
         wxapp_install_idle_handler();
         
 #ifdef __WXGTK20__
-    if (win->m_imContext)
+    if (win->m_imData)
     {
         GtkPizza *pizza = GTK_PIZZA( m_widget );
-        gtk_im_context_set_client_window( win->m_imContext, pizza->bin_window );
+        gtk_im_context_set_client_window( win->m_imData->context,
+                                          pizza->bin_window );
     }
 #endif
 
@@ -2567,11 +2670,14 @@ void wxWindowGTK::Init()
 
     m_clipPaintRegion = FALSE;
 
+    m_needsStyleChange = false;
+
     m_cursor = *wxSTANDARD_CURSOR;
 
 #ifdef __WXGTK20__
-    m_imContext = NULL;
+    m_imData = NULL;
     m_x11Context = NULL;
+    m_dirtyTabOrder = false;
 #else
 #ifdef HAVE_XIM
     m_ic = (GdkIC*) NULL;
@@ -2714,9 +2820,6 @@ wxWindowGTK::~wxWindowGTK()
     if (g_focusWindow == this)
         g_focusWindow = NULL;
 
-    if (g_activeFrame == this)
-        g_activeFrame = NULL;
-
     if ( g_delayedFocus == this )
         g_delayedFocus = NULL;
 
@@ -2746,6 +2849,10 @@ wxWindowGTK::~wxWindowGTK()
         gtk_widget_destroy( m_widget );
         m_widget = (GtkWidget*) NULL;
     }
+
+#ifdef __WXGTK20__
+    delete m_imData;
+#endif
 }
 
 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos,  const wxSize &size )
@@ -2794,12 +2901,12 @@ void wxWindowGTK::PostCreation()
 
 #ifdef __WXGTK20__
         // Create input method handler
-        m_imContext = gtk_im_multicontext_new();
+        m_imData = new wxGtkIMData;
 
         // Cannot handle drawing preedited text yet
-        gtk_im_context_set_use_preedit( m_imContext, FALSE );
+        gtk_im_context_set_use_preedit( m_imData->context, FALSE );
 
-        g_signal_connect (G_OBJECT (m_imContext), "commit",
+        g_signal_connect (G_OBJECT (m_imData->context), "commit",
                           G_CALLBACK (gtk_wxwindow_commit_cb), this);
 #endif
 
@@ -2815,14 +2922,17 @@ void wxWindowGTK::PostCreation()
 
     // focus handling
 
-    if (m_focusWidget == NULL)
-        m_focusWidget = m_widget;
-
-    gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event",
-        GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this );
+    if (!GTK_IS_WINDOW(m_widget))
+    {
+        if (m_focusWidget == NULL)
+            m_focusWidget = m_widget;
+            
+        gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event",
+            GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this );
 
-    gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_out_event",
-         GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this );
+        gtk_signal_connect_after( GTK_OBJECT(m_focusWidget), "focus_out_event",
+            GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this );
+    }
 
     // connect to the various key and mouse handlers
 
@@ -2850,18 +2960,27 @@ void wxWindowGTK::PostCreation()
                             GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this );
     }
 
-    if ( !GTK_IS_COMBO(m_widget))
+    if (GTK_IS_COMBO(m_widget))
+    {
+        GtkCombo *gcombo = GTK_COMBO(m_widget);
+    
+        gtk_signal_connect( GTK_OBJECT(gcombo->entry), "size_request",
+                            GTK_SIGNAL_FUNC(wxgtk_combo_size_request_callback),
+                            (gpointer) this );
+    }
+    else
     {
         // This is needed if we want to add our windows into native
-        // GTK control, such as the toolbar. With this callback, the
+        // 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. FIXME
-        // when moving to GTK 2.0.
+        // programmer). Sadly, it misbehaves for wxComboBox.
         gtk_signal_connect( GTK_OBJECT(m_widget), "size_request",
                             GTK_SIGNAL_FUNC(wxgtk_window_size_request_callback),
                             (gpointer) this );
     }
 
+    InheritAttributes();
+
     m_hasVMT = TRUE;
 
     // unless the window was created initially hidden (i.e. Hide() had been
@@ -2890,6 +3009,8 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget )
 #ifdef __WXGTK20__
     gtk_signal_connect( GTK_OBJECT(widget), "scroll_event",
       GTK_SIGNAL_FUNC(gtk_window_wheel_callback), (gpointer)this );
+    g_signal_connect(widget, "popup_menu",
+        G_CALLBACK(wxgtk_window_popup_menu_callback), this);
 #endif
 
     gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event",
@@ -2981,22 +3102,37 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags
         if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
         if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
 
-        int border = 0;
+        int left_border = 0;
+        int right_border = 0;
+        int top_border = 0;
         int bottom_border = 0;
 
-#ifndef __WXGTK20__
+        /* the default button has a border around it */
         if (GTK_WIDGET_CAN_DEFAULT(m_widget))
         {
-            /* the default button has a border around it */
-            border = 6;
+#ifdef __WXGTK20__
+            GtkBorder *default_border = NULL;
+            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;
+                g_free( default_border );
+            }
+#else
+            left_border = 6;
+            right_border = 6;
+            top_border = 6;
             bottom_border = 5;
-        }
 #endif
+        }
 
-        DoMoveWindow( m_x-border,
-                      m_y-border,
-                      m_width+2*border,
-                      m_height+border+bottom_border );
+        DoMoveWindow( m_x-top_border,
+                      m_y-left_border,
+                      m_width+left_border+right_border,
+                      m_height+top_border+bottom_border );
     }
 
     if (m_hasScrolling)
@@ -3029,32 +3165,20 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags
 
 void wxWindowGTK::OnInternalIdle()
 {
-    // Update invalidated regions.
-    GtkUpdate();
-
-    // Synthetize activate events.
-    if ( g_sendActivateEvent != -1 )
+#ifdef __WXGTK20__
+    if ( m_dirtyTabOrder )
+        RealizeTabOrder();
+#endif
+    // Update style if the window was not yet realized
+    // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
+    if (m_needsStyleChange)
     {
-        bool activate = g_sendActivateEvent != 0;
-
-        // do it only once
-        g_sendActivateEvent = -1;
-
-        wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast);
+        SetBackgroundStyle(GetBackgroundStyle());
+        m_needsStyleChange = false;
     }
 
-    if ( g_activeFrameLostFocus )
-    {
-        if ( g_activeFrame )
-        {
-            wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from idle)"), g_activeFrame);
-            wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
-            event.SetEventObject(g_activeFrame);
-            g_activeFrame->GetEventHandler()->ProcessEvent(event);
-            g_activeFrame = NULL;
-        }
-        g_activeFrameLostFocus = FALSE;
-    }
+    // Update invalidated regions.
+    GtkUpdate();
 
     wxCursor cursor = m_cursor;
     if (g_globalCursor.Ok()) cursor = g_globalCursor;
@@ -3328,7 +3452,7 @@ bool wxWindowGTK::Show( bool show )
         gtk_widget_hide( m_widget );
 
     wxShowEvent eventShow(GetId(), show);
-    eventShow.m_eventObject = this;
+    eventShow.SetEventObject(this);
 
     GetEventHandler()->ProcessEvent(eventShow);
 
@@ -3397,7 +3521,7 @@ int wxWindowGTK::GetCharHeight() const
 
     g_object_unref( G_OBJECT( layout ) );
 
-    return (int) (rect.height / PANGO_SCALE);
+    return (int) PANGO_PIXELS(rect.height);
 #else
     GdkFont *gfont = font.GetInternalFont( 1.0 );
 
@@ -3431,7 +3555,7 @@ int wxWindowGTK::GetCharWidth() const
 
     g_object_unref( G_OBJECT( layout ) );
 
-    return (int) (rect.width / PANGO_SCALE);
+    return (int) PANGO_PIXELS(rect.width);
 #else
     GdkFont *gfont = font.GetInternalFont( 1.0 );
 
@@ -3482,17 +3606,18 @@ void wxWindowGTK::GetTextExtent( const wxString& string,
         pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
 #endif
     }
-    PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
 
     PangoRectangle rect;
-    pango_layout_line_get_extents(line, NULL, &rect);
+    pango_layout_get_extents(layout, NULL, &rect);
 
-    if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE);
-    if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE);
+    if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
+    if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
     if (descent)
     {
-        // Do something about metrics here
-        (*descent) = 0;
+        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;  // ??
 
@@ -3509,7 +3634,6 @@ void wxWindowGTK::GetTextExtent( const wxString& string,
 void wxWindowGTK::SetFocus()
 {
     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
-
     if ( m_hasFocus )
     {
         // don't do anything if we already have focus
@@ -3525,8 +3649,16 @@ void wxWindowGTK::SetFocus()
     }
     else if (m_widget)
     {
+#ifdef __WXGTK20__
+        if (GTK_IS_CONTAINER(m_widget))
+        {
+            gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
+        }
+        else
+#endif
         if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
         {
+        
             if (!GTK_WIDGET_REALIZED(m_widget))
             {
                 // we can't set the focus to the widget now so we remember that
@@ -3547,11 +3679,14 @@ void wxWindowGTK::SetFocus()
                 gtk_widget_grab_focus (m_widget);
             }
         }
-        else if (GTK_IS_CONTAINER(m_widget))
+        else 
+#ifndef __WXGTK20__
+        if (GTK_IS_CONTAINER(m_widget))
         {
-            SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD );
+            gtk_container_focus( GTK_CONTAINER(m_widget), GTK_DIR_TAB_FORWARD );
         }
         else
+#endif
         {
            wxLogTrace(TRACE_FOCUS,
                       _T("Can't set focus to %s(%s)"),
@@ -3616,22 +3751,88 @@ void wxWindowGTK::DoAddChild(wxWindowGTK *child)
     (*m_insertCallback)(this, child);
 }
 
+#ifdef __WXGTK20__
+
+void wxWindowGTK::AddChild(wxWindowBase *child)
+{
+    wxWindowBase::AddChild(child);
+    m_dirtyTabOrder = true;
+    if (g_isIdle)
+        wxapp_install_idle_handler();
+}
+
+void wxWindowGTK::RemoveChild(wxWindowBase *child)
+{
+    wxWindowBase::RemoveChild(child);
+    m_dirtyTabOrder = true;
+    if (g_isIdle)
+        wxapp_install_idle_handler();
+}
+    
+void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
+{
+    wxWindowBase::DoMoveInTabOrder(win, move);
+    m_dirtyTabOrder = true;
+    if (g_isIdle)
+        wxapp_install_idle_handler();
+}
+
+void wxWindowGTK::RealizeTabOrder()
+{
+    if (m_wxwindow)
+    {
+        if (m_children.size() > 0)
+        {
+            GList *chain = NULL;
+            
+            for (wxWindowList::const_iterator i = m_children.begin();
+                    i != m_children.end(); ++i)
+            {
+                chain = g_list_prepend(chain, (*i)->m_widget);
+            }
+            
+            chain = g_list_reverse(chain);
+            
+            gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
+            g_list_free(chain);
+        }
+        else
+        {
+            gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
+        }
+    }
+    
+    m_dirtyTabOrder = false;
+}
+
+#endif // __WXGTK20__
+
 void wxWindowGTK::Raise()
 {
     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
 
-    if (!m_widget->window) return;
-
-    gdk_window_raise( m_widget->window );
+    if (m_wxwindow && m_wxwindow->window)
+    {
+        gdk_window_raise( m_wxwindow->window );
+    }
+     else if (m_widget->window)
+    {
+        gdk_window_raise( m_widget->window );
+    }
 }
 
 void wxWindowGTK::Lower()
 {
     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
 
-    if (!m_widget->window) return;
-
-    gdk_window_lower( m_widget->window );
+    if (m_wxwindow && m_wxwindow->window)
+    {
+        gdk_window_lower( m_wxwindow->window );
+    }
+     else if (m_widget->window)
+    {
+        gdk_window_lower( m_widget->window );
+    }
 }
 
 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
@@ -3757,6 +3958,12 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
 void wxWindowGTK::Update()
 {
     GtkUpdate();
+
+    // when we call Update() we really want to update the window immediately on
+    // screen, even if itmeans 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()
@@ -3787,34 +3994,35 @@ void wxWindowGTK::GtkSendPaintEvents()
     // widget to draw on
     GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
 
-    if (GetThemeEnabled())
+    if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
     {
         // find ancestor from which to steal background
-        wxWindow *parent = GetParent();
-        while (parent && !parent->IsTopLevel())
-            parent = parent->GetParent();
+        wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
         if (!parent)
             parent = (wxWindow*)this;
 
-        wxRegionIterator upd( m_updateRegion );
-        while (upd)
+        if (GTK_WIDGET_MAPPED(parent->m_widget))
         {
-            GdkRectangle rect;
-            rect.x = upd.GetX();
-            rect.y = upd.GetY();
-            rect.width = upd.GetWidth();
-            rect.height = upd.GetHeight();
-
-            gtk_paint_flat_box( parent->m_widget->style,
-                        pizza->bin_window,
-                        (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
-                        GTK_SHADOW_NONE,
-                        &rect,
-                        parent->m_widget,
-                        (char *)"base",
-                        0, 0, -1, -1 );
-
-            upd ++;
+            wxRegionIterator upd( m_updateRegion );
+            while (upd)
+            {
+                GdkRectangle rect;
+                rect.x = upd.GetX();
+                rect.y = upd.GetY();
+                rect.width = upd.GetWidth();
+                rect.height = upd.GetHeight();
+
+                gtk_paint_flat_box( parent->m_widget->style,
+                            pizza->bin_window,
+                            (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
+                            GTK_SHADOW_NONE,
+                            &rect,
+                            parent->m_widget,
+                            (char *)"base",
+                            0, 0, -1, -1 );
+
+                ++upd;
+            }
         }
     }
     else
@@ -3841,7 +4049,7 @@ void wxWindowGTK::GtkSendPaintEvents()
         wxEraseEvent erase_event( GetId(), &dc );
         erase_event.SetEventObject( this );
 
-        if (!GetEventHandler()->ProcessEvent(erase_event))
+        if (!GetEventHandler()->ProcessEvent(erase_event) && GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
         {
             if (!g_eraseGC)
             {
@@ -3966,8 +4174,9 @@ 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):
-    ApplyWidgetStyle(true);
+    // even if the bg colour changed from valid to wxNullColour)
+    if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
+        ApplyWidgetStyle(true);
 
     return true;
 }
@@ -4082,17 +4291,55 @@ void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
         DoApplyWidgetStyle(style);
         gtk_rc_style_unref(style);
     }
+
+    // Style change may affect GTK+'s size calculation:
+    InvalidateBestSize();
 }
 
 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
 {
     if (m_wxwindow)
-        // should we also do m_widget in this case?
         gtk_widget_modify_style(m_wxwindow, style);
-    else
-        gtk_widget_modify_style(m_widget, style);
+    gtk_widget_modify_style(m_widget, style);
 }
 
+bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
+{
+    wxWindowBase::SetBackgroundStyle(style);
+    
+    if (style == wxBG_STYLE_CUSTOM)
+    {
+        GdkWindow *window = (GdkWindow*) NULL;
+        if (m_wxwindow)
+            window = GTK_PIZZA(m_wxwindow)->bin_window;
+        else
+            window = GetConnectWidget()->window;
+
+        if (window)
+        {
+            // Make sure GDK/X11 doesn't refresh the window
+            // automatically.
+            gdk_window_set_back_pixmap( window, None, False );
+#ifdef __X__
+            Display* display = GDK_WINDOW_DISPLAY(window);
+            XFlush(display);
+#endif
+            m_needsStyleChange = false;
+        }
+        else
+            // Do in OnIdle, because the window is not yet available
+            m_needsStyleChange = true;
+        
+        // Don't apply widget style, or we get a grey background
+    }
+    else
+    {
+        // apply style change (forceStyle=true so that new style is applied
+        // even if the bg colour changed from valid to wxNullColour):
+        ApplyWidgetStyle(true);
+    }
+    return true;
+}
 
 //-----------------------------------------------------------------------------
 // Pop-up menu stuff
@@ -4106,9 +4353,10 @@ void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting  )
     *is_waiting = FALSE;
 }
 
-static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win )
+void SetInvokingWindow( wxMenu *menu, wxWindow* win )
 {
     menu->SetInvokingWindow( win );
+    
     wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
     while (node)
     {
@@ -4149,16 +4397,20 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
 
     wxCHECK_MSG( menu != NULL, false, wxT("invalid popup-menu") );
 
+    // NOTE: if you change this code, you need to update
+    //       the same code in taskbar.cpp as well. This
+    //       is ugly code duplication, I know,
+
     SetInvokingWindow( menu, this );
 
     menu->UpdateUI();
 
     bool is_waiting = true;
 
-    gtk_signal_connect( GTK_OBJECT(menu->m_menu),
-                        "hide",
-                        GTK_SIGNAL_FUNC(gtk_pop_hide_callback),
-                        (gpointer)&is_waiting );
+    gulong handler = gtk_signal_connect( GTK_OBJECT(menu->m_menu),
+                                         "hide",
+                                         GTK_SIGNAL_FUNC(gtk_pop_hide_callback),
+                                         (gpointer)&is_waiting );
 
     wxPoint pos;
     gpointer userdata;
@@ -4195,6 +4447,8 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
         gtk_main_iteration();
     }
 
+    gtk_signal_disconnect(GTK_OBJECT(menu->m_menu), handler);
+
     return true;
 }