/* Class functions */ gboolean scim_bridge_client_imcontext_filter_key_event (GtkIMContext *context, GdkEventKey *event) { scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_filter_key_event ()"); ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context); if (!(event->send_event & SEND_EVENT_MASK) && scim_bridge_client_is_messenger_opened () && imcontext != NULL && !key_snooper_used) { if (imcontext->client_window != NULL) { int new_window_x; int new_window_y; gdk_window_get_origin (imcontext->client_window, &new_window_x, &new_window_y); if (imcontext->window_x != new_window_x || imcontext->window_y != new_window_y) { imcontext->window_x = new_window_x; imcontext->window_y = new_window_y; scim_bridge_pdebugln (1, "The cursor location is changed: x = %d + %d\ty = %d + %d", imcontext->window_x, imcontext->cursor_x, imcontext->window_y, imcontext->cursor_y); if (set_cursor_location (imcontext, new_window_x, new_window_y, imcontext->cursor_x, imcontext->cursor_y)) { scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_filter_key_event ()"); return gtk_im_context_filter_keypress (fallback_imcontext, event); } } } boolean consumed = FALSE; if (filter_key_event (imcontext, event, &consumed)) { scim_bridge_perrorln ("An IOException occurred at scim_bridge_client_imcontext_filter_key_event ()"); } else if (consumed) { return TRUE; } } unsigned int accelerator_mask = (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK); if (imcontext == NULL || !imcontext->enabled) { return gtk_im_context_filter_keypress (fallback_imcontext, event); } else if (event->type == GDK_KEY_PRESS && (event->state & accelerator_mask) == 0) { guint32 wchar = gdk_keyval_to_unicode (event->keyval); if (wchar != 0) { gchar buffer[10]; const int buffer_length = g_unichar_to_utf8 (wchar, buffer); buffer[buffer_length] = '\0'; g_signal_emit_by_name (focused_imcontext, "commit", &buffer); return TRUE; } }
static gboolean st_im_text_key_release_event (ClutterActor *actor, ClutterKeyEvent *event) { StIMText *self = ST_IM_TEXT (actor); StIMTextPrivate *priv = self->priv; ClutterText *clutter_text = CLUTTER_TEXT (actor); GdkEventKey *event_gdk; gboolean result = FALSE; if (clutter_text_get_editable (clutter_text)) { event_gdk = key_event_to_gdk (event); if (gtk_im_context_filter_keypress (priv->im_context, event_gdk)) { priv->need_im_reset = TRUE; result = TRUE; } gdk_event_free ((GdkEvent *)event_gdk); } if (!result && CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event) result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event (actor, event); return result; }
bool gKey::enable(gControl *control, GdkEventKey *event) { bool f = false; if (_valid) disable(); _valid = true; _canceled = false; if (event) { //if (event->type == GDK_KEY_RELEASE) _im_no_commit = false; _event = *event; _event.window = _im_window; if (!_no_input_method && control == _im_control) { #if DEBUG_IM fprintf(stderr, "gKey::enable: %p flag = %d event->string = %d\n", event, (event->state & (1 << 25)) != 0, *event->string); #endif f = gtk_im_context_filter_keypress(_im_context, &_event); #if DEBUG_IM fprintf(stderr, "gKey::enable: %p filter -> %d\n", event, f); #endif } } return f || _canceled; }
void EditorClient::handleInputMethodKeydown(KeyboardEvent* event) { WebKitWebViewPrivate* priv = m_webView->priv; // TODO: Dispatch IE-compatible text input events for IM events. if (gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey())) event->setDefaultHandled(); }
static gboolean nsgtk_tree_window_keyrelease_event(GtkWidget *widget, GdkEventKey *event, gpointer g) { struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; return gtk_im_context_filter_keypress(tw->input_method, event); }
static gboolean key_release(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { update_keyboard_state(event,FALSE); if(gtk_im_context_filter_keypress(input_method,event)) return TRUE; return FALSE; }
bool TextInputGTK::FilterKeyPress() { if (mContext) { GdkEvent *keyEvent = gtk_get_current_event(); bool result = gtk_im_context_filter_keypress( mContext, reinterpret_cast<GdkEventKey *>(keyEvent) ); gdk_event_free( keyEvent ); return result; } return false; }
void EditorClient::handleInputMethodKeydown(KeyboardEvent* event) { Frame* targetFrame = core(m_webView)->focusController()->focusedOrMainFrame(); if (!targetFrame || !targetFrame->editor()->canEdit()) return; WebKitWebViewPrivate* priv = m_webView->priv; // TODO: Dispatch IE-compatible text input events for IM events. if (gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey())) event->setDefaultHandled(); }
void EditorClient::handleInputMethodKeydown(KeyboardEvent* event) { Frame* targetFrame = core(m_webView)->focusController()->focusedOrMainFrame(); if (!targetFrame || !targetFrame->editor()->canEdit()) return; WebKitWebViewPrivate* priv = m_webView->priv; m_preventNextCompositionCommit = false; // Some IM contexts (e.g. 'simple') will act as if they filter every // keystroke and just issue a 'commit' signal during handling. In situations // where the 'commit' signal happens during filtering and there is no active // composition, act as if the keystroke was not filtered. The one exception to // this is when the keyval parameter of the GdkKeyEvent is 0, which is often // a key event sent by the IM context for committing the current composition. // Here is a typical sequence of events for the 'simple' context: // 1. GDK key press event -> webkit_web_view_key_press_event // 2. Keydown event -> EditorClient::handleInputMethodKeydown // gtk_im_context_filter_keypress returns true, but there is a pending // composition so event->preventDefault is not called (below). // 3. Keydown event bubbles through the DOM // 4. Keydown event -> EditorClient::handleKeyboardEvent // No action taken. // 4. GDK key release event -> webkit_web_view_key_release_event // 5. gtk_im_context_filter_keypress is called on the release event. // Simple does not filter most key releases, so the event continues. // 6. Keypress event bubbles through the DOM. // 7. Keypress event -> EditorClient::handleKeyboardEvent // pending composition is inserted. // 8. Keyup event bubbles through the DOM. // 9. Keyup event -> EditorClient::handleKeyboardEvent // No action taken. // There are two situations where we do filter the keystroke: // 1. The IMContext instructed us to filter and we have no pending composition. // 2. The IMContext did not instruct us to filter, but the keystroke caused a // composition in progress to finish. It seems that sometimes SCIM will finish // a composition and not mark the keystroke as filtered. m_treatContextCommitAsKeyEvent = (!targetFrame->editor()->hasComposition()) && event->keyEvent()->gdkEventKey()->keyval; clearPendingComposition(); if ((gtk_im_context_filter_keypress(priv->imContext.get(), event->keyEvent()->gdkEventKey()) && !m_pendingComposition) || (!m_treatContextCommitAsKeyEvent && !targetFrame->editor()->hasComposition())) event->preventDefault(); m_treatContextCommitAsKeyEvent = false; }
static gboolean key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { update_keyboard_state(event,TRUE); if(gtk_im_context_filter_keypress(input_method,event)) return TRUE; if(check_key_action(widget,event)) { gtk_im_context_reset(input_method); return TRUE; } return FALSE; }
static gboolean keystroke_cb ( GtkWidget* widget, GdkEventKey* event, gpointer user_data ) { GtkIMContext* im; im = (GtkIMContext*) user_data; if (gtk_im_context_filter_keypress(im, event)) { return TRUE; } return FALSE; }
static gboolean key_pressed_cb(GtkWidget* widget, GdkEventKey* event, gpointer user_data) { HybridConversation *conv; conv = (HybridConversation*)user_data; if (event->keyval == GDK_Return || event->keyval == GDK_ISO_Enter || event->keyval == GDK_KP_Enter) { if (event->state & GDK_CONTROL_MASK || event->state & GDK_SHIFT_MASK) { return FALSE; } else { /* find the current chat panel. */ if (gtk_im_context_filter_keypress( GTK_TEXT_VIEW(widget)->im_context, event)) { GTK_TEXT_VIEW(widget)->need_im_reset = TRUE; return TRUE; } message_send(conv); return TRUE; } #if 0 if(event->state & GDK_CONTROL_MASK) { return TRUE; }else{ return FALSE; } #endif } return FALSE; }
void scim_bridge_client_imcontext_forward_key_event (ScimBridgeClientIMContext *imcontext, const ScimBridgeKeyEvent *key_event) { if (imcontext && imcontext == focused_imcontext) { GdkEventKey gdk_event; scim_bridge_key_event_bridge_to_gdk (&gdk_event, imcontext->client_window, key_event); gdk_event.send_event |= SEND_EVENT_MASK; if (!gtk_im_context_filter_keypress (GTK_IM_CONTEXT (imcontext->slave), &gdk_event)) { // To avoid timing issue, we need emit the signal directly, rather than put the event into the queue. if (focused_widget) { gboolean result; g_signal_emit_by_name(focused_widget, scim_bridge_key_event_is_pressed (key_event) ? "key-press-event" : "key-release-event", &gdk_event, &result ); } else { gdk_event_put ((GdkEvent *) &gdk_event); } } } }
static gboolean st_im_text_captured_event (ClutterActor *actor, ClutterEvent *event) { StIMText *self = ST_IM_TEXT (actor); StIMTextPrivate *priv = self->priv; ClutterText *clutter_text = CLUTTER_TEXT (actor); ClutterEventType type = clutter_event_type (event); gboolean result = FALSE; int old_position; if (type != CLUTTER_KEY_PRESS && type != CLUTTER_KEY_RELEASE) return FALSE; if (clutter_text_get_editable (clutter_text)) { GdkEventKey *event_gdk = key_event_to_gdk ((ClutterKeyEvent *)event); if (gtk_im_context_filter_keypress (priv->im_context, event_gdk)) { priv->need_im_reset = TRUE; result = TRUE; } gdk_event_free ((GdkEvent *)event_gdk); } old_position = clutter_text_get_cursor_position (clutter_text); if (!result && CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->captured_event) result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->captured_event (actor, event); if (type == CLUTTER_KEY_PRESS && clutter_text_get_cursor_position (clutter_text) != old_position) reset_im_context (self); return result; }
gboolean gimp_text_tool_editor_key_release (GimpTextTool *text_tool, GdkEventKey *kevent) { if (gtk_im_context_filter_keypress (text_tool->im_context, kevent)) { text_tool->needs_im_reset = TRUE; return TRUE; } gimp_text_tool_ensure_proxy (text_tool); if (gtk_bindings_activate_event (GTK_OBJECT (text_tool->proxy_text_view), kevent)) { GIMP_LOG (TEXT_EDITING, "binding handled event"); return TRUE; } return FALSE; }
/* Class functions */ gboolean scim_bridge_client_imcontext_filter_key_event (GtkIMContext *context, GdkEventKey *event) { scim_bridge_pdebugln (8, "scim_bridge_client_imcontext_filter_key_event ()"); ScimBridgeClientIMContext *imcontext = SCIM_BRIDGE_CLIENT_IMCONTEXT (context); boolean ret = FALSE; if (imcontext) { if (!key_snooper_used) ret = key_snooper(0, event, 0); if (imcontext->slave) { if (!ret) { ret = gtk_im_context_filter_keypress (imcontext->slave, event); } else if (imcontext->slave_preedit) { imcontext->slave_preedit = FALSE; gtk_im_context_reset (imcontext->slave); } } } return ret; }
static gboolean fcitx_im_context_filter_keypress(GtkIMContext *context, GdkEventKey *event) { FcitxLog(LOG_LEVEL, "fcitx_im_context_filter_keypress"); FcitxIMContext *fcitxcontext = FCITX_IM_CONTEXT(context); /* check this first, since we use key snooper, most key will be handled. */ if (fcitx_client_is_valid(fcitxcontext->client) ) { /* XXX it is a workaround for some applications do not set client window. */ if (fcitxcontext->client_window == NULL && event->window != NULL) { gtk_im_context_set_client_window((GtkIMContext *)fcitxcontext, event->window); /* set_cursor_location_internal() will get origin from X server, * it blocks UI. So delay it to idle callback. */ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) _set_cursor_location_internal, g_object_ref(fcitxcontext), (GDestroyNotify) g_object_unref); } } if (G_UNLIKELY(event->state & FcitxKeyState_HandledMask)) return TRUE; if (G_UNLIKELY(event->state & FcitxKeyState_IgnoredMask)) return gtk_im_context_filter_keypress(fcitxcontext->slave, event); if (fcitx_client_is_valid(fcitxcontext->client) && fcitxcontext->has_focus) { _request_surrounding_text (fcitxcontext); fcitxcontext->time = event->time; if (_use_sync_mode) { int ret = fcitx_client_process_key_sync(fcitxcontext->client, event->keyval, event->hardware_keycode, event->state, (event->type == GDK_KEY_PRESS) ? (FCITX_PRESS_KEY) : (FCITX_RELEASE_KEY), event->time); if (ret <= 0) { event->state |= FcitxKeyState_IgnoredMask; return gtk_im_context_filter_keypress(fcitxcontext->slave, event); } else { event->state |= FcitxKeyState_HandledMask; return TRUE; } } else { ProcessKeyStruct* pks = g_malloc0(sizeof(ProcessKeyStruct)); pks->context = fcitxcontext; pks->event = (GdkEventKey *) gdk_event_copy((GdkEvent *) event); fcitx_client_process_key(fcitxcontext->client, _fcitx_im_context_process_key_cb, pks, event->keyval, event->hardware_keycode, event->state, (event->type == GDK_KEY_PRESS) ? (FCITX_PRESS_KEY) : (FCITX_RELEASE_KEY), event->time); event->state |= FcitxKeyState_HandledMask; return TRUE; } } else { return gtk_im_context_filter_keypress(fcitxcontext->slave, event); } return FALSE; }
static gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer g) { struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; struct tree *tree = tw->tree; uint32_t nskey; double value; GtkAdjustment *vscroll; GtkAdjustment *hscroll; GtkAdjustment *scroll = NULL; gdouble hpage, vpage; if (gtk_im_context_filter_keypress(tw->input_method, event)) return TRUE; nskey = gtk_gui_gdkkey_to_nskey(event); if (tree_keypress(tree, nskey) == true) return TRUE; vscroll = gtk_scrolled_window_get_vadjustment(tw->scrolled); hscroll = gtk_scrolled_window_get_hadjustment(tw->scrolled); g_object_get(vscroll, "page-size", &vpage, NULL); g_object_get(hscroll, "page-size", &hpage, NULL); switch (event->keyval) { case GDK_KEY(Home): case GDK_KEY(KP_Home): scroll = vscroll; value = nsgtk_adjustment_get_lower(scroll); break; case GDK_KEY(End): case GDK_KEY(KP_End): scroll = vscroll; value = nsgtk_adjustment_get_upper(scroll) - vpage; if (value < nsgtk_adjustment_get_lower(scroll)) value = nsgtk_adjustment_get_lower(scroll); break; case GDK_KEY(Left): case GDK_KEY(KP_Left): scroll = hscroll; value = gtk_adjustment_get_value(scroll) - nsgtk_adjustment_get_step_increment(scroll); if (value < nsgtk_adjustment_get_lower(scroll)) value = nsgtk_adjustment_get_lower(scroll); break; case GDK_KEY(Up): case GDK_KEY(KP_Up): scroll = vscroll; value = gtk_adjustment_get_value(scroll) - nsgtk_adjustment_get_step_increment(scroll); if (value < nsgtk_adjustment_get_lower(scroll)) value = nsgtk_adjustment_get_lower(scroll); break; case GDK_KEY(Right): case GDK_KEY(KP_Right): scroll = hscroll; value = gtk_adjustment_get_value(scroll) + nsgtk_adjustment_get_step_increment(scroll); if (value > nsgtk_adjustment_get_upper(scroll) - hpage) value = nsgtk_adjustment_get_upper(scroll) - hpage; break; case GDK_KEY(Down): case GDK_KEY(KP_Down): scroll = vscroll; value = gtk_adjustment_get_value(scroll) + nsgtk_adjustment_get_step_increment(scroll); if (value > nsgtk_adjustment_get_upper(scroll) - vpage) value = nsgtk_adjustment_get_upper(scroll) - vpage; break; case GDK_KEY(Page_Up): case GDK_KEY(KP_Page_Up): scroll = vscroll; value = gtk_adjustment_get_value(scroll) - nsgtk_adjustment_get_page_increment(scroll); if (value < nsgtk_adjustment_get_lower(scroll)) value = nsgtk_adjustment_get_lower(scroll); break; case GDK_KEY(Page_Down): case GDK_KEY(KP_Page_Down): scroll = vscroll; value = gtk_adjustment_get_value(scroll) + nsgtk_adjustment_get_page_increment(scroll); if (value > nsgtk_adjustment_get_upper(scroll) - vpage) value = nsgtk_adjustment_get_upper(scroll) - vpage; break; default: break; } if (scroll != NULL) gtk_adjustment_set_value(scroll, value); return TRUE; }
gboolean gimp_text_tool_editor_key_press (GimpTextTool *text_tool, GdkEventKey *kevent) { GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer); GtkTextIter cursor; GtkTextIter selection; gint x_pos = -1; gboolean retval = TRUE; if (gtk_im_context_filter_keypress (text_tool->im_context, kevent)) { text_tool->needs_im_reset = TRUE; text_tool->x_pos = -1; return TRUE; } gimp_text_tool_ensure_proxy (text_tool); if (gtk_bindings_activate_event (GTK_OBJECT (text_tool->proxy_text_view), kevent)) { GIMP_LOG (TEXT_EDITING, "binding handled event"); return TRUE; } gtk_text_buffer_get_iter_at_mark (buffer, &cursor, gtk_text_buffer_get_insert (buffer)); gtk_text_buffer_get_iter_at_mark (buffer, &selection, gtk_text_buffer_get_selection_bound (buffer)); switch (kevent->keyval) { case GDK_KEY_Return: case GDK_KEY_KP_Enter: case GDK_KEY_ISO_Enter: gimp_text_tool_reset_im_context (text_tool); gimp_text_tool_enter_text (text_tool, "\n"); break; case GDK_KEY_Tab: case GDK_KEY_KP_Tab: case GDK_KEY_ISO_Left_Tab: gimp_text_tool_reset_im_context (text_tool); gimp_text_tool_enter_text (text_tool, "\t"); break; case GDK_KEY_Escape: gimp_rectangle_tool_cancel (GIMP_RECTANGLE_TOOL (text_tool)); gimp_tool_control (GIMP_TOOL (text_tool), GIMP_TOOL_ACTION_HALT, GIMP_TOOL (text_tool)->display); break; default: retval = FALSE; } text_tool->x_pos = x_pos; return retval; }
gboolean Ctrl::GtkEvent(GtkWidget *widget, GdkEvent *event, gpointer user_data) { GuiLock __; GdkEventKey *key; bool pressed = false; bool retval = true; Value value; Ctrl *p = GetTopCtrlFromId(user_data); #ifdef LOG_EVENTS String ev = "?"; Tuple2<int, const char *> *f = FindTuple(xEvent, __countof(xEvent), event->type); if(f) ev = f->b; LOG(rmsecs() << " FETCH EVENT " << ev << " ctrl: " << Name(p)); #endif switch(event->type) { case GDK_EXPOSE: case GDK_DAMAGE: if(p) { #ifdef LOG_EVENTS TimeStop tm; #endif p->fullrefresh = false; GdkEventExpose *e = (GdkEventExpose *)event; SystemDraw w(gdk_cairo_create(p->gdk()), p->gdk()); painting = true; Rect r = RectC(e->area.x, e->area.y, e->area.width, e->area.height); w.SetInvalid(e->region); w.Clip(r); p->UpdateArea(w, r); w.End(); cairo_destroy(w); if(p->top->dr) DrawDragRect(*p, *p->top->dr); painting = false; #ifdef LOG_EVENTS LOG("* " << ev << " elapsed " << tm); #endif } return true; case GDK_DELETE: break; case GDK_FOCUS_CHANGE: if(p) { if(((GdkEventFocus *)event)->in) gtk_im_context_focus_in(p->top->im_context); else gtk_im_context_focus_out(p->top->im_context); AddEvent(user_data, EVENT_NONE, value, event); } return false; case GDK_LEAVE_NOTIFY: case GDK_MOTION_NOTIFY: break; case GDK_BUTTON_PRESS: value = DoButtonEvent(event, true); if(IsNull(value)) return false; break; case GDK_2BUTTON_PRESS: value = DoButtonEvent(event, true); if(IsNull(value)) return false; break; case GDK_BUTTON_RELEASE: value = DoButtonEvent(event, false); if(IsNull(value)) return false; break; case GDK_SCROLL: { GdkEventScroll *e = (GdkEventScroll *)event; value = findarg(e->direction, GDK_SCROLL_UP, GDK_SCROLL_LEFT) < 0 ? -120 : 120; break; } case GDK_KEY_PRESS: pressed = true; case GDK_KEY_RELEASE: key = (GdkEventKey *)event; value = (int) key->keyval; if(pressed) { p = GetTopCtrlFromId(user_data); if(p && gtk_im_context_filter_keypress(p->top->im_context, key)) return true; } break; case GDK_CONFIGURE: { retval = false; GdkEventConfigure *e = (GdkEventConfigure *)event; value = RectC(e->x, e->y, e->width, e->height); LLOG("GDK_CONFIGURE " << value); break; } default: return false; } AddEvent(user_data, event->type, value, event); return retval; }
static VALUE rg_filter_keypress(VALUE self, VALUE event) { return CBOOL2RVAL(gtk_im_context_filter_keypress(_SELF(self), (GdkEventKey*)RVAL2GEV(event))); }
bool GtkInputMethodFilter::filterKeyEvent(GdkEventKey* event) { if (!canEdit() || !m_enabled) return sendSimpleKeyEvent(event); m_preeditChanged = false; m_filteringKeyEvent = true; unsigned int lastFilteredKeyPressCodeWithNoResults = m_lastFilteredKeyPressCodeWithNoResults; m_lastFilteredKeyPressCodeWithNoResults = GDK_KEY_VoidSymbol; bool filtered = gtk_im_context_filter_keypress(m_context.get(), event); m_filteringKeyEvent = false; bool justSentFakeKeyUp = m_justSentFakeKeyUp; m_justSentFakeKeyUp = false; if (justSentFakeKeyUp && event->type == GDK_KEY_RELEASE) return true; // Simple input methods work such that even normal keystrokes fire the // commit signal. We detect those situations and treat them as normal // key events, supplying the commit string as the key character. if (filtered && !m_composingTextCurrently && !m_preeditChanged && m_confirmedComposition.length() == 1) { bool result = sendSimpleKeyEvent(event, m_confirmedComposition); m_confirmedComposition = String(); return result; } if (filtered && event->type == GDK_KEY_PRESS) { if (!m_preeditChanged && m_confirmedComposition.isNull()) { m_composingTextCurrently = true; m_lastFilteredKeyPressCodeWithNoResults = event->keyval; return true; } bool result = sendKeyEventWithCompositionResults(event); if (!m_confirmedComposition.isEmpty()) { m_composingTextCurrently = false; m_confirmedComposition = String(); } return result; } // If we previously filtered a key press event and it yielded no results. Suppress // the corresponding key release event to avoid confusing the web content. if (event->type == GDK_KEY_RELEASE && lastFilteredKeyPressCodeWithNoResults == event->keyval) return true; // At this point a keystroke was either: // 1. Unfiltered // 2. A filtered keyup event. As the IME code in EditorClient.h doesn't // ever look at keyup events, we send any composition results before // the key event. // Both might have composition results or not. // // It's important to send the composition results before the event // because some IM modules operate that way. For example (taken from // the Chromium source), the latin-post input method gives this sequence // when you press 'a' and then backspace: // 1. keydown 'a' (filtered) // 2. preedit changed to "a" // 3. keyup 'a' (unfiltered) // 4. keydown Backspace (unfiltered) // 5. commit "a" // 6. preedit end if (!m_confirmedComposition.isEmpty()) confirmComposition(); if (m_preeditChanged) updatePreedit(); return sendSimpleKeyEvent(event); }