]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/window.cpp
use WS_EX_CONTROLPARENT to ensure correct Alt-mnemonic handling with nested panels
[wxWidgets.git] / src / gtk / window.cpp
index 4630c80600a237a5d655d888fa43c0fdba8536a0..61eef683dc393ad88699a83c97571ffe62019737 100644 (file)
@@ -274,6 +274,9 @@ extern bool g_mainThreadLocked;
 #define DEBUG_MAIN_THREAD
 #endif // Debug
 
+// the trace mask used for the focus debugging messages
+#define TRACE_FOCUS _T("focus")
+
 //-----------------------------------------------------------------------------
 // missing gdk functions
 //-----------------------------------------------------------------------------
@@ -509,6 +512,10 @@ static int gtk_window_expose_callback( GtkWidget *widget,
                                   gdk_event->area.y,
                                   gdk_event->area.width,
                                   gdk_event->area.height );
+    win->m_clearRegion.Union( gdk_event->area.x,
+                                  gdk_event->area.y,
+                                  gdk_event->area.width,
+                                  gdk_event->area.height );
 
     // Actual redrawing takes place in idle time.
     win->GtkUpdate();
@@ -1059,14 +1066,28 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
     if (g_blockEventsOnDrag)
         return FALSE;
 
-    wxKeyEvent event( wxEVT_KEY_DOWN );
+    bool ret = FALSE;
+    
+    wxKeyEvent event( wxEVT_CHAR_HOOK );    
     if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
     {
         // unknown key pressed, ignore (the event would be useless anyhow)
         return FALSE;
     }
 
-    bool ret = win->GetEventHandler()->ProcessEvent( event );
+    // Implement wxFrame::OnCharHook by checking ancestor.
+    wxWindow *parent = win;
+    while (parent && !parent->IsTopLevel())
+        parent = parent->GetParent();
+    
+    if (parent)
+        ret = parent->GetEventHandler()->ProcessEvent( event );
+
+    if (!ret)
+    {    
+        event.SetEventType(wxEVT_KEY_DOWN);
+        ret = win->GetEventHandler()->ProcessEvent( event );
+    }
 
 #if wxUSE_ACCEL
     if (!ret)
@@ -1090,7 +1111,7 @@ static gint gtk_window_key_press_callback( GtkWidget *widget,
 
     /* Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
        will only be sent if it is not in an accelerator table. */
-    if ( !ret )
+    if (!ret)
     {
         KeySym keysym = gdk_event->keyval;
         long key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */);
@@ -1226,6 +1247,10 @@ static gint gtk_window_key_release_callback( GtkWidget *widget,
 // the mouse events
 // ============================================================================
 
+// ----------------------------------------------------------------------------
+// mouse event processing helpers
+// ----------------------------------------------------------------------------
+
 // init wxMouseEvent with the info from gdk_event
 #define InitMouseEvent(win, event, gdk_event) \
     { \
@@ -1243,10 +1268,6 @@ static gint gtk_window_key_release_callback( GtkWidget *widget,
     event.m_y = (wxCoord)gdk_event->y - pt.y; \
     }
 
-// ----------------------------------------------------------------------------
-// mouse event processing helper
-// ----------------------------------------------------------------------------
-
 static void AdjustEventButtonState(wxMouseEvent& event)
 {
     // GDK reports the old state of the button for a button press event, but
@@ -1279,11 +1300,79 @@ static void AdjustEventButtonState(wxMouseEvent& event)
     }
 }
 
+// find the window to send the mouse event too
+static
+wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
+{
+    wxCoord xx = x;
+    wxCoord yy = y;
+
+    if (win->m_wxwindow)
+    {
+        GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
+        xx += pizza->xoffset;
+        yy += pizza->yoffset;
+    }
+
+    wxNode *node = win->GetChildren().First();
+    while (node)
+    {
+        wxWindowGTK *child = (wxWindowGTK*)node->Data();
+
+        node = node->Next();
+        if (!child->IsShown())
+            continue;
+
+        if (child->IsTransparentForMouse())
+        {
+            // wxStaticBox is transparent in the box itself
+            int xx1 = child->m_x;
+            int yy1 = child->m_y;
+            int xx2 = child->m_x + child->m_width;
+            int yy2 = child->m_x + child->m_height;
+
+            // left
+            if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
+            // right
+                ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
+            // top
+                ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
+            // bottom
+                ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
+            {
+                win = child;
+                x -= child->m_x;
+                y -= child->m_y;
+                break;
+            }
+
+        }
+        else
+        {
+            if ((child->m_wxwindow == (GtkWidget*) NULL) &&
+                (child->m_x <= xx) &&
+                (child->m_y <= yy) &&
+                (child->m_x+child->m_width  >= xx) &&
+                (child->m_y+child->m_height >= yy))
+            {
+                win = child;
+                x -= child->m_x;
+                y -= child->m_y;
+                break;
+            }
+        }
+    }
+
+    return win;
+}
+
 //-----------------------------------------------------------------------------
 // "button_press_event"
 //-----------------------------------------------------------------------------
 
-static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win )
+static gint gtk_window_button_press_callback( GtkWidget *widget,
+                                              GdkEventButton *gdk_event,
+                                              wxWindowGTK *win )
 {
     DEBUG_MAIN_THREAD
 
@@ -1354,77 +1443,15 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton
 
     AdjustEventButtonState(event);
 
-    // wxListBox actually get mouse events from the item
-
-    if (win->m_isListBox)
-    {
-        event.m_x += widget->allocation.x;
-        event.m_y += widget->allocation.y;
-    }
-
-    // Some control don't have their own X window and thus cannot get
-    // any events.
+    // wxListBox actually get 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);
 
-    if (!g_captureWindow)
-    {
-        wxCoord x = event.m_x;
-        wxCoord y = event.m_y;
-        if (win->m_wxwindow)
-        {
-            GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
-            x += pizza->xoffset;
-            y += pizza->yoffset;
-        }
-
-        wxNode *node = win->GetChildren().First();
-        while (node)
-        {
-            wxWindowGTK *child = (wxWindowGTK*)node->Data();
-
-            node = node->Next();
-            if (!child->IsShown())
-                continue;
-
-            if (child->m_isStaticBox)
-            {
-                // wxStaticBox is transparent in the box itself
-                int xx1 = child->m_x;
-                int yy1 = child->m_y;
-                int xx2 = child->m_x + child->m_width;
-                int yy2 = child->m_x + child->m_height;
-
-                // left
-                if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
-                // right
-                    ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
-                // top
-                    ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
-                // bottom
-                    ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
-                {
-                    win = child;
-                    event.m_x -= child->m_x;
-                    event.m_y -= child->m_y;
-                    break;
-                }
-
-            }
-            else
-            {
-                if ((child->m_wxwindow == (GtkWidget*) NULL) &&
-                    (child->m_x <= x) &&
-                    (child->m_y <= y) &&
-                    (child->m_x+child->m_width  >= x) &&
-                    (child->m_y+child->m_height >= y))
-                {
-                    win = child;
-                    event.m_x -= child->m_x;
-                    event.m_y -= child->m_y;
-                    break;
-                }
-            }
-        }
-    }
+    // find the correct window to send the event too: it may be a different one
+    // from the one which got it at GTK+ level because some control don't have
+    // their own X window and thus cannot get any events.
+    if ( !g_captureWindow )
+        win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
 
     event.SetEventObject( win );
 
@@ -1485,77 +1512,11 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto
 
     AdjustEventButtonState(event);
 
-    // wxListBox actually get mouse events from the item
-
-    if (win->m_isListBox)
-    {
-        event.m_x += widget->allocation.x;
-        event.m_y += widget->allocation.y;
-    }
+    // same wxListBox hack as above
+    win->FixUpMouseEvent(widget, event.m_x, event.m_y);
 
-    // Some control don't have their own X window and thus cannot get
-    // any events.
-
-    if (!g_captureWindow)
-    {
-        wxCoord x = event.m_x;
-        wxCoord y = event.m_y;
-        if (win->m_wxwindow)
-        {
-            GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
-            x += pizza->xoffset;
-            y += pizza->yoffset;
-        }
-
-        wxNode *node = win->GetChildren().First();
-        while (node)
-        {
-            wxWindowGTK *child = (wxWindowGTK*)node->Data();
-
-            node = node->Next();
-            if (!child->IsShown())
-                continue;
-
-            if (child->m_isStaticBox)
-            {
-                // wxStaticBox is transparent in the box itself
-                int xx1 = child->m_x;
-                int yy1 = child->m_y;
-                int xx2 = child->m_x + child->m_width;
-                int yy2 = child->m_x + child->m_height;
-
-                // left
-                if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
-                // right
-                    ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
-                // top
-                    ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
-                // bottom
-                    ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
-                {
-                    win = child;
-                    event.m_x -= child->m_x;
-                    event.m_y -= child->m_y;
-                    break;
-                }
-
-            }
-            else
-            {
-                if ((child->m_wxwindow == (GtkWidget*) NULL) &&
-                    (child->m_x <= x) &&
-                    (child->m_y <= y) &&
-                    (child->m_x+child->m_width  >= x) &&
-                    (child->m_y+child->m_height >= y))
-                {
-                    win = child;
-                    event.m_x -= child->m_x;
-                    event.m_y -= child->m_y;
-                    break;
-                }
-            }
-        }
-    }
+    if ( !g_captureWindow )
+        win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
 
     event.SetEventObject( win );
 
@@ -1626,66 +1587,7 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget,
     }
     else // no capture
     {
-        // Some control don't have their own X window and thus cannot get
-        // any events.
-
-        wxCoord x = event.m_x;
-        wxCoord y = event.m_y;
-        if (win->m_wxwindow)
-        {
-            GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
-            x += pizza->xoffset;
-            y += pizza->yoffset;
-        }
-
-        wxNode *node = win->GetChildren().First();
-        while (node)
-        {
-            wxWindowGTK *child = (wxWindowGTK*)node->Data();
-
-            node = node->Next();
-            if (!child->IsShown())
-                continue;
-
-            if (child->m_isStaticBox)
-            {
-                // wxStaticBox is transparent in the box itself
-                int xx1 = child->m_x;
-                int yy1 = child->m_y;
-                int xx2 = child->m_x + child->m_width;
-                int yy2 = child->m_x + child->m_height;
-
-                // left
-                if (((x >= xx1) && (x <= xx1+10) && (y >= yy1) && (y <= yy2)) ||
-                // right
-                    ((x >= xx2-10) && (x <= xx2) && (y >= yy1) && (y <= yy2)) ||
-                // top
-                    ((x >= xx1) && (x <= xx2) && (y >= yy1) && (y <= yy1+10)) ||
-                // bottom
-                    ((x >= xx1) && (x <= xx2) && (y >= yy2-1) && (y <= yy2)))
-                {
-                    win = child;
-                    event.m_x -= child->m_x;
-                    event.m_y -= child->m_y;
-                    break;
-                }
-
-            }
-            else
-            {
-                if ((child->m_wxwindow == (GtkWidget*) NULL) &&
-                    (child->m_x <= x) &&
-                    (child->m_y <= y) &&
-                    (child->m_x+child->m_width  >= x) &&
-                    (child->m_y+child->m_height >= y))
-                {
-                    win = child;
-                    event.m_x -= child->m_x;
-                    event.m_y -= child->m_y;
-                    break;
-                }
-            }
-        }
+        win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
     }
 
     event.SetEventObject( win );
@@ -1747,9 +1649,8 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget,
     g_focusWindowLast =
     g_focusWindow = win;
 
-#if 0
-    printf( "OnSetFocus 2 from %s\n", win->GetName().c_str() );
-#endif
+    wxLogTrace(TRACE_FOCUS,
+               _T("%s: focus in"), win->GetName().c_str());
 
 #ifdef HAVE_XIM
     if (win->m_ic)
@@ -1789,10 +1690,17 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget,
         //    return TRUE;
     }
 
-    if ( DoSendFocusEvents(win) )
+    // does the window itself think that it has the focus?
+    if ( !win->m_hasFocus )
     {
-       gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
-       return TRUE;
+        // not yet, notify it
+        win->m_hasFocus = TRUE;
+
+        if ( DoSendFocusEvents(win) )
+        {
+           gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
+           return TRUE;
+        }
     }
 
     return FALSE;
@@ -1812,9 +1720,8 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk
     if (!win->m_hasVMT) return FALSE;
     if (g_blockEventsOnDrag) return FALSE;
 
-#if 0
-    wxLogDebug( wxT("OnKillFocus from %s"), win->GetName().c_str() );
-#endif
+    wxLogTrace( TRACE_FOCUS,
+                _T("%s: focus out"), win->GetName().c_str() );
 
     if ( !g_activeFrameLostFocus && g_activeFrame )
     {
@@ -1854,13 +1761,20 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk
     }
 #endif // wxUSE_CARET
 
-    wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
-    event.SetEventObject( win );
-
-    if (win->GetEventHandler()->ProcessEvent( event ))
+    // don't send the window a kill focus event if it thinks that it doesn't
+    // have focus already
+    if ( win->m_hasFocus )
     {
-        gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
-        return TRUE;
+        win->m_hasFocus = FALSE;
+
+        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;
+        }
     }
 
     return FALSE;
@@ -2096,6 +2010,16 @@ wxWindow *wxWindowBase::FindFocus()
     return (wxWindow *)g_focusWindow;
 }
 
+//-----------------------------------------------------------------------------
+// "destroy" event
+//-----------------------------------------------------------------------------
+
+static void gtk_window_destroy_callback( GtkWidget* widget, wxWindow *win )
+{
+    wxWindowDestroyEvent event(win);
+    win->GetEventHandler()->ProcessEvent(event);
+}
+
 //-----------------------------------------------------------------------------
 // "realize" from m_widget
 //-----------------------------------------------------------------------------
@@ -2314,8 +2238,7 @@ static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
 
 wxWindow *wxGetActiveWindow()
 {
-    // the cast is necessary when we compile in wxUniversal mode
-    return (wxWindow *)g_focusWindow;
+    return wxWindow::FindFocus();
 }
 
 //-----------------------------------------------------------------------------
@@ -2367,11 +2290,8 @@ void wxWindowGTK::Init()
 
     m_insertCallback = (wxInsertChildFunction) NULL;
 
-    m_isStaticBox = FALSE;
-    m_isRadioButton = FALSE;
-    m_isListBox = FALSE;
-    m_isFrame = FALSE;
     m_acceptsFocus = FALSE;
+    m_hasFocus = FALSE;
 
     m_clipPaintRegion = FALSE;
 
@@ -2714,6 +2634,9 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget )
 
     gtk_signal_connect( GTK_OBJECT(widget), "leave_notify_event",
       GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this );
+      
+    gtk_signal_connect( GTK_OBJECT(widget), "destroy",
+      GTK_SIGNAL_FUNC(gtk_window_destroy_callback), (gpointer)this );
 }
 
 bool wxWindowGTK::Destroy()
@@ -3223,7 +3146,13 @@ void wxWindowGTK::GetTextExtent( const wxString& string,
 
 void wxWindowGTK::SetFocus()
 {
-    wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
+    wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
+
+    if ( m_hasFocus )
+    {
+        // don't do anything if we already have focus
+        return;
+    }
 
     if (m_wxwindow)
     {
@@ -3238,16 +3167,19 @@ void wxWindowGTK::SetFocus()
         {
             if (!GTK_WIDGET_REALIZED(m_widget))
             {
-                wxLogTrace(_T("focus"),
-                           _T("Delaying setting focus to %s(%s)\n"),
+                // 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(_T("focus"),
-                           _T("Setting focus to %s(%s)\n"),
+                wxLogTrace(TRACE_FOCUS,
+                           _T("Setting focus to %s(%s)"),
                            GetClassInfo()->GetClassName(), GetLabel().c_str());
 
                 gtk_widget_grab_focus (m_widget);
@@ -3259,11 +3191,11 @@ void wxWindowGTK::SetFocus()
         }
         else
         {
-           // ?
+           wxLogTrace(TRACE_FOCUS,
+                      _T("Can't set focus to %s(%s)"),
+                      GetClassInfo()->GetClassName(), GetLabel().c_str());
         }
     }
-
-    (void)DoSendFocusEvents(this);
 }
 
 bool wxWindowGTK::AcceptsFocus() const
@@ -3485,7 +3417,7 @@ void wxWindowGTK::GtkSendPaintEvents()
         while (parent && !parent->IsTopLevel())
             parent = parent->GetParent();
         if (!parent)
-            parent = this;
+            parent = (wxWindow*)this;
     
         wxRegionIterator upd( m_updateRegion );
         while (upd)
@@ -4217,7 +4149,7 @@ void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
 
     wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
-
+    
     // No scrolling requested.
     if ((dx == 0) && (dy == 0)) return;
 
@@ -4241,7 +4173,6 @@ void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
         GetClientSize( &cw, &ch );
         m_clearRegion.Intersect( 0, 0, cw, ch );
     }
-
     m_clipPaintRegion = TRUE;
 
     gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );