X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3bcc8d15815a0c3d8c515083561e296cac242bf0..88f23fdd8366d57d15cba42d152539ff9ccbdd39:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 688d7399cd..ee418a7c17 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -25,6 +25,7 @@ #include "wx/utils.h" #include "wx/dialog.h" #include "wx/msgdlg.h" +#include "wx/module.h" #if wxUSE_DRAG_AND_DROP #include "wx/dnd.h" @@ -39,7 +40,7 @@ #endif // wxUSE_CARET #if wxUSE_TEXTCTRL -#include "wx/textctrl.h" + #include "wx/textctrl.h" #endif #include "wx/menu.h" @@ -47,15 +48,16 @@ #include "wx/intl.h" #include "wx/settings.h" #include "wx/log.h" +#include "wx/fontutil.h" #ifdef __WXDEBUG__ #include "wx/thread.h" #endif #include +#include -#include -#include +#include "wx/gtk/private.h" #include #include #include @@ -65,6 +67,26 @@ #include "wx/gtk/win_gtk.h" +#ifdef __WXGTK20__ +#include +#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 + #undef HAVE_XIM + #endif +#endif + +#ifdef __WXGTK20__ +extern GtkContainerClass *pizza_parent_class; +#endif + //----------------------------------------------------------------------------- // documentation on internals //----------------------------------------------------------------------------- @@ -204,6 +226,8 @@ extern bool g_blockEventsOnDrag; extern bool g_blockEventsOnScroll; extern wxCursor g_globalCursor; +static GdkGC *g_eraseGC = NULL; + // mouse capture state: the window which has it and if the mouse is currently // inside it static wxWindowGTK *g_captureWindow = (wxWindowGTK*) NULL; @@ -214,13 +238,17 @@ static bool g_captureWindowHasMouse = FALSE; // 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; +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 wxWindowGTK *g_activeFrame = (wxWindowGTK*) NULL; static bool g_activeFrameLostFocus = FALSE; +// 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 @@ -238,7 +266,7 @@ extern bool g_mainThreadLocked; //----------------------------------------------------------------------------- #ifndef __WXGTK20__ -#define DISABLE_STYLE_IF_BROKEN_THEME 1 +#define DISABLE_STYLE_IF_BROKEN_THEME 0 #endif #ifdef __WXDEBUG__ @@ -248,47 +276,13 @@ extern bool g_mainThreadLocked; #else # define DEBUG_MAIN_THREAD #endif - -static gint gtk_debug_focus_in_callback( GtkWidget *WXUNUSED(widget), - GdkEvent *WXUNUSED(event), - const wxChar *WXUNUSED(name) ) -{ -/* - static bool s_done = FALSE; - if ( !s_done ) - { - wxLog::AddTraceMask("focus"); - s_done = TRUE; - } - wxLogTrace(wxT("FOCUS NOW AT: %s"), name); -*/ - - return FALSE; -} - -void debug_focus_in( GtkWidget* widget, const wxChar* name, const wxChar *window ) -{ - // suppress warnings about gtk_debug_focus_in_callback being unused with - // this "if ( 0 )" - if ( 0 ) - { - wxString tmp = name; - tmp += wxT(" FROM "); - tmp += window; - - wxChar *s = new wxChar[tmp.Length()+1]; - - wxStrcpy( s, tmp ); - - gtk_signal_connect( GTK_OBJECT(widget), "focus_in_event", - GTK_SIGNAL_FUNC(gtk_debug_focus_in_callback), (gpointer)s ); - } -} - #else #define DEBUG_MAIN_THREAD #endif // Debug +// the trace mask used for the focus debugging messages +#define TRACE_FOCUS _T("focus") + //----------------------------------------------------------------------------- // missing gdk functions //----------------------------------------------------------------------------- @@ -351,7 +345,7 @@ wxWindow *wxFindFocusedChild(wxWindowGTK *win) if ( winFocus == win ) return (wxWindow *)win; - for ( wxWindowList::Node *node = win->GetChildren().GetFirst(); + for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst(); node; node = node->GetNext() ) { @@ -363,16 +357,6 @@ wxWindow *wxFindFocusedChild(wxWindowGTK *win) return (wxWindow *)NULL; } -// Returns toplevel grandparent of given window: -static wxWindowGTK* wxGetTopLevelParent(wxWindowGTK *win) -{ - wxWindowGTK *p = win; - while (p && !p->IsTopLevel()) - p = p->GetParent(); - return p; -} - - static void draw_frame( GtkWidget *widget, wxWindowGTK *win ) { // wxUniversal widgets draw the borders and scrollbars themselves @@ -468,6 +452,11 @@ gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_even draw_frame( widget, win ); +#ifdef __WXGTK20__ + + (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); + +#endif return TRUE; } @@ -475,275 +464,31 @@ gint gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_even // "draw" of m_widget //----------------------------------------------------------------------------- +#ifndef __WXGTK20__ + static void gtk_window_own_draw_callback( GtkWidget *widget, GdkRectangle *WXUNUSED(rect), wxWindowGTK *win ) { draw_frame( widget, win ); } -//----------------------------------------------------------------------------- -// key code mapping routines -//----------------------------------------------------------------------------- - -static long map_to_unmodified_wx_keysym( GdkEventKey *event ) -{ - // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string - // but only event->keyval which is quite useless to us, so remember - // the last character from GDK_KEY_PRESS and resue it as last resort - // - // NB: should be MT-neutral as always called from main thread only - static struct - { - KeySym keysym; - long keycode; - } s_lastKeyPress = { 0, 0 }; - - KeySym keysym = event->keyval; - long key_code; - - switch ( keysym ) - { - case GDK_Shift_L: - case GDK_Shift_R: key_code = WXK_SHIFT; break; - case GDK_Control_L: - case GDK_Control_R: key_code = WXK_CONTROL; break; - case GDK_Meta_L: - case GDK_Meta_R: - case GDK_Alt_L: - case GDK_Alt_R: - case GDK_Super_L: - case GDK_Super_R: key_code = WXK_ALT; break; - case GDK_Menu: key_code = WXK_MENU; break; - case GDK_Help: key_code = WXK_HELP; break; - case GDK_BackSpace: key_code = WXK_BACK; break; - case GDK_ISO_Left_Tab: - case GDK_Tab: key_code = WXK_TAB; break; - case GDK_Linefeed: key_code = WXK_RETURN; break; - case GDK_Clear: key_code = WXK_CLEAR; break; - case GDK_Return: key_code = WXK_RETURN; break; - case GDK_Pause: key_code = WXK_PAUSE; break; - case GDK_Scroll_Lock: key_code = WXK_SCROLL; break; - case GDK_Escape: key_code = WXK_ESCAPE; break; - case GDK_Delete: key_code = WXK_DELETE; break; - case GDK_Home: key_code = WXK_HOME; break; - case GDK_Left: key_code = WXK_LEFT; break; - case GDK_Up: key_code = WXK_UP; break; - case GDK_Right: key_code = WXK_RIGHT; break; - case GDK_Down: key_code = WXK_DOWN; break; - case GDK_Prior: key_code = WXK_PRIOR; break; -// case GDK_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_Next: key_code = WXK_NEXT; break; -// case GDK_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_End: key_code = WXK_END; break; - case GDK_Begin: key_code = WXK_HOME; break; - case GDK_Select: key_code = WXK_SELECT; break; - case GDK_Print: key_code = WXK_PRINT; break; - case GDK_Execute: key_code = WXK_EXECUTE; break; - case GDK_Insert: key_code = WXK_INSERT; break; - case GDK_Num_Lock: key_code = WXK_NUMLOCK; break; - - case GDK_KP_0: key_code = WXK_NUMPAD0; break; - case GDK_KP_1: key_code = WXK_NUMPAD1; break; - case GDK_KP_2: key_code = WXK_NUMPAD2; break; - case GDK_KP_3: key_code = WXK_NUMPAD3; break; - case GDK_KP_4: key_code = WXK_NUMPAD4; break; - case GDK_KP_5: key_code = WXK_NUMPAD5; break; - case GDK_KP_6: key_code = WXK_NUMPAD6; break; - case GDK_KP_7: key_code = WXK_NUMPAD7; break; - case GDK_KP_8: key_code = WXK_NUMPAD8; break; - case GDK_KP_9: key_code = WXK_NUMPAD9; break; - case GDK_KP_Space: key_code = WXK_NUMPAD_SPACE; break; - case GDK_KP_Tab: key_code = WXK_NUMPAD_TAB; break; - case GDK_KP_Enter: key_code = WXK_NUMPAD_ENTER; break; - case GDK_KP_F1: key_code = WXK_NUMPAD_F1; break; - case GDK_KP_F2: key_code = WXK_NUMPAD_F2; break; - case GDK_KP_F3: key_code = WXK_NUMPAD_F3; break; - case GDK_KP_F4: key_code = WXK_NUMPAD_F4; break; - case GDK_KP_Home: key_code = WXK_NUMPAD_HOME; break; - case GDK_KP_Left: key_code = WXK_NUMPAD_LEFT; break; - case GDK_KP_Up: key_code = WXK_NUMPAD_UP; break; - case GDK_KP_Right: key_code = WXK_NUMPAD_RIGHT; break; - case GDK_KP_Down: key_code = WXK_NUMPAD_DOWN; break; - case GDK_KP_Prior: key_code = WXK_NUMPAD_PRIOR; break; -// case GDK_KP_Page_Up: key_code = WXK_NUMPAD_PAGEUP; break; - case GDK_KP_Next: key_code = WXK_NUMPAD_NEXT; break; -// case GDK_KP_Page_Down: key_code = WXK_NUMPAD_PAGEDOWN; break; - case GDK_KP_End: key_code = WXK_NUMPAD_END; break; - case GDK_KP_Begin: key_code = WXK_NUMPAD_BEGIN; break; - case GDK_KP_Insert: key_code = WXK_NUMPAD_INSERT; break; - case GDK_KP_Delete: key_code = WXK_NUMPAD_DELETE; break; - case GDK_KP_Equal: key_code = WXK_NUMPAD_EQUAL; break; - case GDK_KP_Multiply: key_code = WXK_NUMPAD_MULTIPLY; break; - case GDK_KP_Add: key_code = WXK_NUMPAD_ADD; break; - case GDK_KP_Separator: key_code = WXK_NUMPAD_SEPARATOR; break; - case GDK_KP_Subtract: key_code = WXK_NUMPAD_SUBTRACT; break; - case GDK_KP_Decimal: key_code = WXK_NUMPAD_DECIMAL; break; - case GDK_KP_Divide: key_code = WXK_NUMPAD_DIVIDE; break; - - case GDK_F1: key_code = WXK_F1; break; - case GDK_F2: key_code = WXK_F2; break; - case GDK_F3: key_code = WXK_F3; break; - case GDK_F4: key_code = WXK_F4; break; - case GDK_F5: key_code = WXK_F5; break; - case GDK_F6: key_code = WXK_F6; break; - case GDK_F7: key_code = WXK_F7; break; - case GDK_F8: key_code = WXK_F8; break; - case GDK_F9: key_code = WXK_F9; break; - case GDK_F10: key_code = WXK_F10; break; - case GDK_F11: key_code = WXK_F11; break; - case GDK_F12: key_code = WXK_F12; break; - default: - { - // do we have the translation? - if ( event->length == 1 ) - { - keysym = (KeySym)event->string[0]; - } - else if ( (keysym & 0xFF) != keysym ) - { - // non ASCII key, what to do? - - if ( event->type == GDK_KEY_RELEASE ) - { - // reuse the one from the last keypress if any - if ( keysym == s_lastKeyPress.keysym ) - { - key_code = s_lastKeyPress.keycode; - - // skip "return 0" - break; - } - } - - // ignore this one, we don't know it - return 0; - } - //else: ASCII key, ok - - guint upper = gdk_keyval_to_upper( (guint)keysym ); - key_code = upper ? upper : keysym; - - if ( event->type == GDK_KEY_PRESS ) - { - // remember it to be reused below later - s_lastKeyPress.keysym = keysym; - s_lastKeyPress.keycode = key_code; - } - } - } - - return key_code; -} - -static long map_to_wx_keysym( GdkEventKey *event ) -{ - KeySym keysym = event->keyval; - guint key_code = 0; - - switch (keysym) - { - case GDK_Menu: key_code = WXK_MENU; break; - case GDK_Help: key_code = WXK_HELP; break; - case GDK_BackSpace: key_code = WXK_BACK; break; - case GDK_ISO_Left_Tab: - case GDK_Tab: key_code = WXK_TAB; break; - case GDK_Linefeed: key_code = WXK_RETURN; break; - case GDK_Clear: key_code = WXK_CLEAR; break; - case GDK_Return: key_code = WXK_RETURN; break; - case GDK_Pause: key_code = WXK_PAUSE; break; - case GDK_Scroll_Lock: key_code = WXK_SCROLL; break; - case GDK_Escape: key_code = WXK_ESCAPE; break; - case GDK_Delete: key_code = WXK_DELETE; break; - case GDK_Home: key_code = WXK_HOME; break; - case GDK_Left: key_code = WXK_LEFT; break; - case GDK_Up: key_code = WXK_UP; break; - case GDK_Right: key_code = WXK_RIGHT; break; - case GDK_Down: key_code = WXK_DOWN; break; - case GDK_Prior: key_code = WXK_PRIOR; break; -// case GDK_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_Next: key_code = WXK_NEXT; break; -// case GDK_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_End: key_code = WXK_END; break; - case GDK_Begin: key_code = WXK_HOME; break; - case GDK_Select: key_code = WXK_SELECT; break; - case GDK_Print: key_code = WXK_PRINT; break; - case GDK_Execute: key_code = WXK_EXECUTE; break; - case GDK_Insert: key_code = WXK_INSERT; break; - case GDK_Num_Lock: key_code = WXK_NUMLOCK; break; - - case GDK_KP_0: key_code = '0'; break; - case GDK_KP_1: key_code = '1'; break; - case GDK_KP_2: key_code = '2'; break; - case GDK_KP_3: key_code = '3'; break; - case GDK_KP_4: key_code = '4'; break; - case GDK_KP_5: key_code = '5'; break; - case GDK_KP_6: key_code = '6'; break; - case GDK_KP_7: key_code = '7'; break; - case GDK_KP_8: key_code = '8'; break; - case GDK_KP_9: key_code = '9'; break; - case GDK_KP_Space: key_code = ' '; break; - case GDK_KP_Tab: key_code = WXK_TAB; break; /* or '\t' ??? */ - case GDK_KP_Enter: key_code = WXK_RETURN; break; /* or '\r' ??? */ - case GDK_KP_F1: key_code = WXK_NUMPAD_F1; break; - case GDK_KP_F2: key_code = WXK_NUMPAD_F2; break; - case GDK_KP_F3: key_code = WXK_NUMPAD_F3; break; - case GDK_KP_F4: key_code = WXK_NUMPAD_F4; break; - case GDK_KP_Home: key_code = WXK_HOME; break; - case GDK_KP_Left: key_code = WXK_LEFT; break; - case GDK_KP_Up: key_code = WXK_UP; break; - case GDK_KP_Right: key_code = WXK_RIGHT; break; - case GDK_KP_Down: key_code = WXK_DOWN; break; - case GDK_KP_Prior: key_code = WXK_PRIOR; break; -// case GDK_KP_Page_Up: key_code = WXK_PAGEUP; break; - case GDK_KP_Next: key_code = WXK_NEXT; break; -// case GDK_KP_Page_Down: key_code = WXK_PAGEDOWN; break; - case GDK_KP_End: key_code = WXK_END; break; - case GDK_KP_Begin: key_code = WXK_HOME; break; - case GDK_KP_Insert: key_code = WXK_INSERT; break; - case GDK_KP_Delete: key_code = WXK_DELETE; break; - case GDK_KP_Equal: key_code = '='; break; - case GDK_KP_Multiply: key_code = '*'; break; - case GDK_KP_Add: key_code = '+'; break; - case GDK_KP_Separator: key_code = ','; break; - case GDK_KP_Subtract: key_code = '-'; break; - case GDK_KP_Decimal: key_code = '.'; break; - case GDK_KP_Divide: key_code = '/'; break; - - case GDK_F1: key_code = WXK_F1; break; - case GDK_F2: key_code = WXK_F2; break; - case GDK_F3: key_code = WXK_F3; break; - case GDK_F4: key_code = WXK_F4; break; - case GDK_F5: key_code = WXK_F5; break; - case GDK_F6: key_code = WXK_F6; break; - case GDK_F7: key_code = WXK_F7; break; - case GDK_F8: key_code = WXK_F8; break; - case GDK_F9: key_code = WXK_F9; break; - case GDK_F10: key_code = WXK_F10; break; - case GDK_F11: key_code = WXK_F11; break; - case GDK_F12: key_code = WXK_F12; break; - default: - if (event->length == 1) - { - key_code = (unsigned char)*event->string; - } - else if ((keysym & 0xFF) == keysym) - { - key_code = (guint)keysym; - } - } - - return key_code; -} +#endif // GTK+ < 2.0 //----------------------------------------------------------------------------- // "size_request" of m_widget //----------------------------------------------------------------------------- -static void gtk_window_size_request_callback( GtkWidget *widget, GtkRequisition *requisition, wxWindow *win ) +// make it extern because wxStatitText needs to disconnect this one +extern "C" +void wxgtk_window_size_request_callback(GtkWidget *widget, + GtkRequisition *requisition, + wxWindow *win) { - int w,h; + int w, h; win->GetSize( &w, &h ); - if (w < 2) w = 2; - if (h < 2) h = 2; + if (w < 2) + w = 2; + if (h < 2) + h = 2; requisition->height = h; requisition->width = w; @@ -762,8 +507,16 @@ static int gtk_window_expose_callback( GtkWidget *widget, if (g_isIdle) wxapp_install_idle_handler(); -/* - if (win->GetName() == wxT("panel")) +#ifdef __WXGTK20__ + // This callback gets called in drawing-idle time under + // GTK 2.0, so we don't need to defer anything to idle + // time anymore. + + GtkPizza *pizza = GTK_PIZZA( widget ); + if (gdk_event->window != pizza->bin_window) return FALSE; + +#if 0 + if (win->GetName()) { wxPrintf( wxT("OnExpose from ") ); if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) @@ -773,63 +526,34 @@ static int gtk_window_expose_callback( GtkWidget *widget, (int)gdk_event->area.width, (int)gdk_event->area.height ); } -*/ +#endif - GtkPizza *pizza = GTK_PIZZA (widget); + win->GetUpdateRegion() = wxRegion( gdk_event->region ); - if (win->GetThemeEnabled()) - { - wxWindow *parent = win->GetParent(); - while (parent && !parent->IsTopLevel()) - parent = parent->GetParent(); - if (!parent) - parent = win; + win->GtkSendPaintEvents(); - gtk_paint_flat_box (parent->m_widget->style, - pizza->bin_window, - GTK_STATE_NORMAL, - GTK_SHADOW_NONE, - &gdk_event->area, - parent->m_widget, - (char *)"base", - 0, 0, -1, -1); - } + + // Let parent window draw window less widgets + (* GTK_WIDGET_CLASS (pizza_parent_class)->expose_event) (widget, gdk_event); +#else + // This gets called immediately after an expose event + // under GTK 1.2 so we collect the calls and wait for + // the idle handler to pick things up. win->GetUpdateRegion().Union( gdk_event->area.x, 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 ); - // Actually send the various events based on the - // current update region. - if (gdk_event->count == 0) - win->GtkSendPaintEvents(); - - // The following code will result in all window-less widgets - // being redrawn if the wxWindows class is given a chance to - // paint *anything* because it will then be allowed to paint - // over the window-less widgets. - GList *children = pizza->children; - while (children) - { - GtkPizzaChild *child = (GtkPizzaChild*) children->data; - children = children->next; - - GdkEventExpose child_event = *gdk_event; - - if (GTK_WIDGET_NO_WINDOW (child->widget) && - GTK_WIDGET_DRAWABLE (child->widget) /* && - gtk_widget_intersect (child->widget, &gdk_event->area, &child_event.area)*/ ) - { - child_event.area.x = child->widget->allocation.x; - child_event.area.y = child->widget->allocation.y; - child_event.area.width = child->widget->allocation.width; - child_event.area.height = child->widget->allocation.height; - gtk_widget_event (child->widget, (GdkEvent*) &child_event); - } - } + // Actual redrawing takes place in idle time. + // win->GtkUpdate(); +#endif - return TRUE; + return FALSE; } //----------------------------------------------------------------------------- @@ -859,8 +583,10 @@ gint gtk_window_event_event_callback( GtkWidget *widget, // "draw" of m_wxwindow //----------------------------------------------------------------------------- +#ifndef __WXGTK20__ + // This callback is a complete replacement of the gtk_pizza_draw() function, -// which disabled. +// which is disabled. static void gtk_window_draw_callback( GtkWidget *widget, GdkRectangle *rect, @@ -879,8 +605,8 @@ static void gtk_window_draw_callback( GtkWidget *widget, return; } -/* - if (win->GetName() == wxT("panel")) +#if 0 + if (win->GetName()) { wxPrintf( wxT("OnDraw from ") ); if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) @@ -890,8 +616,9 @@ static void gtk_window_draw_callback( GtkWidget *widget, (int)rect->width, (int)rect->height ); } -*/ +#endif +#ifndef __WXUNIVERSAL__ GtkPizza *pizza = GTK_PIZZA (widget); if (win->GetThemeEnabled()) @@ -911,21 +638,15 @@ static void gtk_window_draw_callback( GtkWidget *widget, (char *)"base", 0, 0, -1, -1); } +#endif - - if (!(GTK_WIDGET_APP_PAINTABLE (widget)) && - (pizza->clear_on_draw)) - { - gdk_window_clear_area( pizza->bin_window, - rect->x, rect->y, rect->width, rect->height); - } - + win->m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height ); win->GetUpdateRegion().Union( rect->x, rect->y, rect->width, rect->height ); - // Actually send the various events based on the - // current update region. - win->GtkSendPaintEvents(); + // Update immediately, not in idle time. + win->GtkUpdate(); +#ifndef __WXUNIVERSAL__ // Redraw child widgets GList *children = pizza->children; while (children) @@ -939,159 +660,580 @@ static void gtk_window_draw_callback( GtkWidget *widget, gtk_widget_draw (child->widget, &child_area /* (GdkRectangle*) NULL*/ ); } } +#endif } +#endif + //----------------------------------------------------------------------------- // "key_press_event" from any window //----------------------------------------------------------------------------- -// turn on to see the key event codes on the console -#undef DEBUG_KEY_EVENTS +// set WXTRACE to this to see the key event codes on the console +#define TRACE_KEYS _T("keyevent") -static gint gtk_window_key_press_callback( GtkWidget *widget, - GdkEventKey *gdk_event, - wxWindow *win ) +// translates an X key symbol to WXK_XXX value +// +// if isChar is true it means that the value returned will be used for EVT_CHAR +// event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide, +// for example, while if it is false it means that the value is going to be +// used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to +// WXK_NUMPAD_DIVIDE +static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar) { - DEBUG_MAIN_THREAD + long key_code; - if (g_isIdle) - wxapp_install_idle_handler(); + switch ( keysym ) + { + // Shift, Control and Alt don't generate the CHAR events at all + case GDK_Shift_L: + case GDK_Shift_R: + key_code = isChar ? 0 : WXK_SHIFT; + break; + case GDK_Control_L: + case GDK_Control_R: + key_code = isChar ? 0 : WXK_CONTROL; + break; + case GDK_Meta_L: + case GDK_Meta_R: + case GDK_Alt_L: + case GDK_Alt_R: + case GDK_Super_L: + case GDK_Super_R: + key_code = isChar ? 0 : WXK_ALT; + break; - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; + // neither do the toggle modifies + case GDK_Scroll_Lock: + key_code = isChar ? 0 : WXK_SCROLL; + break; + case GDK_Caps_Lock: + key_code = isChar ? 0 : WXK_CAPITAL; + break; - int x = 0; - int y = 0; - GdkModifierType state; - if (gdk_event->window) - gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + case GDK_Num_Lock: + key_code = isChar ? 0 : WXK_NUMLOCK; + break; - bool ret = FALSE; - long key_code = map_to_unmodified_wx_keysym( gdk_event ); + // various other special keys + case GDK_Menu: + key_code = WXK_MENU; + break; -#ifdef DEBUG_KEY_EVENTS - wxPrintf(_T("Key press event: %d => %ld\n"), gdk_event->keyval, key_code); -#endif // DEBUG_KEY_EVENTS + case GDK_Help: + key_code = WXK_HELP; + break; - /* sending unknown key events doesn't really make sense */ - if (key_code == 0) - return FALSE; + case GDK_BackSpace: + key_code = WXK_BACK; + break; - wxKeyEvent event( wxEVT_KEY_DOWN ); - 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_keyCode = key_code; - event.m_scanCode = gdk_event->keyval; - event.m_x = x; - event.m_y = y; - event.SetEventObject( win ); - ret = win->GetEventHandler()->ProcessEvent( event ); + case GDK_ISO_Left_Tab: + case GDK_Tab: + key_code = WXK_TAB; + break; -#if wxUSE_ACCEL - if (!ret) - { - wxWindowGTK *ancestor = win; - while (ancestor) - { - int command = ancestor->GetAcceleratorTable()->GetCommand( event ); - if (command != -1) - { - wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command ); - ret = ancestor->GetEventHandler()->ProcessEvent( command_event ); - break; - } - if (ancestor->IsTopLevel()) - break; - ancestor = ancestor->GetParent(); - } - } -#endif // wxUSE_ACCEL + case GDK_Linefeed: + case GDK_Return: + key_code = WXK_RETURN; + break; - /* 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 ) - { - key_code = map_to_wx_keysym( gdk_event ); + case GDK_Clear: + key_code = WXK_CLEAR; + break; - if ( key_code ) - { -#ifdef DEBUG_KEY_EVENTS - wxPrintf(_T("Char event: %ld\n"), key_code); -#endif // DEBUG_KEY_EVENTS + case GDK_Pause: + key_code = WXK_PAUSE; + break; - // reuse the ame event object, just change its type and use the - // translated keycode instead of the raw one - event.SetEventType(wxEVT_CHAR); - event.m_keyCode = key_code; + case GDK_Select: + key_code = WXK_SELECT; + break; - ret = win->GetEventHandler()->ProcessEvent( event ); - } - } + case GDK_Print: + key_code = WXK_PRINT; + break; - /* win is a control: tab can be propagated up */ - if ( !ret && - ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) && -// VZ: testing for wxTE_PROCESS_TAB shouldn't be done here the control may -// have this style, yet choose not to process this particular TAB in which -// case TAB must still work as a navigational character -#if 0 - !win->HasFlag(wxTE_PROCESS_TAB) && -#endif // 0 - win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) ) - { - wxNavigationKeyEvent new_event; - new_event.SetEventObject( win->GetParent() ); - /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */ - new_event.SetDirection( (gdk_event->keyval == GDK_Tab) ); - /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ - new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) ); - new_event.SetCurrentFocus( win ); - ret = win->GetParent()->GetEventHandler()->ProcessEvent( new_event ); - } + case GDK_Execute: + key_code = WXK_EXECUTE; + break; - /* generate wxID_CANCEL if has been pressed (typically in dialogs) */ - if ( !ret && - (gdk_event->keyval == GDK_Escape) ) + case GDK_Escape: + key_code = WXK_ESCAPE; + break; + + // cursor and other extended keyboard keys + case GDK_Delete: + key_code = WXK_DELETE; + break; + + case GDK_Home: + key_code = WXK_HOME; + break; + + case GDK_Left: + key_code = WXK_LEFT; + break; + + case GDK_Up: + key_code = WXK_UP; + break; + + case GDK_Right: + key_code = WXK_RIGHT; + break; + + case GDK_Down: + key_code = WXK_DOWN; + break; + + case GDK_Prior: // == GDK_Page_Up + key_code = WXK_PRIOR; + break; + + case GDK_Next: // == GDK_Page_Down + key_code = WXK_NEXT; + break; + + case GDK_End: + key_code = WXK_END; + break; + + case GDK_Begin: + key_code = WXK_HOME; + break; + + case GDK_Insert: + key_code = WXK_INSERT; + break; + + + // numpad keys + case GDK_KP_0: + case GDK_KP_1: + case GDK_KP_2: + case GDK_KP_3: + case GDK_KP_4: + case GDK_KP_5: + case GDK_KP_6: + case GDK_KP_7: + case GDK_KP_8: + case GDK_KP_9: + key_code = (isChar ? '0' : WXK_NUMPAD0) + keysym - GDK_KP_0; + break; + + case GDK_KP_Space: + key_code = isChar ? ' ' : WXK_NUMPAD_SPACE; + break; + + case GDK_KP_Tab: + key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB; + break; + + case GDK_KP_Enter: + key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER; + break; + + case GDK_KP_F1: + key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1; + break; + + case GDK_KP_F2: + key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2; + break; + + case GDK_KP_F3: + key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3; + break; + + case GDK_KP_F4: + key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4; + break; + + case GDK_KP_Home: + key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME; + break; + + case GDK_KP_Left: + key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT; + break; + + case GDK_KP_Up: + key_code = isChar ? WXK_UP : WXK_NUMPAD_UP; + break; + + case GDK_KP_Right: + key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT; + break; + + case GDK_KP_Down: + key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN; + break; + + case GDK_KP_Prior: // == GDK_KP_Page_Up + key_code = isChar ? WXK_PRIOR : WXK_NUMPAD_PRIOR; + break; + + case GDK_KP_Next: // == GDK_KP_Page_Down + key_code = isChar ? WXK_NEXT : WXK_NUMPAD_NEXT; + break; + + case GDK_KP_End: + key_code = isChar ? WXK_END : WXK_NUMPAD_END; + break; + + case GDK_KP_Begin: + key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN; + break; + + case GDK_KP_Insert: + key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT; + break; + + case GDK_KP_Delete: + key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE; + break; + + case GDK_KP_Equal: + key_code = isChar ? '=' : WXK_NUMPAD_EQUAL; + break; + + case GDK_KP_Multiply: + key_code = isChar ? '*' : WXK_NUMPAD_MULTIPLY; + break; + + case GDK_KP_Add: + key_code = isChar ? '+' : WXK_NUMPAD_ADD; + break; + + case GDK_KP_Separator: + // FIXME: what is this? + key_code = isChar ? '.' : WXK_NUMPAD_SEPARATOR; + break; + + case GDK_KP_Subtract: + key_code = isChar ? '-' : WXK_NUMPAD_SUBTRACT; + break; + + case GDK_KP_Decimal: + key_code = isChar ? '.' : WXK_NUMPAD_DECIMAL; + break; + + case GDK_KP_Divide: + key_code = isChar ? '/' : WXK_NUMPAD_DIVIDE; + break; + + + // function keys + case GDK_F1: + case GDK_F2: + case GDK_F3: + case GDK_F4: + case GDK_F5: + case GDK_F6: + case GDK_F7: + case GDK_F8: + case GDK_F9: + case GDK_F10: + case GDK_F11: + case GDK_F12: + key_code = WXK_F1 + keysym - GDK_F1; + break; + + default: + key_code = 0; + } + + return key_code; +} + +static inline bool wxIsAsciiKeysym(KeySym ks) +{ + return ks < 256; +} + +static bool +wxTranslateGTKKeyEventToWx(wxKeyEvent& event, + wxWindowGTK *win, + GdkEventKey *gdk_event) +{ + // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string + // but only event->keyval which is quite useless to us, so remember + // the last character from GDK_KEY_PRESS and reuse it as last resort + // + // NB: should be MT-safe as we're always called from the main thread only + static struct + { + KeySym keysym; + long keycode; + } s_lastKeyPress = { 0, 0 }; + + KeySym keysym = gdk_event->keyval; + + wxLogTrace(TRACE_KEYS, _T("Key %s event: keysym = %ld"), + event.GetEventType() == wxEVT_KEY_UP ? _T("release") + : _T("press"), + keysym); + + long key_code = wxTranslateKeySymToWXKey(keysym, FALSE /* !isChar */); + + if ( !key_code ) + { + // do we have the translation or is it a plain ASCII character? + if ( (gdk_event->length == 1) || wxIsAsciiKeysym(keysym) ) + { + // we should use keysym if it is ASCII as X does some translations + // like "I pressed while Control is down" => "Ctrl-I" == "TAB" + // which we don't want here (but which we do use for OnChar()) + if ( !wxIsAsciiKeysym(keysym) ) + { + keysym = (KeySym)gdk_event->string[0]; + } + + // we want to always get the same key code when the same key is + // pressed regardless of the state of the modifies, i.e. on a + // standard US keyboard pressing '5' or '%' ('5' key with + // Shift) should result in the same key code in OnKeyDown(): + // '5' (although OnChar() will get either '5' or '%'). + // + // to do it we first translate keysym to keycode (== scan code) + // and then back but always using the lower register + Display *dpy = (Display *)wxGetDisplay(); + KeyCode keycode = XKeysymToKeycode(dpy, keysym); + + wxLogTrace(TRACE_KEYS, _T("\t-> keycode %d"), keycode); + + KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0); + + // use the normalized, i.e. lower register, keysym if we've + // got one + key_code = keysymNormalized ? keysymNormalized : keysym; + + // as explained above, we want to have lower register key codes + // normally but for the letter keys we want to have the upper ones + // + // NB: don't use XConvertCase() here, we want to do it for letters + // only + key_code = toupper(key_code); + } + else // non ASCII key, what to do? + { + // by default, ignore it + key_code = 0; + + // but if we have cached information from the last KEY_PRESS + if ( gdk_event->type == GDK_KEY_RELEASE ) + { + // then reuse it + if ( keysym == s_lastKeyPress.keysym ) + { + key_code = s_lastKeyPress.keycode; + } + } + } + + if ( gdk_event->type == GDK_KEY_PRESS ) + { + // remember it to be reused for KEY_UP event later + s_lastKeyPress.keysym = keysym; + s_lastKeyPress.keycode = key_code; + } + } + + wxLogTrace(TRACE_KEYS, _T("\t-> wxKeyCode %ld"), key_code); + + // sending unknown key events doesn't really make sense + if ( !key_code ) + 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; + 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; +} + + +static gint gtk_window_key_press_callback( GtkWidget *widget, + GdkEventKey *gdk_event, + wxWindow *win ) +{ + DEBUG_MAIN_THREAD + + if (g_isIdle) + wxapp_install_idle_handler(); + + if (!win->m_hasVMT) + return FALSE; + if (g_blockEventsOnDrag) + return FALSE; + + + wxKeyEvent event( wxEVT_KEY_DOWN ); + if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) { - wxCommandEvent new_event(wxEVT_COMMAND_BUTTON_CLICKED,wxID_CANCEL); - new_event.SetEventObject( win ); - ret = win->GetEventHandler()->ProcessEvent( new_event ); + // unknown key pressed, ignore (the event would be useless anyhow) + return FALSE; } - /* Doesn't work. */ -#if 0 // (GTK_MINOR_VERSION > 0) - /* Pressing F10 will activate the menu bar of the top frame. */ - if ( (!ret) && - (gdk_event->keyval == GDK_F10) ) + // Emit KEY_DOWN event + bool ret = win->GetEventHandler()->ProcessEvent( event ); + +#if wxUSE_ACCEL + if (!ret) { wxWindowGTK *ancestor = win; while (ancestor) { - if (wxIsKindOf(ancestor,wxFrame)) + int command = ancestor->GetAcceleratorTable()->GetCommand( event ); + if (command != -1) + { + wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command ); + ret = ancestor->GetEventHandler()->ProcessEvent( command_event ); + break; + } + if (ancestor->IsTopLevel()) + break; + ancestor = ancestor->GetParent(); + } + } +#endif // wxUSE_ACCEL + + // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x + // will only be sent if it is not in an accelerator table. + if (!ret) + { + long key_code; + KeySym keysym = gdk_event->keyval; +#ifdef __WXGTK20__ + // 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. But only do it this way for non-KeySym keys. + key_code = wxTranslateKeySymToWXKey(gdk_event->keyval, FALSE /* isChar */); + if ( !key_code && win->m_imContext ) + { + gtk_im_context_filter_keypress ( (GtkIMContext*) win->m_imContext, gdk_event ); + ret = TRUE; + } + else +#endif + { + // Find key code for EVT_CHAR and EVT_CHAR_HOOK events + key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); + if ( !key_code ) { - wxFrame *frame = (wxFrame*) ancestor; - wxMenuBar *menubar = frame->GetMenuBar(); - if (menubar) + if ( gdk_event->length == 1 ) + { + key_code = (unsigned char)gdk_event->string[0]; + } + else if ( wxIsAsciiKeysym(keysym) ) { - wxNode *node = menubar->GetMenus().First(); - if (node) - { - wxMenu *firstMenu = (wxMenu*) node->Data(); - gtk_menu_item_select( GTK_MENU_ITEM(firstMenu->m_owner) ); - ret = TRUE; - break; - } + // ASCII key + key_code = (unsigned char)keysym; + } + } + + if ( key_code ) + { + wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); + + event.m_keyCode = key_code; + + // Implement OnCharHook by checking ancesteror top level windows + wxWindow *parent = win; + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (parent) + { + event.SetEventType( wxEVT_CHAR_HOOK ); + ret = parent->GetEventHandler()->ProcessEvent( event ); + } + + if (!ret) + { + event.SetEventType(wxEVT_CHAR); + ret = win->GetEventHandler()->ProcessEvent( event ); } } - ancestor = ancestor->GetParent(); } } + + // win is a control: tab can be propagated up + if ( !ret && + ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab)) && +// VZ: testing for wxTE_PROCESS_TAB shouldn't be done here the control may +// have this style, yet choose not to process this particular TAB in which +// case TAB must still work as a navigational character +#if 0 + !win->HasFlag(wxTE_PROCESS_TAB) && #endif // 0 + win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) ) + { + wxNavigationKeyEvent new_event; + new_event.SetEventObject( win->GetParent() ); + // GDK reports GDK_ISO_Left_Tab for SHIFT-TAB + new_event.SetDirection( (gdk_event->keyval == GDK_Tab) ); + // CTRL-TAB changes the (parent) window, i.e. switch notebook page + new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) ); + new_event.SetCurrentFocus( win ); + ret = win->GetParent()->GetEventHandler()->ProcessEvent( new_event ); + } + + // generate wxID_CANCEL if has been pressed (typically in dialogs) + if ( !ret && + (gdk_event->keyval == GDK_Escape) ) + { + // however only do it if we have a Cancel button in the dialog, + // otherwise the user code may get confused by the events from a + // non-existing button and, worse, a wxButton might get button event + // from another button which is not really expected + wxWindow *winForCancel = win, + *btnCancel = NULL; + while ( winForCancel ) + { + btnCancel = winForCancel->FindWindow(wxID_CANCEL); + if ( btnCancel ) + { + // found a cancel button + break; + } + + if ( winForCancel->IsTopLevel() ) + { + // no need to look further + break; + } + + // maybe our parent has a cancel button? + winForCancel = winForCancel->GetParent(); + } + + if ( btnCancel ) + { + wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL); + event.SetEventObject(btnCancel); + ret = btnCancel->GetEventHandler()->ProcessEvent(event); + } + } if (ret) { @@ -1102,81 +1244,127 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, return FALSE; } +#ifdef __WXGTK20__ +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 ); + + // Backward compatible for ISO-8859 + if (event.m_uniChar < 256) + event.m_keyCode = event.m_uniChar; +#else + gunichar uniChar = g_utf8_get_char( str ); + // We cannot handle Unicode in non-Unicode mode + if (uniChar > 255) return; + + event.m_keyCode = uniChar; +#endif + + + // TODO: We still need to set all the extra attributes of the + // event, modifiers and such... + + + // 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) + { + event.SetEventType(wxEVT_CHAR); + ret = window->GetEventHandler()->ProcessEvent( event ); + } +} +#endif + + //----------------------------------------------------------------------------- // "key_release_event" from any window //----------------------------------------------------------------------------- -static gint gtk_window_key_release_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindowGTK *win ) +static gint gtk_window_key_release_callback( GtkWidget *widget, + GdkEventKey *gdk_event, + wxWindowGTK *win ) { DEBUG_MAIN_THREAD if (g_isIdle) wxapp_install_idle_handler(); - if (!win->m_hasVMT) return FALSE; - if (g_blockEventsOnDrag) return FALSE; - - long key_code = map_to_unmodified_wx_keysym( gdk_event ); - -#ifdef DEBUG_KEY_EVENTS - wxPrintf(_T("Key release event: %d => %ld\n"), gdk_event->keyval, key_code); -#endif // DEBUG_KEY_EVENTS - - /* sending unknown key events doesn't really make sense */ - if (key_code == 0) return FALSE; + if (!win->m_hasVMT) + return FALSE; - int x = 0; - int y = 0; - GdkModifierType state; - if (gdk_event->window) - gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + if (g_blockEventsOnDrag) + return FALSE; wxKeyEvent event( wxEVT_KEY_UP ); - 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_keyCode = key_code; - event.m_scanCode = gdk_event->keyval; - event.m_x = x; - event.m_y = y; - event.SetEventObject( win ); - - if (win->GetEventHandler()->ProcessEvent( event )) + if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) { - gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); - return TRUE; + // unknown key pressed, ignore (the event would be useless anyhow + return FALSE; } - return FALSE; + if ( !win->GetEventHandler()->ProcessEvent( event ) ) + return FALSE; + + gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_release_event" ); + return TRUE; } // ============================================================================ // the mouse events // ============================================================================ -// init wxMouseEvent with the info from gdk_event -#define InitMouseEvent(win, event, 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); \ -\ - wxPoint pt = win->GetClientAreaOrigin(); \ - event.m_x = (wxCoord)gdk_event->x - pt.x; \ - event.m_y = (wxCoord)gdk_event->y - pt.y; \ - } - // ---------------------------------------------------------------------------- -// mouse event processing helper +// mouse event processing helpers // ---------------------------------------------------------------------------- +// init wxMouseEvent with the info from gdk_event +// +// NB: this has to be a macro as gdk_event type is different for different +// events we're used with +#define InitMouseEvent(/* wxWindowGTK * */ win, \ + /* wxMouseEvent& */ event, \ + /* GdkEventXXX * */ 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); \ + if (event.GetEventType()==wxEVT_MOUSEWHEEL) \ + { \ + if (((GdkEventButton*)gdk_event)->button == 4) \ + event.m_wheelRotation = 120; \ + else if (((GdkEventButton*)gdk_event)->button == 5) \ + event.m_wheelRotation = -120; \ + } \ + \ + wxPoint pt = win->GetClientAreaOrigin(); \ + event.m_x = (wxCoord)gdk_event->x - pt.x; \ + event.m_y = (wxCoord)gdk_event->y - pt.y; \ + \ + event.SetEventObject( win ); \ + event.SetId( win->GetId() ); \ + event.SetTimestamp( gdk_event->time ); \ +} \ + static void AdjustEventButtonState(wxMouseEvent& event) { // GDK reports the old state of the button for a button press event, but @@ -1209,11 +1397,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; + } + + wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst(); + while (node) + { + wxWindowGTK *child = node->GetData(); + + node = node->GetNext(); + 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_y + 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 @@ -1232,19 +1488,34 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; - if (win->m_wxwindow) + if (win->m_wxwindow && (g_focusWindow != win) && win->AcceptsFocus()) { - if (GTK_WIDGET_CAN_FOCUS(win->m_wxwindow) && !GTK_WIDGET_HAS_FOCUS (win->m_wxwindow) ) - { - gtk_widget_grab_focus (win->m_wxwindow); - + gtk_widget_grab_focus( win->m_wxwindow ); /* - wxPrintf( wxT("GrabFocus from ") ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( wxT(".\n") ); + wxPrintf( wxT("GrabFocus from ") ); + if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) + wxPrintf( win->GetClassInfo()->GetClassName() ); + wxPrintf( wxT(".\n") ); */ + } + // GDK sends surplus button down event + // before a double click event. We + // need to filter these out. + if (gdk_event->type == GDK_BUTTON_PRESS) + { + GdkEvent *peek_event = gdk_event_peek(); + if (peek_event) + { + if (peek_event->type == GDK_2BUTTON_PRESS) + { + gdk_event_free( peek_event ); + return TRUE; + } + else + { + gdk_event_free( peek_event ); + } } } @@ -1259,21 +1530,37 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton default: break; } } - else if (gdk_event->button == 2) + else if (gdk_event->button == 2) + { + switch (gdk_event->type) + { + case GDK_BUTTON_PRESS: event_type = wxEVT_MIDDLE_DOWN; break; + case GDK_2BUTTON_PRESS: event_type = wxEVT_MIDDLE_DCLICK; break; + default: break; + } + } + else if (gdk_event->button == 3) + { + switch (gdk_event->type) + { + case GDK_BUTTON_PRESS: event_type = wxEVT_RIGHT_DOWN; break; + case GDK_2BUTTON_PRESS: event_type = wxEVT_RIGHT_DCLICK; break; + default: break; + } + } + else if (gdk_event->button == 4) { switch (gdk_event->type) { - case GDK_BUTTON_PRESS: event_type = wxEVT_MIDDLE_DOWN; break; - case GDK_2BUTTON_PRESS: event_type = wxEVT_MIDDLE_DCLICK; break; + case GDK_BUTTON_PRESS: event_type = wxEVT_MOUSEWHEEL; break; default: break; } } - else if (gdk_event->button == 3) + else if (gdk_event->button == 5) { switch (gdk_event->type) { - case GDK_BUTTON_PRESS: event_type = wxEVT_RIGHT_DOWN; break; - case GDK_2BUTTON_PRESS: event_type = wxEVT_RIGHT_DCLICK; break; + case GDK_BUTTON_PRESS: event_type = wxEVT_MOUSEWHEEL; break; default: break; } } @@ -1289,79 +1576,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. - - 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; - } - } - } - } + // 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); - event.SetEventObject( win ); + // 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); gs_timeLastClick = gdk_event->time; @@ -1372,6 +1595,20 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton wxPrintf( wxT(".\n") ); */ +#ifndef __WXGTK20__ + if (event_type == wxEVT_LEFT_DCLICK) + { + // GTK 1.2 crashes when intercepting double + // click events from both wxSpinButton and + // wxSpinCtrl + if (GTK_IS_SPIN_BUTTON(win->m_widget)) + { + // Just disable this event for now. + return FALSE; + } + } +#endif + if (win->GetEventHandler()->ProcessEvent( event )) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_press_event" ); @@ -1385,7 +1622,9 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton // "button_release_event" //----------------------------------------------------------------------------- -static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxWindowGTK *win ) +static gint gtk_window_button_release_callback( GtkWidget *widget, + GdkEventButton *gdk_event, + wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1398,21 +1637,25 @@ static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButto if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; -/* - printf( "OnButtonRelease from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - printf( win->GetClassInfo()->GetClassName() ); - printf( ".\n" ); -*/ - wxEventType event_type = wxEVT_NULL; switch (gdk_event->button) { - case 1: event_type = wxEVT_LEFT_UP; break; - case 2: event_type = wxEVT_MIDDLE_UP; break; - case 3: event_type = wxEVT_RIGHT_UP; break; - default: return FALSE; + case 1: + event_type = wxEVT_LEFT_UP; + break; + + case 2: + event_type = wxEVT_MIDDLE_UP; + break; + + case 3: + event_type = wxEVT_RIGHT_UP; + break; + + default: + // unknwon button, don't process + return FALSE; } wxMouseEvent event( event_type ); @@ -1420,79 +1663,25 @@ 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) + if ( event_type == wxEVT_RIGHT_UP ) { - 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; - } - } - } + // 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); } - event.SetEventObject( win ); + if ( !g_captureWindow ) + win = FindWindowForMouseEvent(win, event.m_x, event.m_y); if (win->GetEventHandler()->ProcessEvent( event )) { @@ -1546,6 +1735,10 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, { // synthetize a mouse enter or leave event if needed GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL); + // This seems to be necessary and actually been added to + // GDK itself in version 2.0.X + gdk_flush(); + bool hasMouse = winUnderMouse == gdk_event->window; if ( hasMouse != g_captureWindowHasMouse ) { @@ -1561,70 +1754,9 @@ 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 ); - if (win->GetEventHandler()->ProcessEvent( event )) { gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "motion_notify_event" ); @@ -1638,6 +1770,21 @@ static gint gtk_window_motion_notify_callback( GtkWidget *widget, // "focus_in_event" //----------------------------------------------------------------------------- +// 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->GetEventHandler()->ProcessEvent(eventChildFocus); + + wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId()); + eventFocus.SetEventObject(win); + + return win->GetEventHandler()->ProcessEvent(eventFocus); +} + static gint gtk_window_focus_in_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindow *win ) @@ -1667,17 +1814,8 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, g_focusWindowLast = g_focusWindow = win; -#if 0 - wxPrintf( "OnSetFocus from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( ".\n" ); -#endif - - // notify the parent keeping track of focus for the kbd navigation - // purposes that we got it - wxChildFocusEvent eventFocus(win); - (void)win->GetEventHandler()->ProcessEvent(eventFocus); + wxLogTrace(TRACE_FOCUS, + _T("%s: focus in"), win->GetName().c_str()); #ifdef HAVE_XIM if (win->m_ic) @@ -1693,6 +1831,8 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, } #endif // wxUSE_CARET + g_activeFrameLostFocus = FALSE; + wxWindowGTK *active = wxGetTopLevelParent(win); if ( active != g_activeFrame ) { @@ -1709,19 +1849,24 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId()); event.SetEventObject(g_activeFrame); g_activeFrame->GetEventHandler()->ProcessEvent(event); - } - g_activeFrameLostFocus = FALSE; + // Don't send focus events in addition to activate + // if (win == g_activeFrame) + // return TRUE; + } - wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() ); - event.SetEventObject( win ); - - if (win->GetEventHandler()->ProcessEvent( event )) + // 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; } @@ -1730,9 +1875,7 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, // "focus_out_event" //----------------------------------------------------------------------------- -static GtkWidget *gs_widgetLastFocus = NULL; - -static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED(event), wxWindowGTK *win ) +static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1742,17 +1885,8 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; - // VZ: this is really weird but GTK+ seems to call us from inside - // gtk_widget_grab_focus(), i.e. it first sends "focus_out" signal to - // this widget and then "focus_in". This is totally unexpected and - // completely breaks wxUniv code so ignore this dummy event (we can't - // be losing focus if we're about to acquire it!) - if ( widget == gs_widgetLastFocus ) - { - gs_widgetLastFocus = NULL; - - return FALSE; - } + wxLogTrace( TRACE_FOCUS, + _T("%s: focus out"), win->GetName().c_str() ); if ( !g_activeFrameLostFocus && g_activeFrame ) { @@ -1761,7 +1895,7 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED // always) and makes using Mahogany quite annoying #if 0 wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame, - wxT("unfocusing window that hasn't gained focus properly") ) + wxT("unfocusing window that hasn't gained focus properly") ); #endif // 0 g_activeFrameLostFocus = TRUE; @@ -1778,13 +1912,6 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED g_focusWindow = (wxWindowGTK *)NULL; -#if 0 - wxPrintf( "OnKillFocus from " ); - if (win->GetClassInfo() && win->GetClassInfo()->GetClassName()) - wxPrintf( win->GetClassInfo()->GetClassName() ); - wxPrintf( ".\n" ); -#endif - #ifdef HAVE_XIM if (win->m_ic) gdk_im_end(); @@ -1799,13 +1926,20 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED } #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; @@ -1815,7 +1949,10 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEvent *WXUNUSED // "enter_notify_event" //----------------------------------------------------------------------------- -static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win ) +static +gint gtk_window_enter_callback( GtkWidget *widget, + GdkEventCrossing *gdk_event, + wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1825,11 +1962,10 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; - if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; + // Event was emitted after a grab + if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; - wxMouseEvent event( wxEVT_ENTER_WINDOW ); - event.SetTimestamp( gdk_event->time ); - event.SetEventObject( win ); + if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; int x = 0; int y = 0; @@ -1837,6 +1973,7 @@ static gint gtk_window_enter_callback( GtkWidget *widget, GdkEventCrossing *gdk_ gdk_window_get_pointer( widget->window, &x, &y, &state ); + wxMouseEvent event( wxEVT_ENTER_WINDOW ); InitMouseEvent(win, event, gdk_event); wxPoint pt = win->GetClientAreaOrigin(); event.m_x = x + pt.x; @@ -1865,6 +2002,9 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; + // Event was emitted after an ungrab + if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE; + if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE; wxMouseEvent event( wxEVT_LEAVE_WINDOW ); @@ -1877,13 +2017,13 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ gdk_window_get_pointer( widget->window, &x, &y, &state ); - event.m_shiftDown = (state & GDK_SHIFT_MASK); - event.m_controlDown = (state & GDK_CONTROL_MASK); - event.m_altDown = (state & GDK_MOD1_MASK); - event.m_metaDown = (state & GDK_MOD2_MASK); - event.m_leftDown = (state & GDK_BUTTON1_MASK); - event.m_middleDown = (state & GDK_BUTTON2_MASK); - event.m_rightDown = (state & GDK_BUTTON3_MASK); + event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0; + event.m_controlDown = (state & GDK_CONTROL_MASK) != 0; + event.m_altDown = (state & GDK_MOD1_MASK) != 0; + event.m_metaDown = (state & GDK_MOD2_MASK) != 0; + event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0; + event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0; + event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0; wxPoint pt = win->GetClientAreaOrigin(); event.m_x = x + pt.x; @@ -1902,7 +2042,9 @@ static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_ // "value_changed" from m_vAdjust //----------------------------------------------------------------------------- -static void gtk_window_vscroll_callback( GtkAdjustment *adjust, wxWindowGTK *win ) +static void gtk_window_vscroll_callback( GtkAdjustment *adjust, + SCROLLBAR_CBACK_ARG + wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1918,14 +2060,10 @@ static void gtk_window_vscroll_callback( GtkAdjustment *adjust, wxWindowGTK *win win->m_oldVerticalPos = adjust->value; - GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget); - GtkRange *range = GTK_RANGE( scrolledWindow->vscrollbar ); - - wxEventType command = wxEVT_SCROLLWIN_THUMBTRACK; - if (range->scroll_type == GTK_SCROLL_STEP_BACKWARD) command = wxEVT_SCROLLWIN_LINEUP; - else if (range->scroll_type == GTK_SCROLL_STEP_FORWARD) command = wxEVT_SCROLLWIN_LINEDOWN; - else if (range->scroll_type == GTK_SCROLL_PAGE_BACKWARD) command = wxEVT_SCROLLWIN_PAGEUP; - else if (range->scroll_type == GTK_SCROLL_PAGE_FORWARD) command = wxEVT_SCROLLWIN_PAGEDOWN; +#ifndef __WXGTK20__ + GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget); +#endif + wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->vscrollbar)); int value = (int)(adjust->value+0.5); @@ -1938,7 +2076,9 @@ static void gtk_window_vscroll_callback( GtkAdjustment *adjust, wxWindowGTK *win // "value_changed" from m_hAdjust //----------------------------------------------------------------------------- -static void gtk_window_hscroll_callback( GtkAdjustment *adjust, wxWindowGTK *win ) +static void gtk_window_hscroll_callback( GtkAdjustment *adjust, + SCROLLBAR_CBACK_ARG + wxWindowGTK *win ) { DEBUG_MAIN_THREAD @@ -1951,16 +2091,12 @@ static void gtk_window_hscroll_callback( GtkAdjustment *adjust, wxWindowGTK *win float diff = adjust->value - win->m_oldHorizontalPos; if (fabs(diff) < 0.2) return; - win->m_oldHorizontalPos = adjust->value; - - GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget); - GtkRange *range = GTK_RANGE( scrolledWindow->hscrollbar ); +#ifndef __WXGTK20__ + GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget); +#endif + wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->hscrollbar)); - wxEventType command = wxEVT_SCROLLWIN_THUMBTRACK; - if (range->scroll_type == GTK_SCROLL_STEP_BACKWARD) command = wxEVT_SCROLLWIN_LINEUP; - else if (range->scroll_type == GTK_SCROLL_STEP_FORWARD) command = wxEVT_SCROLLWIN_LINEDOWN; - else if (range->scroll_type == GTK_SCROLL_PAGE_BACKWARD) command = wxEVT_SCROLLWIN_PAGEUP; - else if (range->scroll_type == GTK_SCROLL_PAGE_FORWARD) command = wxEVT_SCROLLWIN_PAGEDOWN; + win->m_oldHorizontalPos = adjust->value; int value = (int)(adjust->value+0.5); @@ -1984,7 +2120,11 @@ static gint gtk_scrollbar_button_press_callback( GtkRange *widget, g_blockEventsOnScroll = TRUE; + + // FIXME: there is no 'slider' field in GTK+ 2.0 any more +#ifndef __WXGTK20__ win->m_isScrolling = (gdk_event->window == widget->slider); +#endif return FALSE; } @@ -2045,6 +2185,7 @@ wxWindow *wxWindowBase::FindFocus() return (wxWindow *)g_focusWindow; } + //----------------------------------------------------------------------------- // "realize" from m_widget //----------------------------------------------------------------------------- @@ -2053,18 +2194,26 @@ wxWindow *wxWindowBase::FindFocus() been realized, so we do this directly after realization. */ static gint -gtk_window_realized_callback( GtkWidget *WXUNUSED(m_widget), wxWindow *win ) +gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) { DEBUG_MAIN_THREAD if (g_isIdle) wxapp_install_idle_handler(); + + if (win->m_delayedBackgroundColour && !win->GetThemeEnabled()) + win->GtkSetBackgroundColour( win->GetBackgroundColour() ); - if (win->m_delayedBackgroundColour) - win->SetBackgroundColour( win->GetBackgroundColour() ); + if (win->m_delayedForegroundColour && !win->GetThemeEnabled()) + win->GtkSetForegroundColour( win->GetForegroundColour() ); - if (win->m_delayedForegroundColour) - win->SetForegroundColour( win->GetForegroundColour() ); +#ifdef __WXGTK20__ + if (win->m_imContext) + { + GtkPizza *pizza = GTK_PIZZA( m_widget ); + gtk_im_context_set_client_window( (GtkIMContext*) win->m_imContext, pizza->bin_window ); + } +#endif wxWindowCreateEvent event( win ); event.SetEventObject( win ); @@ -2263,8 +2412,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(); } //----------------------------------------------------------------------------- @@ -2308,19 +2456,18 @@ void wxWindowGTK::Init() m_hAdjust = (GtkAdjustment*) NULL; m_vAdjust = (GtkAdjustment*) NULL; - m_oldHorizontalPos = 0.0; + m_oldHorizontalPos = m_oldVerticalPos = 0.0; + m_oldClientWidth = + m_oldClientHeight = 0; m_resizing = FALSE; m_widgetStyle = (GtkStyle*) NULL; 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; @@ -2329,10 +2476,15 @@ void wxWindowGTK::Init() m_delayedForegroundColour = FALSE; m_delayedBackgroundColour = FALSE; +#ifdef __WXGTK20__ + m_imContext = NULL; + m_x11Context = NULL; +#else #ifdef HAVE_XIM m_ic = (GdkIC*) NULL; m_icattr = (GdkICAttr*) NULL; #endif +#endif } wxWindowGTK::wxWindowGTK() @@ -2368,6 +2520,9 @@ bool wxWindowGTK::Create( wxWindow *parent, m_insertCallback = wxInsertChildInWindow; + // always needed for background clearing + m_delayedBackgroundColour = TRUE; + m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL ); GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS ); @@ -2383,10 +2538,7 @@ bool wxWindowGTK::Create( wxWindow *parent, m_wxwindow = gtk_pizza_new(); - gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow ); - #ifndef __WXUNIVERSAL__ -#if (GTK_MINOR_VERSION > 0) GtkPizza *pizza = GTK_PIZZA(m_wxwindow); if (HasFlag(wxRAISED_BORDER)) @@ -2405,33 +2557,13 @@ bool wxWindowGTK::Create( wxWindow *parent, { gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE ); } -#else // GTK_MINOR_VERSION == 0 - GtkViewport *viewport = GTK_VIEWPORT(scrolledWindow->viewport); - - if (HasFlag(wxRAISED_BORDER)) - { - gtk_viewport_set_shadow_type( viewport, GTK_SHADOW_OUT ); - } - else if (HasFlag(wxSUNKEN_BORDER)) - { - gtk_viewport_set_shadow_type( viewport, GTK_SHADOW_IN ); - } - else - { - gtk_viewport_set_shadow_type( viewport, GTK_SHADOW_NONE ); - } -#endif // GTK_MINOR_VERSION #endif // __WXUNIVERSAL__ + gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow ); + GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS ); m_acceptsFocus = TRUE; -#if (GTK_MINOR_VERSION == 0) - // shut the viewport up - gtk_viewport_set_hadjustment( viewport, (GtkAdjustment*) gtk_adjustment_new( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) ); - gtk_viewport_set_vadjustment( viewport, (GtkAdjustment*) gtk_adjustment_new( 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) ); -#endif // GTK_MINOR_VERSION == 0 - // I _really_ don't want scrollbars in the beginning m_vAdjust->lower = 0.0; m_vAdjust->upper = 1.0; @@ -2473,6 +2605,17 @@ bool wxWindowGTK::Create( wxWindow *parent, gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed", (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this ); +#ifdef __WXGTK20__ + // Create input method handler + m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); + + // Cannot handle drawing preedited text yet + gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); + + g_signal_connect (G_OBJECT (m_imContext), "commit", + G_CALLBACK (gtk_wxwindow_commit_cb), this); +#endif + gtk_widget_show( m_wxwindow ); if (m_parent) @@ -2489,12 +2632,17 @@ bool wxWindowGTK::Create( wxWindow *parent, wxWindowGTK::~wxWindowGTK() { + SendDestroyEvent(); + if (g_focusWindow == this) g_focusWindow = NULL; if (g_activeFrame == this) g_activeFrame = NULL; + if ( g_delayedFocus == this ) + g_delayedFocus = NULL; + m_isBeingDeleted = TRUE; m_hasVMT = FALSE; @@ -2540,17 +2688,17 @@ bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const w { wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") ); - /* this turns -1 into 20 so that a minimal window is - visible even although -1,-1 has been given as the - size of the window. the same trick is used in other - ports and should make debugging easier */ - m_width = WidthDefault(size.x); + // This turns -1 into 30 so that a minimal window is + // visible even although -1,-1 has been given as the + // size of the window. the same trick is used in other + // ports and should make debugging easier. + m_width = WidthDefault(size.x) ; m_height = HeightDefault(size.y); m_x = (int)pos.x; m_y = (int)pos.y; - /* some reasonable defaults */ + // some reasonable defaults if (!parent) { if (m_x == -1) @@ -2583,6 +2731,7 @@ void wxWindowGTK::PostCreation() gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event", GTK_SIGNAL_FUNC(gtk_window_expose_callback), (gpointer)this ); +#ifndef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw", GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this ); @@ -2591,14 +2740,30 @@ void wxWindowGTK::PostCreation() gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event", GTK_SIGNAL_FUNC(gtk_window_event_event_callback), (gpointer)this ); } +#else + // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxNO_FULL_REPAINT_ON_RESIZE ) ); +#endif + +#ifdef __WXGTK20__ + // Create input method handler + m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); + + // Cannot handle drawing preedited text yet + gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); + + g_signal_connect (G_OBJECT (m_imContext), "commit", + G_CALLBACK (gtk_wxwindow_commit_cb), this); +#endif } - // these are called when the "sunken" or "raised" borders are drawn */ + // these are called when the "sunken" or "raised" borders are drawn gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event", GTK_SIGNAL_FUNC(gtk_window_own_expose_callback), (gpointer)this ); +#ifndef __WXGTK20__ gtk_signal_connect( GTK_OBJECT(m_widget), "draw", GTK_SIGNAL_FUNC(gtk_window_own_draw_callback), (gpointer)this ); +#endif } // focus handling @@ -2606,12 +2771,6 @@ void wxWindowGTK::PostCreation() if (m_focusWidget == NULL) m_focusWidget = m_widget; -#if 0 - if (GetClassInfo() && GetClassInfo()->GetClassName()) - wxPrintf( GetClassInfo()->GetClassName() ); - wxPrintf( ".\n" ); -#endif - gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event", GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this ); @@ -2644,7 +2803,7 @@ void wxWindowGTK::PostCreation() GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this ); } - if (!GTK_IS_COMBO(m_widget)) + if ( !GTK_IS_COMBO(m_widget)) { // This is needed if we want to add our windows into native // GTK control, such as the toolbar. With this callback, the @@ -2652,7 +2811,8 @@ void wxWindowGTK::PostCreation() // programmer). Sadly, it misbehaves for wxComboBox. FIXME // when moving to GTK 2.0. gtk_signal_connect( GTK_OBJECT(m_widget), "size_request", - GTK_SIGNAL_FUNC(gtk_window_size_request_callback), (gpointer) this ); + GTK_SIGNAL_FUNC(wxgtk_window_size_request_callback), + (gpointer) this ); } m_hasVMT = TRUE; @@ -2727,21 +2887,18 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags else { GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow); - if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0) { if (x != -1) m_x = x + pizza->xoffset; if (y != -1) m_y = y + pizza->yoffset; - if (width != -1) m_width = width; - if (height != -1) m_height = height; } else { m_x = x + pizza->xoffset; m_y = y + pizza->yoffset; - m_width = width; - m_height = height; } + if (width != -1) m_width = width; + if (height != -1) m_height = height; if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH) { @@ -2753,20 +2910,27 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags if (height == -1) m_height = 26; } - if ((m_minWidth != -1) && (m_width < m_minWidth)) m_width = m_minWidth; - if ((m_minHeight != -1) && (m_height < m_minHeight)) m_height = m_minHeight; - if ((m_maxWidth != -1) && (m_width > m_maxWidth)) m_width = m_maxWidth; - if ((m_maxHeight != -1) && (m_height > m_maxHeight)) m_height = m_maxHeight; + int minWidth = GetMinWidth(), + minHeight = GetMinHeight(), + maxWidth = GetMaxWidth(), + maxHeight = GetMaxHeight(); + + if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth; + if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight; + if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth; + if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight; int border = 0; int bottom_border = 0; +#ifndef __WXGTK20__ if (GTK_WIDGET_CAN_DEFAULT(m_widget)) { /* the default button has a border around it */ border = 6; bottom_border = 5; } +#endif DoMoveWindow( m_x-border, m_y-border, @@ -2805,8 +2969,8 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags void wxWindowGTK::OnInternalIdle() { // Update invalidated regions. - Update(); - + GtkUpdate(); + // Synthetize activate events. if ( g_sendActivateEvent != -1 ) { @@ -2865,7 +3029,8 @@ void wxWindowGTK::OnInternalIdle() } } - UpdateWindowUI(); + if (wxUpdateUIEvent::CanUpdate(this)) + UpdateWindowUI(wxUPDATE_UI_FROMIDLE); } void wxWindowGTK::DoGetSize( int *width, int *height ) const @@ -3101,6 +3266,11 @@ bool wxWindowGTK::Show( bool show ) else gtk_widget_hide( m_widget ); + wxShowEvent eventShow(GetId(), show); + eventShow.m_eventObject = this; + + GetEventHandler()->ProcessEvent(eventShow); + return TRUE; } @@ -3111,7 +3281,7 @@ static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable) // Recurse, so that children have the opportunity to Do The Right Thing // and reset colours that have been messed up by a parent's (really ancestor's) // Enable call - for ( wxWindowList::Node *node = win->GetChildren().GetFirst(); + for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst(); node; node = node->GetNext() ) { @@ -3146,9 +3316,31 @@ int wxWindowGTK::GetCharHeight() const wxCHECK_MSG( m_font.Ok(), 12, wxT("invalid font") ); +#ifdef __WXGTK20__ + PangoContext *context = NULL; + if (m_widget) + context = gtk_widget_get_pango_context( m_widget ); + + if (!context) + return 0; + + PangoFontDescription *desc = m_font.GetNativeFontInfo()->description; + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, "H", 1); + PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; + + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + g_object_unref( G_OBJECT( layout ) ); + + return (int) (rect.height / PANGO_SCALE); +#else GdkFont *font = m_font.GetInternalFont( 1.0 ); return font->ascent + font->descent; +#endif } int wxWindowGTK::GetCharWidth() const @@ -3157,47 +3349,115 @@ int wxWindowGTK::GetCharWidth() const wxCHECK_MSG( m_font.Ok(), 8, wxT("invalid font") ); +#ifdef __WXGTK20__ + PangoContext *context = NULL; + if (m_widget) + context = gtk_widget_get_pango_context( m_widget ); + + if (!context) + return 0; + + PangoFontDescription *desc = m_font.GetNativeFontInfo()->description; + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, "H", 1); + PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; + + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + g_object_unref( G_OBJECT( layout ) ); + + return (int) (rect.width / PANGO_SCALE); +#else GdkFont *font = m_font.GetInternalFont( 1.0 ); return gdk_string_width( font, "H" ); +#endif } -void wxWindowGTK::GetTextExtent( const wxString& string, - int *x, - int *y, - int *descent, - int *externalLeading, - const wxFont *theFont ) const -{ - wxFont fontToUse = m_font; - if (theFont) fontToUse = *theFont; +void wxWindowGTK::GetTextExtent( const wxString& string, + int *x, + int *y, + int *descent, + int *externalLeading, + const wxFont *theFont ) const +{ + wxFont fontToUse = m_font; + if (theFont) fontToUse = *theFont; + + wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") ); + + if (string.IsEmpty()) + { + if (x) (*x) = 0; + if (y) (*y) = 0; + return; + } + +#ifdef __WXGTK20__ + PangoContext *context = NULL; + if (m_widget) + context = gtk_widget_get_pango_context( m_widget ); + + if (!context) + { + if (x) (*x) = 0; + if (y) (*y) = 0; + return; + } + + PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description; + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, desc); + { +#if wxUSE_UNICODE + const wxCharBuffer data = wxConvUTF8.cWC2MB( string ); + pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data )); +#else + const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string ); + const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata ); + 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); - wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") ); + if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE); + if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE); + if (descent) + { + // Do something about metrics here + (*descent) = 0; + } + if (externalLeading) (*externalLeading) = 0; // ?? + g_object_unref( G_OBJECT( layout ) ); +#else GdkFont *font = fontToUse.GetInternalFont( 1.0 ); - if (x) (*x) = gdk_string_width( font, string.mbc_str() ); + if (x) (*x) = gdk_string_width( font, wxGTK_CONV( string ) ); if (y) (*y) = font->ascent + font->descent; if (descent) (*descent) = font->descent; if (externalLeading) (*externalLeading) = 0; // ?? +#endif } void wxWindowGTK::SetFocus() { - wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); + wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); -#if 0 - wxPrintf( "SetFocus from " ); - if (GetClassInfo() && GetClassInfo()->GetClassName()) - wxPrintf( GetClassInfo()->GetClassName() ); - wxPrintf( ".\n" ); -#endif + if ( m_hasFocus ) + { + // don't do anything if we already have focus + return; + } if (m_wxwindow) { if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow)) { - // see comment in gtk_window_focus_out_callback() - gs_widgetLastFocus = m_wxwindow; gtk_widget_grab_focus (m_wxwindow); } } @@ -3205,25 +3465,37 @@ void wxWindowGTK::SetFocus() { if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) ) { - gtk_widget_grab_focus (m_widget); + 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()); + + gtk_widget_grab_focus (m_widget); + } } else if (GTK_IS_CONTAINER(m_widget)) { - gtk_container_focus( GTK_CONTAINER(m_widget), GTK_DIR_TAB_FORWARD ); + SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD ); } else { - // ? + wxLogTrace(TRACE_FOCUS, + _T("Can't set focus to %s(%s)"), + GetClassInfo()->GetClassName(), GetLabel().c_str()); } } - -#if 0 - wxPrintf( "SetFocus finished in " ); - if (GetClassInfo() && GetClassInfo()->GetClassName()) - wxPrintf( GetClassInfo()->GetClassName() ); - wxPrintf( ".\n" ); -#endif - } bool wxWindowGTK::AcceptsFocus() const @@ -3333,16 +3605,27 @@ void wxWindowGTK::WarpPointer( int x, int y ) gdk_window_warp_pointer( window, x, y ); } + void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) { if (!m_widget) return; if (!m_widget->window) return; - // temporarily hide the caret to avoid nasty interactions between caret - // drawing and the window contents redraw -#if 0 // def wxUSE_CARET -- doesn't seem to help :-( - wxCaretSuspend cs((wxWindow *)this); -#endif // wxUSE_CARET +#ifndef __WXGTK20__ + if (g_isIdle) + wxapp_install_idle_handler(); + + wxRect myRect(0,0,0,0); + if (m_wxwindow && rect) + { + myRect.SetSize(wxSize( m_wxwindow->allocation.width, + m_wxwindow->allocation.height)); + myRect.Intersect(*rect); + if (!myRect.width || !myRect.height) + // nothing to do, rectangle is empty + return; + rect = &myRect; + } if (eraseBackground && m_wxwindow && m_wxwindow->window) { @@ -3389,47 +3672,136 @@ void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect ) gtk_widget_draw( m_widget, (GdkRectangle*) NULL ); } } +#else + if (m_wxwindow) + { + if (rect) + { + GdkRectangle gdk_rect; + gdk_rect.x = rect->x; + gdk_rect.y = rect->y; + gdk_rect.width = rect->width; + gdk_rect.height = rect->height; + gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, &gdk_rect, TRUE ); + } + else + { + gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE ); + } + } +#endif } void wxWindowGTK::Update() { + GtkUpdate(); +} + +void wxWindowGTK::GtkUpdate() +{ +#ifdef __WXGTK20__ + if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window) + gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE ); +#else if (!m_updateRegion.IsEmpty()) - { GtkSendPaintEvents(); - } +#endif } void wxWindowGTK::GtkSendPaintEvents() { if (!m_wxwindow) { +#ifndef __WXGTK20__ m_clearRegion.Clear(); +#endif m_updateRegion.Clear(); return; } + // Clip to paint region in wxClientDC m_clipPaintRegion = TRUE; - if (!m_clearRegion.IsEmpty()) +#ifndef __WXGTK20__ + // widget to draw on + GtkPizza *pizza = GTK_PIZZA (m_wxwindow); + + // later for GTK 2.0, too. + if (GetThemeEnabled()) + { + // find ancestor from which to steal background + wxWindow *parent = GetParent(); + while (parent && !parent->IsTopLevel()) + parent = parent->GetParent(); + if (!parent) + parent = (wxWindow*)this; + + 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, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &rect, + parent->m_widget, + (char *)"base", + 0, 0, -1, -1 ); + + upd ++; + } + } + else +#endif + +#ifdef __WXGTK20__ { wxWindowDC dc( (wxWindow*)this ); - dc.SetClippingRegion( m_clearRegion ); - + dc.SetClippingRegion( m_updateRegion ); + + wxEraseEvent erase_event( GetId(), &dc ); + erase_event.SetEventObject( this ); + + GetEventHandler()->ProcessEvent(erase_event); + } +#else + // if (!m_clearRegion.IsEmpty()) // Always send an erase event under GTK 1.2 + { + wxWindowDC dc( (wxWindow*)this ); + if (m_clearRegion.IsEmpty()) + dc.SetClippingRegion( m_updateRegion ); + else + dc.SetClippingRegion( m_clearRegion ); + wxEraseEvent erase_event( GetId(), &dc ); erase_event.SetEventObject( this ); - + if (!GetEventHandler()->ProcessEvent(erase_event)) { + if (!g_eraseGC) + { + g_eraseGC = gdk_gc_new( pizza->bin_window ); + gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); + } + gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() ); + wxRegionIterator upd( m_clearRegion ); while (upd) { - gdk_window_clear_area( GTK_PIZZA(m_wxwindow)->bin_window, - upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() ); + gdk_draw_rectangle( pizza->bin_window, g_eraseGC, 1, + upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() ); upd ++; } } m_clearRegion.Clear(); } +#endif wxNcPaintEvent nc_paint_event( GetId() ); nc_paint_event.SetEventObject( this ); @@ -3439,21 +3811,70 @@ void wxWindowGTK::GtkSendPaintEvents() paint_event.SetEventObject( this ); GetEventHandler()->ProcessEvent( paint_event ); - m_updateRegion.Clear(); - m_clipPaintRegion = FALSE; + +#ifndef __WXUNIVERSAL__ +#ifndef __WXGTK20__ + // The following code will result in all window-less widgets + // being redrawn because the wxWindows class is allowed to + // paint over the window-less widgets. + + GList *children = pizza->children; + while (children) + { + GtkPizzaChild *child = (GtkPizzaChild*) children->data; + children = children->next; + + if (GTK_WIDGET_NO_WINDOW (child->widget) && + GTK_WIDGET_DRAWABLE (child->widget)) + { + // Get intersection of widget area and update region + wxRegion region( m_updateRegion ); + + GdkEventExpose gdk_event; + gdk_event.type = GDK_EXPOSE; + gdk_event.window = pizza->bin_window; + gdk_event.count = 0; + + wxRegionIterator upd( m_updateRegion ); + while (upd) + { + GdkRectangle rect; + rect.x = upd.GetX(); + rect.y = upd.GetY(); + rect.width = upd.GetWidth(); + rect.height = upd.GetHeight(); + + if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area)) + { + gtk_widget_event (child->widget, (GdkEvent*) &gdk_event); + } + + upd ++; + } + } + } +#endif +#endif + + m_updateRegion.Clear(); } void wxWindowGTK::Clear() { wxCHECK_RET( m_widget != NULL, wxT("invalid window") ); - if (!m_widget->window) return; - +#ifndef __WXGTK20__ if (m_wxwindow && m_wxwindow->window) { -// gdk_window_clear( m_wxwindow->window ); + m_clearRegion.Clear(); + wxSize size( GetClientSize() ); + m_clearRegion.Union( 0,0,size.x,size.y ); + + // Better do this in idle? + GtkUpdate(); } +#endif } #if wxUSE_TOOLTIPS @@ -3471,16 +3892,34 @@ void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip ) } #endif // wxUSE_TOOLTIPS +void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour ) +{ + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; + + wxASSERT( window ); + + // We need the pixel value e.g. for background clearing. + m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); + + if (m_wxwindow) + { + // wxMSW doesn't clear the window here, either. + gdk_window_set_background( window, m_backgroundColour.GetColor() ); + } + + ApplyWidgetStyle(); +} + bool wxWindowGTK::SetBackgroundColour( const wxColour &colour ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); if (!wxWindowBase::SetBackgroundColour(colour)) - { - // don't leave if the GTK widget has just - // been realized - if (!m_delayedBackgroundColour) return FALSE; - } + return FALSE; GdkWindow *window = (GdkWindow*) NULL; if (m_wxwindow) @@ -3494,24 +3933,29 @@ bool wxWindowGTK::SetBackgroundColour( const wxColour &colour ) // but it couldn't get applied as the // widget hasn't been realized yet. m_delayedBackgroundColour = TRUE; + return TRUE; } - - if ((m_wxwindow) && - (m_wxwindow->window) && - (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))) + else { - /* wxMSW doesn't clear the window here. I don't do that either to - provide compatibility. call Clear() to do the job. */ - - m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) ); - gdk_window_set_background( window, m_backgroundColour.GetColor() ); + GtkSetBackgroundColour( colour ); } - ApplyWidgetStyle(); - return TRUE; } +void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour ) +{ + GdkWindow *window = (GdkWindow*) NULL; + if (m_wxwindow) + window = GTK_PIZZA(m_wxwindow)->bin_window; + else + window = GetConnectWidget()->window; + + wxASSERT( window ); + + ApplyWidgetStyle(); +} + bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) { wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") ); @@ -3536,21 +3980,37 @@ bool wxWindowGTK::SetForegroundColour( const wxColour &colour ) // widget hasn't been realized yet. m_delayedForegroundColour = TRUE; } - - ApplyWidgetStyle(); + else + { + GtkSetForegroundColour( colour ); + } return TRUE; } +#ifdef __WXGTK20__ +PangoContext *wxWindowGTK::GtkGetPangoDefaultContext() +{ + return gtk_widget_get_pango_context( m_widget ); +} + +PangoContext *wxWindowGTK::GtkGetPangoX11Context() +{ + if (!m_x11Context) + m_x11Context = pango_x_get_context( gdk_display ); + + return m_x11Context; +} +#endif + GtkStyle *wxWindowGTK::GetWidgetStyle() { if (m_widgetStyle) { GtkStyle *remake = gtk_style_copy( m_widgetStyle ); -#ifdef __WXGTK20__ - /* FIXME: is this necessary? */ - _G_TYPE_IGC(remake, GtkObjectClass) = _G_TYPE_IGC(m_widgetStyle, GtkObjectClass); -#else + + // FIXME: no more klass in 2.0 +#ifndef __WXGTK20__ remake->klass = m_widgetStyle->klass; #endif @@ -3565,10 +4025,9 @@ GtkStyle *wxWindowGTK::GetWidgetStyle() def = gtk_widget_get_default_style(); m_widgetStyle = gtk_style_copy( def ); -#ifdef __WXGTK20__ - /* FIXME: is this necessary? */ - _G_TYPE_IGC(m_widgetStyle, GtkObjectClass) = _G_TYPE_IGC(def, GtkObjectClass); -#else + + // FIXME: no more klass in 2.0 +#ifndef __WXGTK20__ m_widgetStyle->klass = def->klass; #endif } @@ -3578,7 +4037,7 @@ GtkStyle *wxWindowGTK::GetWidgetStyle() void wxWindowGTK::SetWidgetStyle() { -#if DISABLE_STYLE_IF_BROKEN_THEM +#if DISABLE_STYLE_IF_BROKEN_THEME if (m_widget->style->engine_data) { static bool s_warningPrinted = FALSE; @@ -3596,8 +4055,13 @@ void wxWindowGTK::SetWidgetStyle() if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT )) { +#ifdef __WXGTK20__ + pango_font_description_free( style->font_desc ); + style->font_desc = pango_font_description_copy( m_font.GetNativeFontInfo()->description ); +#else gdk_font_unref( style->font ); style->font = gdk_font_ref( m_font.GetInternalFont( 1.0 ) ); +#endif } if (m_foregroundColour.Ok()) @@ -3684,7 +4148,7 @@ void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting ) static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win ) { menu->SetInvokingWindow( win ); - wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst(); + wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst(); while (node) { wxMenuItem *menuitem = node->GetData(); @@ -3707,6 +4171,9 @@ static gint gs_pop_y = 0; extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu, gint *x, gint *y, +#ifdef __WXGTK20__ + gboolean * WXUNUSED(whatever), +#endif gpointer WXUNUSED(user_data) ) { // ensure that the menu appears entirely on screen @@ -3755,8 +4222,7 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) while (is_waiting) { - while (gtk_events_pending()) - gtk_main_iteration(); + gtk_main_iteration(); } return TRUE; @@ -4042,89 +4508,37 @@ void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) ) // No scrolling requested. if ((dx == 0) && (dy == 0)) return; - + +#ifndef __WXGTK20__ if (!m_updateRegion.IsEmpty()) { m_updateRegion.Offset( dx, dy ); - + int cw = 0; int ch = 0; GetClientSize( &cw, &ch ); m_updateRegion.Intersect( 0, 0, cw, ch ); } - + if (!m_clearRegion.IsEmpty()) { m_clearRegion.Offset( dx, dy ); - + int cw = 0; int ch = 0; GetClientSize( &cw, &ch ); m_clearRegion.Intersect( 0, 0, cw, ch ); } - -#if 1 +#endif m_clipPaintRegion = TRUE; - - gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); - - m_clipPaintRegion = FALSE; - -#else - - if (m_children.GetCount() > 0) - { - gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); - } - else - { - GtkPizza *pizza = GTK_PIZZA(m_wxwindow); - - pizza->xoffset -= dx; - pizza->yoffset -= dy; - - GdkGC *m_scrollGC = gdk_gc_new( pizza->bin_window ); - gdk_gc_set_exposures( m_scrollGC, TRUE ); - - int cw = 0; - int ch = 0; - GetClientSize( &cw, &ch ); - int w = cw - abs(dx); - int h = ch - abs(dy); - if ((h < 0) || (w < 0)) - { - Refresh(); - } - else - { - int s_x = 0; - int s_y = 0; - if (dx < 0) s_x = -dx; - if (dy < 0) s_y = -dy; - int d_x = 0; - int d_y = 0; - if (dx > 0) d_x = dx; - if (dy > 0) d_y = dy; - - gdk_window_copy_area( pizza->bin_window, m_scrollGC, d_x, d_y, - pizza->bin_window, s_x, s_y, w, h ); - - wxRect rect; - if (dx < 0) rect.x = cw+dx; else rect.x = 0; - if (dy < 0) rect.y = ch+dy; else rect.y = 0; - if (dy != 0) rect.width = cw; else rect.width = abs(dx); - if (dx != 0) rect.height = ch; else rect.height = abs(dy); - - Refresh( TRUE, &rect ); - } + gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy ); - gdk_gc_unref( m_scrollGC ); - } -#endif + m_clipPaintRegion = FALSE; } + // Find the wxWindow at the current mouse position, also returning the mouse // position. wxWindow* wxFindWindowAtPointer(wxPoint& pt) @@ -4148,10 +4562,8 @@ wxPoint wxGetMousePosition() int x, y; GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y); - if (!windowAtPtr) - return wxPoint(-999, -999); - Display *display = GDK_WINDOW_XDISPLAY(windowAtPtr); + Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY(); Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display)); Window rootReturn, childReturn; int rootX, rootY, winX, winY; @@ -4166,3 +4578,34 @@ wxPoint wxGetMousePosition() } +// ---------------------------------------------------------------------------- +// wxDCModule +// ---------------------------------------------------------------------------- + +class wxWinModule : public wxModule +{ +public: + bool OnInit(); + void OnExit(); + +private: + DECLARE_DYNAMIC_CLASS(wxWinModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule) + +bool wxWinModule::OnInit() +{ + // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() ); + // gdk_gc_set_fill( g_eraseGC, GDK_SOLID ); + + return TRUE; +} + +void wxWinModule::OnExit() +{ + if (g_eraseGC) + gdk_gc_unref( g_eraseGC ); +} + +// vi:sts=4:sw=4:et