X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/fe161a2685012cf90bb5bfe95f2260aee71d8ad7..422d0ff0bec79832494fe4605ffdcf8e87ba6c03:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 8e5f4bdd3b..8947ad7008 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -965,6 +965,30 @@ static inline bool wxIsAsciiKeysym(KeySym ks) return ks < 256; } +static void wxFillOtherKeyEventFields(wxKeyEvent& event, + wxWindowGTK *win, + GdkEventKey *gdk_event) +{ + int x = 0; + int y = 0; + GdkModifierType state; + if (gdk_event->window) + gdk_window_get_pointer(gdk_event->window, &x, &y, &state); + + event.SetTimestamp( gdk_event->time ); + event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0; + event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0; + event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0; + event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0; + event.m_scanCode = gdk_event->keyval; + event.m_rawCode = (wxUint32) gdk_event->keyval; + event.m_rawFlags = 0; + event.m_x = x; + event.m_y = y; + event.SetEventObject( win ); +} + + static bool wxTranslateGTKKeyEventToWx(wxKeyEvent& event, wxWindowGTK *win, @@ -1060,29 +1084,32 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event, return FALSE; // now fill all the other fields - int x = 0; - int y = 0; - GdkModifierType state; - if (gdk_event->window) - gdk_window_get_pointer(gdk_event->window, &x, &y, &state); - - event.SetTimestamp( gdk_event->time ); - event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0; - event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0; - event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0; - event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK) != 0; + wxFillOtherKeyEventFields(event, win, gdk_event); + event.m_keyCode = key_code; - event.m_scanCode = gdk_event->keyval; - event.m_rawCode = (wxUint32) gdk_event->keyval; - event.m_rawFlags = 0; - event.m_x = x; - event.m_y = y; - event.SetEventObject( win ); return TRUE; } +#ifdef __WXGTK20__ +struct wxGtkIMData +{ + GtkIMContext *context; + GdkEventKey *lastKeyEvent; + + wxGtkIMData() + { + context = gtk_im_multicontext_new(); + lastKeyEvent = NULL; + } + ~wxGtkIMData() + { + g_object_unref(context); + } +}; +#endif + static gint gtk_window_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxWindow *win ) @@ -1096,12 +1123,43 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, return FALSE; if (g_blockEventsOnDrag) return FALSE; - - + +#ifdef __WXGTK20__ + // We have to pass key press events through GTK+'s Input Method context + // object in order to get correct characters. By doing so, we loose the + // ability to let other GTK+'s handlers (namely, widgets' default signal + // handlers) handle the signal by returning false from this callback. + // Because GTK+ sends the events to parent widgets as well, we can't + // afford loosing it, otherwise native widgets inserted into wxPanel + // would break in subtle ways (e.g. spacebar would no longer toggle + // wxCheckButton's state). Therefore, we only pass the event to IM if it + // originated in this window's widget, which we detect by checking if we've + // seen the same event before (no events from children are lost this way, + // because gtk_window_key_press_callback is installed for native controls + // as well and the wxKeyEvent it creates propagates upwards). + static GdkEventKey s_lastEvent; + + bool useIM = (win->m_imData != NULL) && + memcmp(gdk_event, &s_lastEvent, sizeof(GdkEventKey)) != 0; + + s_lastEvent = *gdk_event; +#endif + wxKeyEvent event( wxEVT_KEY_DOWN ); if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) { // unknown key pressed, ignore (the event would be useless anyhow) +#ifdef __WXGTK20__ + if ( useIM ) + { + // it may be useful for the input method, though: + win->m_imData->lastKeyEvent = gdk_event; + bool ret = gtk_im_context_filter_keypress(win->m_imData->context, + gdk_event); + win->m_imData->lastKeyEvent = NULL; + return ret; + } +#endif return FALSE; } @@ -1132,58 +1190,63 @@ static gint gtk_window_key_press_callback( GtkWidget *widget, // 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 ) + if (useIM) { - gtk_im_context_filter_keypress ( (GtkIMContext*) win->m_imContext, gdk_event ); - ret = TRUE; + // 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. + win->m_imData->lastKeyEvent = gdk_event; + if ( gtk_im_context_filter_keypress(win->m_imData->context, + gdk_event) ) + { + win->m_imData->lastKeyEvent = NULL; + wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM")); + return TRUE; + } + else + win->m_imData->lastKeyEvent = NULL; } - else #endif + + long key_code; + KeySym keysym = gdk_event->keyval; + // Find key code for EVT_CHAR and EVT_CHAR_HOOK events + key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); + if ( !key_code ) { - // Find key code for EVT_CHAR and EVT_CHAR_HOOK events - key_code = wxTranslateKeySymToWXKey(keysym, TRUE /* isChar */); - if ( !key_code ) + if ( gdk_event->length == 1 ) { - if ( gdk_event->length == 1 ) - { - key_code = (unsigned char)gdk_event->string[0]; - } - else if ( wxIsAsciiKeysym(keysym) ) - { - // ASCII key - key_code = (unsigned char)keysym; - } + key_code = (unsigned char)gdk_event->string[0]; } - - if ( key_code ) + else if ( wxIsAsciiKeysym(keysym) ) { - wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code); + // ASCII key + key_code = (unsigned char)keysym; + } + } - event.m_keyCode = key_code; + if ( key_code ) + { + wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), 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 ); - } + event.m_keyCode = key_code; - if (!ret) - { - event.SetEventType(wxEVT_CHAR); - ret = win->GetEventHandler()->ProcessEvent( event ); - } + // 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 ); } } } @@ -1263,28 +1326,37 @@ static void gtk_wxwindow_commit_cb (GtkIMContext *context, const gchar *str, wxWindow *window) { - bool ret = FALSE; - wxKeyEvent event( wxEVT_KEY_DOWN ); + // take modifiers, cursor position, timestamp etc. from the last + // key_press_event that was fed into Input Method: + if (window->m_imData->lastKeyEvent) + { + wxFillOtherKeyEventFields(event, + window, window->m_imData->lastKeyEvent); + } + #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; + wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), 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... + wchar_t unistr[2]; + unistr[0] = g_utf8_get_char(str); + unistr[1] = 0; + wxCharBuffer ansistr(wxConvLocal.cWC2MB(unistr)); + // We cannot handle characters that cannot be represented in + // current locale's charset in non-Unicode mode: + if (ansistr.data() == NULL) + return; + event.m_keyCode = ansistr[0u]; + wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), (wxChar)event.m_keyCode); +#endif // wxUSE_UNICODE + bool ret = false; // Implement OnCharHook by checking ancestor top level windows wxWindow *parent = window; @@ -1534,8 +1606,8 @@ static gint gtk_window_button_press_callback( GtkWidget *widget, wxEventType event_type = wxEVT_NULL; - // GdkDisplay is a GTK+ 2.1.0 thing -#if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 1, 0) + // GdkDisplay is a GTK+ 2.2.0 thing +#if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0) if ( gdk_event->type == GDK_2BUTTON_PRESS && gdk_event->button >= 1 && gdk_event->button <= 3 ) { @@ -1900,6 +1972,11 @@ static gint gtk_window_focus_in_callback( GtkWidget *widget, if (g_isIdle) wxapp_install_idle_handler(); +#ifdef __WXGTK20__ + if (win->m_imData) + gtk_im_context_focus_in(win->m_imData->context); +#endif + if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; @@ -1988,6 +2065,11 @@ static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk if (g_isIdle) wxapp_install_idle_handler(); +#ifdef __WXGTK20__ + if (win->m_imData) + gtk_im_context_focus_out(win->m_imData->context); +#endif + if (!win->m_hasVMT) return FALSE; if (g_blockEventsOnDrag) return FALSE; @@ -2308,10 +2390,11 @@ gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win ) wxapp_install_idle_handler(); #ifdef __WXGTK20__ - if (win->m_imContext) + if (win->m_imData) { GtkPizza *pizza = GTK_PIZZA( m_widget ); - gtk_im_context_set_client_window( (GtkIMContext*) win->m_imContext, pizza->bin_window ); + gtk_im_context_set_client_window( win->m_imData->context, + pizza->bin_window ); } #endif @@ -2570,8 +2653,9 @@ void wxWindowGTK::Init() m_cursor = *wxSTANDARD_CURSOR; #ifdef __WXGTK20__ - m_imContext = NULL; + m_imData = NULL; m_x11Context = NULL; + m_dirtyTabOrder = false; #else #ifdef HAVE_XIM m_ic = (GdkIC*) NULL; @@ -2695,17 +2779,6 @@ 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) @@ -2757,6 +2830,10 @@ wxWindowGTK::~wxWindowGTK() gtk_widget_destroy( m_widget ); m_widget = (GtkWidget*) NULL; } + +#ifdef __WXGTK20__ + delete m_imData; +#endif } bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size ) @@ -2801,18 +2878,18 @@ void wxWindowGTK::PostCreation() #else // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), !HasFlag( wxFULL_REPAINT_ON_RESIZE ) ); #endif + } #ifdef __WXGTK20__ // Create input method handler - m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new (); + m_imData = new wxGtkIMData; // Cannot handle drawing preedited text yet - gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE ); + gtk_im_context_set_use_preedit( m_imData->context, FALSE ); - g_signal_connect (G_OBJECT (m_imContext), "commit", - G_CALLBACK (gtk_wxwindow_commit_cb), this); + g_signal_connect (G_OBJECT (m_imData->context), "commit", + G_CALLBACK (gtk_wxwindow_commit_cb), this); #endif - } // these are called when the "sunken" or "raised" borders are drawn gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event", @@ -3040,6 +3117,11 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags void wxWindowGTK::OnInternalIdle() { +#ifdef __WXGTK20__ + if ( m_dirtyTabOrder ) + RealizeTabOrder(); +#endif + // Update invalidated regions. GtkUpdate(); @@ -3408,7 +3490,7 @@ int wxWindowGTK::GetCharHeight() const g_object_unref( G_OBJECT( layout ) ); - return (int) (rect.height / PANGO_SCALE); + return (int) PANGO_PIXELS(rect.height); #else GdkFont *gfont = font.GetInternalFont( 1.0 ); @@ -3442,7 +3524,7 @@ int wxWindowGTK::GetCharWidth() const g_object_unref( G_OBJECT( layout ) ); - return (int) (rect.width / PANGO_SCALE); + return (int) PANGO_PIXELS(rect.width); #else GdkFont *gfont = font.GetInternalFont( 1.0 ); @@ -3493,17 +3575,18 @@ void wxWindowGTK::GetTextExtent( const wxString& string, pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data )); #endif } - PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data; PangoRectangle rect; - pango_layout_line_get_extents(line, NULL, &rect); + pango_layout_get_extents(layout, NULL, &rect); - if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE); - if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE); + if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width); + if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height); if (descent) { - // Do something about metrics here - (*descent) = 0; + PangoLayoutIter *iter = pango_layout_get_iter(layout); + int baseline = pango_layout_iter_get_baseline(iter); + pango_layout_iter_free(iter); + *descent = *y - PANGO_PIXELS(baseline); } if (externalLeading) (*externalLeading) = 0; // ?? @@ -3627,6 +3710,62 @@ void wxWindowGTK::DoAddChild(wxWindowGTK *child) (*m_insertCallback)(this, child); } +#ifdef __WXGTK20__ + +void wxWindowGTK::AddChild(wxWindowBase *child) +{ + wxWindowBase::AddChild(child); + m_dirtyTabOrder = true; + if (g_isIdle) + wxapp_install_idle_handler(); +} + +void wxWindowGTK::RemoveChild(wxWindowBase *child) +{ + wxWindowBase::RemoveChild(child); + m_dirtyTabOrder = true; + if (g_isIdle) + wxapp_install_idle_handler(); +} + +void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move) +{ + wxWindowBase::DoMoveInTabOrder(win, move); + m_dirtyTabOrder = true; + if (g_isIdle) + wxapp_install_idle_handler(); +} + +void wxWindowGTK::RealizeTabOrder() +{ + if (m_wxwindow) + { + if (m_children.size() > 0) + { + GList *chain = NULL; + + for (wxWindowList::const_iterator i = m_children.begin(); + i != m_children.end(); ++i) + { + chain = g_list_prepend(chain, (*i)->m_widget); + } + + chain = g_list_reverse(chain); + + gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain); + g_list_free(chain); + } + else + { + gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow)); + } + } + + m_dirtyTabOrder = false; +} + +#endif // __WXGTK20__ + void wxWindowGTK::Raise() { wxCHECK_RET( (m_widget != NULL), wxT("invalid window") ); @@ -4024,15 +4163,15 @@ GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle) { // do we need to apply any changes at all? if ( !forceStyle && - !m_hasFont && !m_hasFgCol && - (!m_hasBgCol || !m_backgroundColour.Ok()) ) + !m_font.Ok() && + !m_foregroundColour.Ok() && !m_backgroundColour.Ok() ) { return NULL; } GtkRcStyle *style = gtk_rc_style_new(); - if ( m_hasFont ) + if ( m_font.Ok() ) { #ifdef __WXGTK20__ style->font_desc = @@ -4090,15 +4229,21 @@ void wxWindowGTK::ApplyWidgetStyle(bool forceStyle) GtkRcStyle *style = CreateWidgetStyle(forceStyle); if ( style ) { - if (m_wxwindow) - // should we also do m_widget in this case? - gtk_widget_modify_style(m_wxwindow, style); - else - gtk_widget_modify_style(m_widget, style); + DoApplyWidgetStyle(style); gtk_rc_style_unref(style); } } +void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style) +{ + if (m_wxwindow) + // should we also do m_widget in this case? + gtk_widget_modify_style(m_wxwindow, style); + else + gtk_widget_modify_style(m_widget, style); +} + + //----------------------------------------------------------------------------- // Pop-up menu stuff //-----------------------------------------------------------------------------