X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktextview.c;h=2de8a1e623be9f15d7b3bf23ead0c54b9a9580cb;hb=5e2c23214564f7dcc687fa8467020eeb6b9407a9;hp=0c122582c061ed5f3a688687119f7168fae8273c;hpb=2ef5844bac935e13c3c9cbb27037c6a0aa43c284;p=~andy%2Fgtk diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 0c122582c..2de8a1e62 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -52,8 +50,13 @@ #include "gtkwindow.h" #include "gtkscrollable.h" #include "gtktypebuiltins.h" +#include "gtktexthandleprivate.h" +#include "gtkstylecontextprivate.h" +#include "gtkcssstylepropertyprivate.h" +#include "gtkbubblewindowprivate.h" +#include "gtktoolbar.h" -#include "a11y/gtktextviewaccessible.h" +#include "a11y/gtktextviewaccessibleprivate.h" /** * SECTION:gtktextview @@ -134,6 +137,9 @@ struct _GtkTextViewPrivate GdkDevice *dnd_device; gulong selection_drag_handler; + GtkTextHandle *text_handle; + GtkWidget *selection_bubble; + guint selection_bubble_timeout_id; GtkTextWindow *text_window; GtkTextWindow *left_window; @@ -231,6 +237,9 @@ struct _GtkTextViewPrivate * driving the scrollable adjustment values */ guint hscroll_policy : 1; guint vscroll_policy : 1; + guint cursor_handle_dragged : 1; + guint selection_handle_dragged : 1; + guint populate_all : 1; }; struct _GtkTextPendingScroll @@ -283,7 +292,10 @@ enum PROP_HADJUSTMENT, PROP_VADJUSTMENT, PROP_HSCROLL_POLICY, - PROP_VSCROLL_POLICY + PROP_VSCROLL_POLICY, + PROP_INPUT_PURPOSE, + PROP_INPUT_HINTS, + PROP_POPULATE_ALL }; static void gtk_text_view_finalize (GObject *object); @@ -340,7 +352,7 @@ static gboolean gtk_text_view_focus (GtkWidget *widget, GtkDirectionType direction); static void gtk_text_view_select_all (GtkWidget *widget, gboolean select); - +static gboolean get_middle_click_paste (GtkTextView *text_view); /* Source side drag signals */ static void gtk_text_view_drag_begin (GtkWidget *widget, @@ -454,6 +466,8 @@ static void gtk_text_view_target_list_notify (GtkTextBuffer *buffer, static void gtk_text_view_paste_done_handler (GtkTextBuffer *buffer, GtkClipboard *clipboard, gpointer data); +static void gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer, + gpointer data); static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view, GtkTextIter *cursor, gint *x, @@ -497,6 +511,22 @@ static void gtk_text_view_forall (GtkContainer *container, GtkCallback callback, gpointer callback_data); +/* GtkTextHandle handlers */ +static void gtk_text_view_handle_dragged (GtkTextHandle *handle, + GtkTextHandlePosition pos, + gint x, + gint y, + GtkTextView *text_view); +static void gtk_text_view_handle_drag_finished (GtkTextHandle *handle, + GtkTextHandlePosition pos, + GtkTextView *text_view); +static void gtk_text_view_update_handles (GtkTextView *text_view, + GtkTextHandleMode mode); + +static void gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view); +static void gtk_text_view_selection_bubble_popup_set (GtkTextView *text_view); + + /* FIXME probably need the focus methods. */ typedef struct _GtkTextViewChild GtkTextViewChild; @@ -559,6 +589,7 @@ static gint text_window_get_height (GtkTextWindow *win); static guint signals[LAST_SIGNAL] = { 0 }; +static gboolean test_touchscreen = FALSE; G_DEFINE_TYPE_WITH_CODE (GtkTextView, gtk_text_view, GTK_TYPE_CONTAINER, G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)) @@ -779,7 +810,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) /** * GtkTextView:im-module: * - * Which IM (input method) module should be used for this entry. + * Which IM (input method) module should be used for this text_view. * See #GtkIMContext. * * Setting this to a non-%NULL value overrides the @@ -796,6 +827,58 @@ gtk_text_view_class_init (GtkTextViewClass *klass) NULL, GTK_PARAM_READWRITE)); + /** + * GtkTextView:input-purpose: + * + * The purpose of this text field. + * + * This property can be used by on-screen keyboards and other input + * methods to adjust their behaviour. + * + * Since: 3.6 + */ + g_object_class_install_property (gobject_class, + PROP_INPUT_PURPOSE, + g_param_spec_enum ("input-purpose", + P_("Purpose"), + P_("Purpose of the text field"), + GTK_TYPE_INPUT_PURPOSE, + GTK_INPUT_PURPOSE_FREE_FORM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GtkTextView:input-hints: + * + * Additional hints (beyond #GtkTextView:input-purpose) that + * allow input methods to fine-tune their behaviour. + * + * Since: 3.6 + */ + g_object_class_install_property (gobject_class, + PROP_INPUT_HINTS, + g_param_spec_flags ("input-hints", + P_("hints"), + P_("Hints for the text field behaviour"), + GTK_TYPE_INPUT_HINTS, + GTK_INPUT_HINT_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** GtkTextView:populate-all: + * + * If ::populate-all is %TRUE, the #GtkTextView::populate-popup + * signal is also emitted for touch popups. + * + * Since: 3.8 + */ + g_object_class_install_property (gobject_class, + PROP_POPULATE_ALL, + g_param_spec_boolean ("populate-all", + P_("Populate all"), + P_("Whether to emit ::populate-popup for touch popups"), + FALSE, + GTK_PARAM_READWRITE)); + + /* GtkScrollable interface */ g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); @@ -1057,14 +1140,23 @@ gtk_text_view_class_init (GtkTextViewClass *klass) /** * GtkTextView::populate-popup: - * @entry: The text view on which the signal is emitted - * @menu: the menu that is being populated + * @text_view: The text view on which the signal is emitted + * @popup: the container that is being populated * - * The ::populate-popup signal gets emitted before showing the + * The ::populate-popup signal gets emitted before showing the * context menu of the text view. * * If you need to add items to the context menu, connect - * to this signal and append your menuitems to the @menu. + * to this signal and append your items to the @popup, which + * will be a #GtkMenu in this case. + * + * If #GtkEntry::populate-toolbar is %TRUE, this signal will + * also be emitted to populate touch popups. In this case, + * @popup will be a different container, e.g. a #GtkToolbar. + * + * The signal handler should not make assumptions about the + * type of @widget, but check whether @popup is a #GtkMenu + * or #GtkToolbar or another kind of container. */ signals[POPULATE_POPUP] = g_signal_new (I_("populate-popup"), @@ -1074,7 +1166,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) NULL, NULL, _gtk_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - GTK_TYPE_MENU); + GTK_TYPE_WIDGET); /** * GtkTextView::select-all: @@ -1354,6 +1446,7 @@ gtk_text_view_class_init (GtkTextViewClass *klass) g_type_class_add_private (gobject_class, sizeof (GtkTextViewPrivate)); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_TEXT_VIEW_ACCESSIBLE); + test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN") != NULL; } static void @@ -1420,6 +1513,12 @@ gtk_text_view_init (GtkTextView *text_view) /* We handle all our own redrawing */ gtk_widget_set_redraw_on_allocate (widget, FALSE); + + priv->text_handle = _gtk_text_handle_new (widget); + g_signal_connect (priv->text_handle, "handle-dragged", + G_CALLBACK (gtk_text_view_handle_dragged), text_view); + g_signal_connect (priv->text_handle, "drag-finished", + G_CALLBACK (gtk_text_view_handle_drag_finished), text_view); } /** @@ -1479,6 +1578,7 @@ gtk_text_view_set_buffer (GtkTextView *text_view, GtkTextBuffer *buffer) { GtkTextViewPrivate *priv; + GtkTextBuffer *old_buffer; g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer)); @@ -1488,6 +1588,7 @@ gtk_text_view_set_buffer (GtkTextView *text_view, if (priv->buffer == buffer) return; + old_buffer = priv->buffer; if (priv->buffer != NULL) { /* Destroy all anchored children */ @@ -1520,6 +1621,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view, g_signal_handlers_disconnect_by_func (priv->buffer, gtk_text_view_paste_done_handler, text_view); + g_signal_handlers_disconnect_by_func (priv->buffer, + gtk_text_view_buffer_changed_handler, + text_view); if (gtk_widget_get_realized (GTK_WIDGET (text_view))) { @@ -1531,7 +1635,6 @@ gtk_text_view_set_buffer (GtkTextView *text_view, if (priv->layout) gtk_text_layout_set_buffer (priv->layout, NULL); - g_object_unref (priv->buffer); priv->dnd_mark = NULL; priv->first_para_mark = NULL; cancel_pending_scroll (text_view); @@ -1570,6 +1673,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view, g_signal_connect (priv->buffer, "paste-done", G_CALLBACK (gtk_text_view_paste_done_handler), text_view); + g_signal_connect (priv->buffer, "changed", + G_CALLBACK (gtk_text_view_buffer_changed_handler), + text_view); gtk_text_view_target_list_notify (priv->buffer, NULL, text_view); @@ -1579,8 +1685,14 @@ gtk_text_view_set_buffer (GtkTextView *text_view, GDK_SELECTION_PRIMARY); gtk_text_buffer_add_selection_clipboard (priv->buffer, clipboard); } + + gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_NONE); } + _gtk_text_view_accessible_set_buffer (text_view, old_buffer); + if (old_buffer) + g_object_unref (old_buffer); + g_object_notify (G_OBJECT (text_view), "buffer"); if (gtk_widget_get_visible (GTK_WIDGET (text_view))) @@ -3065,6 +3177,10 @@ gtk_text_view_finalize (GObject *object) if (priv->bottom_window) text_window_free (priv->bottom_window); + if (priv->selection_bubble) + gtk_widget_destroy (priv->selection_bubble); + + g_object_unref (priv->text_handle); g_object_unref (priv->im_context); g_free (priv->im_module); @@ -3167,6 +3283,18 @@ gtk_text_view_set_property (GObject *object, gtk_widget_queue_resize (GTK_WIDGET (text_view)); break; + case PROP_INPUT_PURPOSE: + gtk_text_view_set_input_purpose (text_view, g_value_get_enum (value)); + break; + + case PROP_INPUT_HINTS: + gtk_text_view_set_input_hints (text_view, g_value_get_flags (value)); + break; + + case PROP_POPULATE_ALL: + text_view->priv->populate_all = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3263,6 +3391,18 @@ gtk_text_view_get_property (GObject *object, g_value_set_enum (value, priv->vscroll_policy); break; + case PROP_INPUT_PURPOSE: + g_value_set_enum (value, gtk_text_view_get_input_purpose (text_view)); + break; + + case PROP_INPUT_HINTS: + g_value_set_flags (value, gtk_text_view_get_input_hints (text_view)); + break; + + case PROP_POPULATE_ALL: + g_value_set_boolean (value, priv->populate_all); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3979,12 +4119,10 @@ gtk_text_view_realize (GtkWidget *widget) GtkTextView *text_view; GtkTextViewPrivate *priv; GtkStyleContext *context; - GtkStateFlags state; GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; GSList *tmp_list; - GdkRGBA color; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -4007,13 +4145,14 @@ gtk_text_view_realize (GtkWidget *widget) window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); - gdk_window_set_user_data (window, widget); + gtk_widget_register_window (widget, window); context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - gtk_style_context_get_background_color (context, state, &color); - gdk_window_set_background_rgba (window, &color); + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); + gtk_style_context_set_background (context, window); + gtk_style_context_restore (context); text_window_realize (priv->text_window, widget); @@ -4051,6 +4190,8 @@ gtk_text_view_realize (GtkWidget *widget) /* Ensure updating the spot location. */ gtk_text_view_update_im_spot_location (text_view); + + _gtk_text_handle_set_relative_to (priv->text_handle, priv->text_window->window); } static void @@ -4091,6 +4232,8 @@ gtk_text_view_unrealize (GtkWidget *widget) if (priv->bottom_window) text_window_unrealize (priv->bottom_window); + _gtk_text_handle_set_relative_to (priv->text_handle, NULL); + GTK_WIDGET_CLASS (gtk_text_view_parent_class)->unrealize (widget); } @@ -4113,16 +4256,14 @@ gtk_text_view_set_background (GtkTextView *text_view) gtk_style_context_save (context); gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); - gtk_style_context_get_background_color (context, state, &color); - gdk_window_set_background_rgba (priv->text_window->bin_window, &color); + gtk_style_context_set_background (context, priv->text_window->bin_window); + gtk_style_context_set_background (context, gtk_widget_get_window (widget)); gtk_style_context_restore (context); /* Set lateral panes background */ gtk_style_context_get_background_color (context, state, &color); - gdk_window_set_background_rgba (gtk_widget_get_window (widget), &color); - if (priv->left_window) gdk_window_set_background_rgba (priv->left_window->bin_window, &color); @@ -4142,6 +4283,8 @@ gtk_text_view_style_updated (GtkWidget *widget) GtkTextView *text_view; GtkTextViewPrivate *priv; PangoContext *ltr_context, *rtl_context; + GtkStyleContext *style_context; + const GtkBitmask *changes; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -4153,7 +4296,11 @@ gtk_text_view_style_updated (GtkWidget *widget) gtk_text_view_set_background (text_view); } - if (priv->layout && priv->layout->default_style) + + style_context = gtk_widget_get_style_context (widget); + changes = _gtk_style_context_get_changes (style_context); + if ((changes == NULL || _gtk_css_style_property_changes_affect_font (changes)) && + priv->layout && priv->layout->default_style) { gtk_text_view_set_attributes_from_style (text_view, priv->layout->default_style); @@ -4268,8 +4415,15 @@ gtk_text_view_grab_notify (GtkWidget *widget, if (priv->grab_device && gtk_widget_device_is_shadowed (widget, priv->grab_device)) { + if (priv->drag_start_x >= 0) + { + priv->drag_start_x = -1; + priv->drag_start_y = -1; + } + gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget)); gtk_text_view_unobscure_mouse_cursor (GTK_TEXT_VIEW (widget)); + priv->grab_device = NULL; } } @@ -4353,6 +4507,183 @@ emit_event_on_tags (GtkWidget *widget, return retval; } +static void +gtk_text_view_set_handle_position (GtkTextView *text_view, + GtkTextIter *iter, + GtkTextHandlePosition pos) +{ + GtkTextViewPrivate *priv; + GdkRectangle rect; + gint x, y; + + priv = text_view->priv; + gtk_text_view_get_cursor_locations (text_view, iter, &rect, NULL); + + x = rect.x - priv->xoffset; + y = rect.y - priv->yoffset; + + if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) && + (x < 0 || x > SCREEN_WIDTH (text_view) || + y < 0 || y > SCREEN_HEIGHT (text_view))) + { + /* Hide the handle if it's not being manipulated + * and fell outside of the visible text area. + */ + _gtk_text_handle_set_visible (priv->text_handle, pos, FALSE); + } + else + { + _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE); + + rect.x = CLAMP (x, 0, SCREEN_WIDTH (text_view)); + rect.y = CLAMP (y, 0, SCREEN_HEIGHT (text_view)); + _gtk_text_handle_set_position (priv->text_handle, pos, &rect); + } +} + +static void +gtk_text_view_handle_dragged (GtkTextHandle *handle, + GtkTextHandlePosition pos, + gint x, + gint y, + GtkTextView *text_view) +{ + GtkTextViewPrivate *priv; + GtkTextIter old_cursor, old_bound; + GtkTextIter cursor, bound, iter; + GtkTextIter *min, *max; + GtkTextHandleMode mode; + GtkTextBuffer *buffer; + GtkTextHandlePosition cursor_pos; + + priv = text_view->priv; + buffer = get_buffer (text_view); + mode = _gtk_text_handle_get_mode (handle); + + gtk_text_view_selection_bubble_popup_unset (text_view); + gtk_text_layout_get_iter_at_pixel (priv->layout, &iter, + x + priv->xoffset, + y + priv->yoffset); + gtk_text_buffer_get_iter_at_mark (buffer, &old_cursor, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_get_iter_at_mark (buffer, &old_bound, + gtk_text_buffer_get_selection_bound (buffer)); + cursor = old_cursor; + bound = old_bound; + + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR || + gtk_text_iter_compare (&cursor, &bound) >= 0) + { + cursor_pos = GTK_TEXT_HANDLE_POSITION_CURSOR; + max = &cursor; + min = &bound; + } + else + { + cursor_pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START; + max = &bound; + min = &cursor; + } + + if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END) + { + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION && + gtk_text_iter_compare (&iter, min) <= 0) + { + iter = *min; + gtk_text_iter_forward_char (&iter); + } + + *max = iter; + gtk_text_view_set_handle_position (text_view, &iter, pos); + } + else + { + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION && + gtk_text_iter_compare (&iter, max) >= 0) + { + iter = *max; + gtk_text_iter_backward_char (&iter); + } + + *min = iter; + gtk_text_view_set_handle_position (text_view, &iter, pos); + } + + if (gtk_text_iter_compare (&old_cursor, &cursor) != 0 || + gtk_text_iter_compare (&old_bound, &bound) != 0) + { + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR) + gtk_text_buffer_place_cursor (buffer, &cursor); + else + gtk_text_buffer_select_range (buffer, &cursor, &bound); + + if (_gtk_text_handle_get_is_dragged (priv->text_handle, cursor_pos)) + gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_buffer_get_insert (buffer)); + else + gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_buffer_get_selection_bound (buffer)); + } +} + +static void +gtk_text_view_handle_drag_finished (GtkTextHandle *handle, + GtkTextHandlePosition pos, + GtkTextView *text_view) +{ + gtk_text_view_selection_bubble_popup_set (text_view); +} + +static void +gtk_text_view_update_handles (GtkTextView *text_view, + GtkTextHandleMode mode) +{ + GtkTextViewPrivate *priv = text_view->priv; + GtkTextIter cursor, bound, min, max; + GtkTextBuffer *buffer; + + buffer = get_buffer (text_view); + + gtk_text_buffer_get_iter_at_mark (buffer, &cursor, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_get_iter_at_mark (buffer, &bound, + gtk_text_buffer_get_selection_bound (buffer)); + + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION && + gtk_text_iter_compare (&cursor, &bound) == 0) + { + mode = GTK_TEXT_HANDLE_MODE_CURSOR; + } + + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR && + (!gtk_widget_is_sensitive (GTK_WIDGET (text_view)) || !priv->cursor_visible)) + { + mode = GTK_TEXT_HANDLE_MODE_NONE; + } + + _gtk_text_handle_set_mode (priv->text_handle, mode); + + if (gtk_text_iter_compare (&cursor, &bound) >= 0) + { + min = bound; + max = cursor; + } + else + { + min = cursor; + max = bound; + } + + if (mode != GTK_TEXT_HANDLE_MODE_NONE) + gtk_text_view_set_handle_position (text_view, &max, + GTK_TEXT_HANDLE_POSITION_SELECTION_END); + + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION) + gtk_text_view_set_handle_position (text_view, &min, + GTK_TEXT_HANDLE_POSITION_SELECTION_START); +} + static gint gtk_text_view_event (GtkWidget *widget, GdkEvent *event) { @@ -4484,6 +4815,12 @@ gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event) gtk_text_view_reset_blink_time (text_view); gtk_text_view_pend_cursor_blink (text_view); + if (!event->send_event) + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + + gtk_text_view_selection_bubble_popup_unset (text_view); + return retval; } @@ -4518,6 +4855,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) { GtkTextView *text_view; GtkTextViewPrivate *priv; + GdkDevice *device; + gboolean is_touchscreen; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -4532,15 +4871,20 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) } gtk_text_view_reset_blink_time (text_view); + gtk_text_view_selection_bubble_popup_unset (text_view); #if 0 /* debug hack */ - if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0) + if (event->button == GDK_BUTTON_SECONDARY && (event->state & GDK_CONTROL_MASK) != 0) _gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer); - else if (event->button == 3) + else if (event->button == GDK_BUTTON_SECONDARY) gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout); #endif + device = gdk_event_get_source_device ((GdkEvent *) event); + is_touchscreen = test_touchscreen || + gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN; + if (event->type == GDK_BUTTON_PRESS) { gtk_text_view_reset_im_context (text_view); @@ -4550,7 +4894,7 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) gtk_text_view_do_popup (text_view, event); return TRUE; } - else if (event->button == 1) + else if (event->button == GDK_BUTTON_PRIMARY) { /* If we're in the selection, start a drag copy/move of the * selection; otherwise, start creating a new selection. @@ -4570,18 +4914,29 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION))) { + priv->grab_device = event->device; priv->drag_start_x = event->x; priv->drag_start_y = event->y; priv->pending_place_cursor_button = event->button; } else { + GtkTextHandleMode mode; + gtk_text_view_start_selection_drag (text_view, &iter, event); + + if (gtk_widget_is_sensitive (widget) && is_touchscreen) + mode = GTK_TEXT_HANDLE_MODE_CURSOR; + else + mode = GTK_TEXT_HANDLE_MODE_NONE; + + gtk_text_view_update_handles (text_view, mode); } return TRUE; } - else if (event->button == 2) + else if (event->button == GDK_BUTTON_MIDDLE && + get_middle_click_paste (text_view)) { GtkTextIter iter; @@ -4603,9 +4958,10 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) } else if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) && - event->button == 1) + event->button == GDK_BUTTON_PRIMARY) { GtkTextIter iter; + GtkTextHandleMode mode; gtk_text_view_end_selection_drag (text_view); @@ -4613,11 +4969,18 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) &iter, event->x + priv->xoffset, event->y + priv->yoffset); - + gtk_text_view_start_selection_drag (text_view, &iter, event); + + if (gtk_widget_is_sensitive (widget) && is_touchscreen) + mode = GTK_TEXT_HANDLE_MODE_SELECTION; + else + mode = GTK_TEXT_HANDLE_MODE_NONE; + + gtk_text_view_update_handles (text_view, mode); return TRUE; } - + return FALSE; } @@ -4626,14 +4989,16 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) { GtkTextView *text_view; GtkTextViewPrivate *priv; + GdkDevice *device; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; + device = gdk_event_get_source_device ((GdkEvent *) event); if (event->window != priv->text_window->bin_window) return FALSE; - if (event->button == 1) + if (event->button == GDK_BUTTON_PRIMARY) { if (priv->drag_start_x >= 0) { @@ -4642,9 +5007,15 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) } if (gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget))) - return TRUE; + { + if (test_touchscreen || + gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN) + gtk_text_view_selection_bubble_popup_set (text_view); + return TRUE; + } else if (priv->pending_place_cursor_button == event->button) { + GtkTextHandleMode mode; GtkTextIter iter; /* Unselect everything; we clicked inside selection, but @@ -4658,9 +5029,17 @@ gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event) gtk_text_buffer_place_cursor (get_buffer (text_view), &iter); gtk_text_view_check_cursor_blink (text_view); - + + if (gtk_widget_is_sensitive (widget) && + (test_touchscreen || + gdk_device_get_source (device) == GDK_SOURCE_TOUCHSCREEN)) + mode = GTK_TEXT_HANDLE_MODE_CURSOR; + else + mode = GTK_TEXT_HANDLE_MODE_NONE; + + gtk_text_view_update_handles (text_view, mode); priv->pending_place_cursor_button = 0; - + return FALSE; } } @@ -4734,6 +5113,9 @@ gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event) g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)), keymap_direction_changed, text_view); + gtk_text_view_selection_bubble_popup_unset (text_view); + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); if (priv->editable) { @@ -4796,8 +5178,6 @@ gtk_text_view_paint (GtkWidget *widget, { GtkTextView *text_view; GtkTextViewPrivate *priv; - GList *child_exposes; - GList *tmp_list; text_view = GTK_TEXT_VIEW (widget); priv = text_view->priv; @@ -4825,33 +5205,15 @@ gtk_text_view_paint (GtkWidget *widget, area->width, area->height); #endif - child_exposes = NULL; - cairo_save (cr); cairo_translate (cr, -priv->xoffset, -priv->yoffset); gtk_text_layout_draw (priv->layout, widget, cr, - &child_exposes); + NULL); cairo_restore (cr); - - tmp_list = child_exposes; - while (tmp_list != NULL) - { - GtkWidget *child = tmp_list->data; - - gtk_container_propagate_draw (GTK_CONTAINER (text_view), - child, - cr); - - g_object_unref (child); - - tmp_list = tmp_list->next; - } - - g_list_free (child_exposes); } static gboolean @@ -4860,9 +5222,22 @@ gtk_text_view_draw (GtkWidget *widget, { GSList *tmp_list; GdkWindow *window; - + GtkStyleContext *context; + + context = gtk_widget_get_style_context (widget); + if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget))) - gtk_text_view_draw_focus (widget, cr); + { + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); + gtk_render_background (context, cr, + 0, 0, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + gtk_style_context_restore (context); + + gtk_text_view_draw_focus (widget, cr); + } window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT); @@ -4886,10 +5261,9 @@ gtk_text_view_draw (GtkWidget *widget, /* propagate_draw checks that event->window matches * child->window */ - if (!vc->anchor) - gtk_container_propagate_draw (GTK_CONTAINER (widget), - vc->widget, - cr); + gtk_container_propagate_draw (GTK_CONTAINER (widget), + vc->widget, + cr); tmp_list = tmp_list->next; } @@ -4926,24 +5300,31 @@ gtk_text_view_focus (GtkWidget *widget, { GtkContainer *container; gboolean result; - - container = GTK_CONTAINER (widget); + + container = GTK_CONTAINER (widget); if (!gtk_widget_is_focus (widget) && gtk_container_get_focus_child (container) == NULL) { - gtk_widget_grab_focus (widget); - return TRUE; + if (gtk_widget_get_can_focus (widget)) + { + gtk_widget_grab_focus (widget); + return TRUE; + } + + return FALSE; } else { + gboolean can_focus; /* * Unset CAN_FOCUS flag so that gtk_container_focus() allows * children to get the focus */ + can_focus = gtk_widget_get_can_focus (widget); gtk_widget_set_can_focus (widget, FALSE); result = GTK_WIDGET_CLASS (gtk_text_view_parent_class)->focus (widget, direction); - gtk_widget_set_can_focus (widget, TRUE); + gtk_widget_set_can_focus (widget, can_focus); return result; } @@ -5065,6 +5446,18 @@ cursor_blinks (GtkTextView *text_view) return FALSE; } +static gboolean +get_middle_click_paste (GtkTextView *text_view) +{ + GtkSettings *settings; + gboolean paste; + + settings = gtk_widget_get_settings (GTK_WIDGET (text_view)); + g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL); + + return paste; +} + static gint get_cursor_time (GtkTextView *text_view) { @@ -6002,6 +6395,7 @@ gtk_text_view_cut_clipboard (GtkTextView *text_view) DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (get_buffer (text_view))); + gtk_text_view_selection_bubble_popup_unset (text_view); } static void @@ -6047,6 +6441,17 @@ gtk_text_view_paste_done_handler (GtkTextBuffer *buffer, priv->scroll_after_paste = TRUE; } +static void +gtk_text_view_buffer_changed_handler (GtkTextBuffer *buffer, + gpointer data) +{ + GtkTextView *text_view = data; + GtkTextViewPrivate *priv = text_view->priv; + + gtk_text_view_update_handles (text_view, + _gtk_text_handle_get_mode (priv->text_handle)); +} + static void gtk_text_view_toggle_overwrite (GtkTextView *text_view) { @@ -6202,24 +6607,25 @@ get_iter_at_pointer (GtkTextView *text_view, } static void -move_mark_to_pointer_and_scroll (GtkTextView *text_view, - const gchar *mark_name, - GdkDevice *device) +move_mark_to_pointer_and_scroll (GtkTextView *text_view, + const gchar *mark_name, + GdkDevice *device, + GdkInputSource source) { GtkTextIter newplace; + GtkTextBuffer *buffer; GtkTextMark *mark; + buffer = get_buffer (text_view); get_iter_at_pointer (text_view, device, &newplace, NULL, NULL); - - mark = gtk_text_buffer_get_mark (get_buffer (text_view), mark_name); - + + mark = gtk_text_buffer_get_mark (buffer, mark_name); + /* This may invalidate the layout */ DV(g_print (G_STRLOC": move mark\n")); - - gtk_text_buffer_move_mark (get_buffer (text_view), - mark, - &newplace); - + + gtk_text_buffer_move_mark (buffer, mark, &newplace); + DV(g_print (G_STRLOC": scrolling onscreen\n")); gtk_text_view_scroll_mark_onscreen (text_view, mark); @@ -6404,16 +6810,22 @@ selection_motion_event_handler (GtkTextView *text_view, SelectionData *data) { GtkTextViewPrivate *priv; + GdkInputSource input_source; + GdkDevice *device; priv = text_view->priv; gdk_event_request_motions (event); + device = gdk_event_get_source_device ((GdkEvent *) event); + input_source = gdk_device_get_source (device); + if (priv->grab_device != event->device) return FALSE; if (data->granularity == SELECT_CHARACTERS) { - move_mark_to_pointer_and_scroll (text_view, "insert", event->device); + move_mark_to_pointer_and_scroll (text_view, "insert", + event->device, input_source); } else { @@ -6437,7 +6849,7 @@ selection_motion_event_handler (GtkTextView *text_view, else gtk_text_buffer_select_range (buffer, &end, &orig_start); - gtk_text_view_scroll_mark_onscreen (text_view, + gtk_text_view_scroll_mark_onscreen (text_view, gtk_text_buffer_get_insert (buffer)); } @@ -6452,6 +6864,9 @@ selection_motion_event_handler (GtkTextView *text_view, text_view->priv->scroll_timeout = gdk_threads_add_timeout (50, selection_scan_timeout, text_view); + if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN) + gtk_text_view_update_handles (text_view, GTK_TEXT_HANDLE_MODE_SELECTION); + return TRUE; } @@ -6605,7 +7020,7 @@ gtk_text_view_set_attributes_from_style (GtkTextView *text_view, if (values->font) pango_font_description_free (values->font); - values->font = pango_font_description_copy (gtk_style_context_get_font (context, state)); + gtk_style_context_get (context, state, "font", &values->font, NULL); gtk_style_context_restore (context); } @@ -7692,26 +8107,6 @@ gtk_text_view_value_changed (GtkAdjustment *adjustment, */ gtk_text_view_validate_onscreen (text_view); - /* process exposes */ - if (gtk_widget_get_realized (GTK_WIDGET (text_view))) - { - DV (g_print ("Processing updates (%s)\n", G_STRLOC)); - - if (priv->left_window) - gdk_window_process_updates (priv->left_window->bin_window, TRUE); - - if (priv->right_window) - gdk_window_process_updates (priv->right_window->bin_window, TRUE); - - if (priv->top_window) - gdk_window_process_updates (priv->top_window->bin_window, TRUE); - - if (priv->bottom_window) - gdk_window_process_updates (priv->bottom_window->bin_window, TRUE); - - gdk_window_process_updates (priv->text_window->bin_window, TRUE); - } - /* If this got installed, get rid of it, it's just a waste of time. */ if (priv->first_validate_idle != 0) { @@ -7723,7 +8118,10 @@ gtk_text_view_value_changed (GtkAdjustment *adjustment, * changes made by the validation are pushed through. */ gtk_text_view_update_im_spot_location (text_view); - + + gtk_text_view_update_handles (text_view, + _gtk_text_handle_get_mode (priv->text_handle)); + DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n")); } @@ -7899,7 +8297,11 @@ gtk_text_view_mark_set_handler (GtkTextBuffer *buffer, } if (need_reset) - gtk_text_view_reset_im_context (text_view); + { + gtk_text_view_reset_im_context (text_view); + gtk_text_view_update_handles (text_view, + _gtk_text_handle_get_mode (text_view->priv->text_handle)); + } } static void @@ -8144,7 +8546,7 @@ popup_position_func (GtkMenu *menu, monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y); gtk_menu_set_monitor (menu, monitor_num); - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width)); *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height)); @@ -8367,6 +8769,165 @@ gtk_text_view_popup_menu (GtkWidget *widget) return TRUE; } +static void +gtk_text_view_get_selection_rect (GtkTextView *text_view, + cairo_rectangle_int_t *rect) +{ + cairo_rectangle_int_t rect_cursor, rect_bound; + GtkTextIter cursor, bound; + GtkTextBuffer *buffer; + gint x1, y1, x2, y2; + + buffer = get_buffer (text_view); + gtk_text_buffer_get_iter_at_mark (buffer, &cursor, + gtk_text_buffer_get_insert (buffer)); + gtk_text_buffer_get_iter_at_mark (buffer, &bound, + gtk_text_buffer_get_selection_bound (buffer)); + + gtk_text_view_get_cursor_locations (text_view, &cursor, &rect_cursor, NULL); + gtk_text_view_get_cursor_locations (text_view, &bound, &rect_bound, NULL); + + x1 = MIN (rect_cursor.x, rect_bound.x); + x2 = MAX (rect_cursor.x, rect_bound.x); + y1 = MIN (rect_cursor.y, rect_bound.y); + y2 = MAX (rect_cursor.y + rect_cursor.height, rect_bound.y + rect_bound.height); + + rect->x = x1; + rect->y = y1; + rect->width = x2 - x1; + rect->height = y2 - y1; +} + +static void +activate_bubble_cb (GtkWidget *item, + GtkTextView *text_view) +{ + const gchar *signal = g_object_get_data (G_OBJECT (item), "gtk-signal"); + g_signal_emit_by_name (text_view, signal); + _gtk_bubble_window_popdown (GTK_BUBBLE_WINDOW (text_view->priv->selection_bubble)); +} + +static void +append_bubble_action (GtkTextView *text_view, + GtkWidget *toolbar, + const gchar *stock_id, + const gchar *signal, + gboolean sensitive) +{ + GtkToolItem *item = gtk_tool_button_new_from_stock (stock_id); + g_object_set_data (G_OBJECT (item), I_("gtk-signal"), (char *)signal); + g_signal_connect (item, "clicked", G_CALLBACK (activate_bubble_cb), text_view); + gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive); + gtk_widget_show (GTK_WIDGET (item)); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1); +} + +static void +bubble_targets_received (GtkClipboard *clipboard, + GtkSelectionData *data, + gpointer user_data) +{ + GtkTextView *text_view = user_data; + GtkTextViewPrivate *priv = text_view->priv; + cairo_rectangle_int_t rect; + gboolean has_selection; + gboolean has_clipboard; + gboolean can_insert; + GtkTextIter iter; + GtkTextIter sel_start, sel_end; + GdkWindow *window; + GtkWidget *toolbar; + + has_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view), + &sel_start, &sel_end); + if (!priv->editable && !has_selection) + { + priv->selection_bubble_timeout_id = 0; + return; + } + + if (priv->selection_bubble) + gtk_widget_destroy (priv->selection_bubble); + + window = gtk_widget_get_window (GTK_WIDGET (text_view)); + priv->selection_bubble = _gtk_bubble_window_new (); + toolbar = GTK_WIDGET (gtk_toolbar_new ()); + gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), FALSE); + gtk_widget_show (toolbar); + gtk_container_add (GTK_CONTAINER (priv->selection_bubble), toolbar); + + gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter, + gtk_text_buffer_get_insert (get_buffer (text_view))); + can_insert = gtk_text_iter_can_insert (&iter, priv->editable); + has_clipboard = gtk_selection_data_targets_include_text (data); + + append_bubble_action (text_view, toolbar, GTK_STOCK_CUT, "cut-clipboard", + has_selection && + range_contains_editable_text (&sel_start, &sel_end, + priv->editable)); + append_bubble_action (text_view, toolbar, GTK_STOCK_COPY, "copy-clipboard", + has_selection); + append_bubble_action (text_view, toolbar, GTK_STOCK_PASTE, "paste-clipboard", + can_insert && has_clipboard); + + if (priv->populate_all) + g_signal_emit (text_view, signals[POPULATE_POPUP], 0, toolbar); + + gtk_text_view_get_selection_rect (text_view, &rect); + rect.x -= priv->xoffset; + rect.y -= priv->yoffset; + _gtk_bubble_window_popup (GTK_BUBBLE_WINDOW (priv->selection_bubble), + window, &rect, GTK_POS_TOP); + + priv->selection_bubble_timeout_id = 0; +} + +static gboolean +gtk_text_view_selection_bubble_popup_cb (gpointer user_data) +{ + GtkTextView *text_view = user_data; + gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view), + GDK_SELECTION_CLIPBOARD), + gdk_atom_intern_static_string ("TARGETS"), + bubble_targets_received, + text_view); + + return G_SOURCE_REMOVE; +} + +static void +gtk_text_view_selection_bubble_popup_unset (GtkTextView *text_view) +{ + GtkTextViewPrivate *priv; + + priv = text_view->priv; + + if (priv->selection_bubble) + _gtk_bubble_window_popdown (GTK_BUBBLE_WINDOW (priv->selection_bubble)); + + if (priv->selection_bubble_timeout_id) + { + g_source_remove (priv->selection_bubble_timeout_id); + priv->selection_bubble_timeout_id = 0; + } +} + +static void +gtk_text_view_selection_bubble_popup_set (GtkTextView *text_view) +{ + GtkTextViewPrivate *priv; + + priv = text_view->priv; + + if (priv->selection_bubble_timeout_id) + g_source_remove (priv->selection_bubble_timeout_id); + + priv->selection_bubble_timeout_id = + gdk_threads_add_timeout (1000, gtk_text_view_selection_bubble_popup_cb, + text_view); +} + /* Child GdkWindows */ @@ -8432,7 +8993,7 @@ text_window_realize (GtkTextWindow *win, &attributes, attributes_mask); gdk_window_show (win->window); - gdk_window_set_user_data (win->window, win->widget); + gtk_widget_register_window (win->widget, win->window); gdk_window_lower (win->window); attributes.x = 0; @@ -8441,6 +9002,7 @@ text_window_realize (GtkTextWindow *win, attributes.height = win->allocation.height; attributes.event_mask = (GDK_EXPOSURE_MASK | GDK_SCROLL_MASK | + GDK_SMOOTH_SCROLL_MASK | GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | @@ -8453,7 +9015,7 @@ text_window_realize (GtkTextWindow *win, attributes_mask); gdk_window_show (win->bin_window); - gdk_window_set_user_data (win->bin_window, win->widget); + gtk_widget_register_window (win->widget, win->bin_window); context = gtk_widget_get_style_context (widget); state = gtk_widget_get_state_flags (widget); @@ -8504,8 +9066,8 @@ text_window_unrealize (GtkTextWindow *win) NULL); } - gdk_window_set_user_data (win->window, NULL); - gdk_window_set_user_data (win->bin_window, NULL); + gtk_widget_unregister_window (win->widget, win->window); + gtk_widget_unregister_window (win->widget, win->bin_window); gdk_window_destroy (win->bin_window); gdk_window_destroy (win->window); win->window = NULL; @@ -8534,8 +9096,13 @@ text_window_scroll (GtkTextWindow *win, gint dx, gint dy) { + GtkTextView *view = GTK_TEXT_VIEW (win->widget); + GtkTextViewPrivate *priv = view->priv; + if (dx != 0 || dy != 0) { + if (priv->selection_bubble) + _gtk_bubble_window_popdown (GTK_BUBBLE_WINDOW (priv->selection_bubble)); gdk_window_scroll (win->bin_window, dx, dy); } } @@ -9650,3 +10217,106 @@ gtk_text_view_move_visually (GtkTextView *text_view, return gtk_text_layout_move_iter_visually (text_view->priv->layout, iter, count); } + +/** + * gtk_text_view_set_input_purpose: + * @text_view: a #GtkTextView + * @purpose: the purpose + * + * Sets the #GtkTextView:input-purpose property which + * can be used by on-screen keyboards and other input + * methods to adjust their behaviour. + * + * Since: 3.6 + */ + +void +gtk_text_view_set_input_purpose (GtkTextView *text_view, + GtkInputPurpose purpose) + +{ + g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + + if (gtk_text_view_get_input_purpose (text_view) != purpose) + { + g_object_set (G_OBJECT (text_view->priv->im_context), + "input-purpose", purpose, + NULL); + + g_object_notify (G_OBJECT (text_view), "input-purpose"); + } +} + +/** + * gtk_text_view_get_input_purpose: + * @text_view: a #GtkTextView + * + * Gets the value of the #GtkTextView:input-purpose property. + * + * Since: 3.6 + */ + +GtkInputPurpose +gtk_text_view_get_input_purpose (GtkTextView *text_view) +{ + GtkInputPurpose purpose; + + g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_PURPOSE_FREE_FORM); + + g_object_get (G_OBJECT (text_view->priv->im_context), + "input-purpose", &purpose, + NULL); + + return purpose; +} + +/** + * gtk_text_view_set_input_hints: + * @text_view: a #GtkTextView + * @hints: the hints + * + * Sets the #GtkTextView:input-hints property, which + * allows input methods to fine-tune their behaviour. + * + * Since: 3.6 + */ + +void +gtk_text_view_set_input_hints (GtkTextView *text_view, + GtkInputHints hints) + +{ + g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); + + if (gtk_text_view_get_input_hints (text_view) != hints) + { + g_object_set (G_OBJECT (text_view->priv->im_context), + "input-hints", hints, + NULL); + + g_object_notify (G_OBJECT (text_view), "input-hints"); + } +} + +/** + * gtk_text_view_get_input_hints: + * @text_view: a #GtkTextView + * + * Gets the value of the #GtkTextView:input-hints property. + * + * Since: 3.6 + */ + +GtkInputHints +gtk_text_view_get_input_hints (GtkTextView *text_view) +{ + GtkInputHints hints; + + g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_INPUT_HINT_NONE); + + g_object_get (G_OBJECT (text_view->priv->im_context), + "input-hints", &hints, + NULL); + + return hints; +}