X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkentry.c;h=e3553cc6ca3b085646be8c912a4b6a29dc33a80e;hb=a731cd1ef62b8e460373e660f6f77fce6d4d9ca4;hp=e42670be048c82ca6fa6ebd69ca8ef36985ef2ac;hpb=9d0febc9a64a5bfb0fcfc3a88de4757f6c1ff090;p=~andy%2Fgtk diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index e42670be0..e3553cc6c 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -65,6 +65,9 @@ #include "gtkicontheme.h" #include "gtkwidgetprivate.h" #include "gtkstylecontextprivate.h" +#include "gtktexthandleprivate.h" +#include "gtkbubblewindowprivate.h" +#include "gtktoolbar.h" #include "a11y/gtkentryaccessible.h" @@ -116,9 +119,7 @@ #define MIN_ENTRY_WIDTH 150 #define DRAW_TIMEOUT 20 -#define COMPLETION_TIMEOUT 300 #define PASSWORD_HINT_MAX 8 -#define PAGE_STEP 14 #define MAX_ICONS 2 @@ -142,15 +143,14 @@ struct _GtkEntryPrivate GtkEntryBuffer *buffer; GtkIMContext *im_context; - GtkShadowType shadow_type; GtkWidget *popup_menu; GdkDevice *device; - GdkDevice *completion_device; GdkWindow *text_area; PangoLayout *cached_layout; + PangoAttrList *attrs; gchar *im_module; @@ -160,6 +160,11 @@ struct _GtkEntryPrivate gchar *placeholder_text; + GtkBubbleWindow *bubble_window; + GtkTextHandle *text_handle; + GtkWidget *selection_bubble; + guint selection_bubble_timeout_id; + gfloat xalign; gint ascent; /* font ascent in pango units */ @@ -189,6 +194,7 @@ struct _GtkEntryPrivate guint16 preedit_length; /* length of preedit string, in bytes */ guint16 preedit_cursor; /* offset of cursor within preedit string, in chars */ + guint shadow_type : 4; guint editable : 1; guint in_drag : 1; guint overwrite_mode : 1; @@ -215,6 +221,9 @@ struct _GtkEntryPrivate guint select_words : 1; guint select_lines : 1; guint truncate_multiline : 1; + guint cursor_handle_dragged : 1; + guint selection_handle_dragged : 1; + guint populate_all : 1; }; struct _EntryIconInfo @@ -308,10 +317,15 @@ enum { PROP_IM_MODULE, PROP_EDITING_CANCELED, PROP_PLACEHOLDER_TEXT, - PROP_COMPLETION + PROP_COMPLETION, + PROP_INPUT_PURPOSE, + PROP_INPUT_HINTS, + PROP_ATTRIBUTES, + PROP_POPULATE_ALL }; static guint signals[LAST_SIGNAL] = { 0 }; +static gboolean test_touchscreen = FALSE; typedef enum { CURSOR_STANDARD, @@ -557,6 +571,11 @@ static void gtk_entry_get_text_area_size (GtkEntry *entry, gint *y, gint *width, gint *height); +static void gtk_entry_get_frame_size (GtkEntry *entry, + gint *x, + gint *y, + gint *width, + gint *height); static void get_text_area_size (GtkEntry *entry, gint *x, gint *y, @@ -572,30 +591,20 @@ static void gtk_entry_move_adjustments (GtkEntry *en static GdkPixbuf * gtk_entry_ensure_pixbuf (GtkEntry *entry, GtkEntryIconPosition icon_pos); static void gtk_entry_update_cached_style_values(GtkEntry *entry); +static gboolean get_middle_click_paste (GtkEntry *entry); + +/* GtkTextHandle handlers */ +static void gtk_entry_handle_dragged (GtkTextHandle *handle, + GtkTextHandlePosition pos, + gint x, + gint y, + GtkEntry *entry); +static void gtk_entry_handle_drag_finished (GtkTextHandle *handle, + GtkTextHandlePosition pos, + GtkEntry *entry); -/* Completion */ -static gint gtk_entry_completion_timeout (gpointer data); -static gboolean gtk_entry_completion_key_press (GtkWidget *widget, - GdkEventKey *event, - gpointer user_data); -static void gtk_entry_completion_changed (GtkWidget *entry, - gpointer user_data); -static gboolean check_completion_callback (GtkEntryCompletion *completion); -static void clear_completion_callback (GtkEntry *entry, - GParamSpec *pspec); -static gboolean accept_completion_callback (GtkEntry *entry); -static void completion_insert_text_callback (GtkEntry *entry, - const gchar *text, - gint length, - gint position, - GtkEntryCompletion *completion); -static void completion_changed (GtkEntryCompletion *completion, - GParamSpec *pspec, - gpointer data); -static void disconnect_completion_signals (GtkEntry *entry, - GtkEntryCompletion *completion); -static void connect_completion_signals (GtkEntry *entry, - GtkEntryCompletion *completion); +static void gtk_entry_selection_bubble_popup_set (GtkEntry *entry); +static void gtk_entry_selection_bubble_popup_unset (GtkEntry *entry); static void begin_change (GtkEntry *entry); static void end_change (GtkEntry *entry); @@ -714,6 +723,7 @@ gtk_entry_class_init (GtkEntryClass *class) class->toggle_overwrite = gtk_entry_toggle_overwrite; class->activate = gtk_entry_real_activate; class->get_text_area_size = gtk_entry_get_text_area_size; + class->get_frame_size = gtk_entry_get_frame_size; quark_inner_border = g_quark_from_static_string ("gtk-entry-inner-border"); quark_password_hint = g_quark_from_static_string ("gtk-entry-password-hint"); @@ -790,8 +800,9 @@ gtk_entry_class_init (GtkEntryClass *class) * * Sets the text area's border between the text and the frame. * - * Deprecated: 3.4: Use the standard border and padding CSS properties; - * the value of this style property is ignored. + * Deprecated: 3.4: Use the standard border and padding CSS properties + * (through objects like #GtkStyleContext and #GtkCssProvider); the value + * of this style property is ignored. */ g_object_class_install_property (gobject_class, PROP_INNER_BORDER, @@ -942,7 +953,7 @@ gtk_entry_class_init (GtkEntryClass *class) GTK_PARAM_READWRITE)); /** - * GtkEntry:caps-lock-warning + * GtkEntry:caps-lock-warning: * * Whether password entries will show a warning when Caps Lock is on. * @@ -1357,6 +1368,78 @@ gtk_entry_class_init (GtkEntryClass *class) GTK_TYPE_ENTRY_COMPLETION, GTK_PARAM_READWRITE)); + /** + * GtkEntry: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. + * + * Note that setting the purpose to %GTK_INPUT_PURPOSE_PASSWORD or + * %GTK_INPUT_PURPOSE_PIN is independent from setting + * #GtkEntry:visibility. + * + * 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)); + + /** + * GtkEntry:input-hints: + * + * Additional hints (beyond #GtkEntry: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)); + + /** + * GtkEntry:attributes: + * + * A list of Pango attributes to apply to the text of the entry. + * + * This is mainly useful to change the size or weight of the text. + * + * Since: 3.6 + */ + g_object_class_install_property (gobject_class, + PROP_ATTRIBUTES, + g_param_spec_boxed ("attributes", + P_("Attributes"), + P_("A list of style attributes to apply to the text of the label"), + PANGO_TYPE_ATTR_LIST, + GTK_PARAM_READWRITE)); + + /** GtkEntry:populate-all: + * + * If ::populate-all is %TRUE, the #GtkEntry::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)); + /** * GtkEntry:icon-prelight: * @@ -1379,8 +1462,9 @@ gtk_entry_class_init (GtkEntryClass *class) * * Since: 2.16 * - * Deprecated: 3.4: Use the standard margin CSS property; - * the value of this style property is ignored. + * Deprecated: 3.4: Use the standard margin CSS property (through objects + * like #GtkStyleContext and #GtkCssProvider); the value of this style + * property is ignored. */ gtk_widget_class_install_style_property (widget_class, g_param_spec_boxed ("progress-border", @@ -1414,13 +1498,20 @@ gtk_entry_class_init (GtkEntryClass *class) /** * GtkEntry::populate-popup: * @entry: The entry on which the signal is emitted - * @menu: the menu that is being populated + * @popup: the container that is being populated * - * The ::populate-popup signal gets emitted before showing the - * context menu of the entry. + * The ::populate-popup signal gets emitted before showing the + * context menu of the entry. * * 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 @widget, which + * will be a #GtkMenu in this case. + * + * If #GtkEntry::populate-all is %TRUE, this signal will + * also be emitted to populate touch popups. In this case, + * @widget will be a different container, e.g. a #GtkToolbar. + * The signal handler should not make assumptions about the + * type of @widget. */ signals[POPULATE_POPUP] = g_signal_new (I_("populate-popup"), @@ -1430,7 +1521,7 @@ gtk_entry_class_init (GtkEntryClass *class) NULL, NULL, _gtk_marshal_VOID__OBJECT, G_TYPE_NONE, 1, - GTK_TYPE_MENU); + GTK_TYPE_WIDGET); /* Action signals */ @@ -1438,12 +1529,13 @@ gtk_entry_class_init (GtkEntryClass *class) * GtkEntry::activate: * @entry: The entry on which the signal is emitted * - * A keybinding signal - * which gets emitted when the user activates the entry. - * - * Applications should not connect to it, but may emit it with - * g_signal_emit_by_name() if they need to control activation - * programmatically. + * The ::activate signal is emitted when the user hits + * the Enter key. + * + * While this signal is used as a + * keybinding signal, + * it is also commonly used by applications to intercept + * activation of entries. * * The default bindings for this signal are all forms of the Enter key. */ @@ -1875,8 +1967,9 @@ gtk_entry_class_init (GtkEntryClass *class) * * Since: 2.10 * - * Deprecated: 3.4: Use the standard border and padding CSS properties; - * the value of this style property is ignored. + * Deprecated: 3.4: Use the standard border and padding CSS properties + * (through objects like #GtkStyleContext and #GtkCssProvider); the value + * of this style property is ignored. */ gtk_widget_class_install_style_property (widget_class, g_param_spec_boxed ("inner-border", @@ -1887,6 +1980,7 @@ gtk_entry_class_init (GtkEntryClass *class) G_PARAM_DEPRECATED)); g_type_class_add_private (gobject_class, sizeof (GtkEntryPrivate)); + test_touchscreen = g_getenv ("GTK_TEST_TOUCHSCREEN") != NULL; gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE); } @@ -1957,7 +2051,7 @@ gtk_entry_set_property (GObject *object, if (!new_value) { - _gtk_entry_reset_im_context (entry); + gtk_entry_reset_im_context (entry); if (gtk_widget_has_focus (widget)) gtk_im_context_focus_out (priv->im_context); @@ -2157,6 +2251,22 @@ gtk_entry_set_property (GObject *object, gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (g_value_get_object (value))); break; + case PROP_INPUT_PURPOSE: + gtk_entry_set_input_purpose (entry, g_value_get_enum (value)); + break; + + case PROP_INPUT_HINTS: + gtk_entry_set_input_hints (entry, g_value_get_flags (value)); + break; + + case PROP_ATTRIBUTES: + gtk_entry_set_attributes (entry, g_value_get_boxed (value)); + break; + + case PROP_POPULATE_ALL: + entry->priv->populate_all = g_value_get_boolean (value); + break; + case PROP_SCROLL_OFFSET: case PROP_CURSOR_POSITION: default: @@ -2381,6 +2491,22 @@ gtk_entry_get_property (GObject *object, g_value_set_object (value, G_OBJECT (gtk_entry_get_completion (entry))); break; + case PROP_INPUT_PURPOSE: + g_value_set_enum (value, gtk_entry_get_input_purpose (entry)); + break; + + case PROP_INPUT_HINTS: + g_value_set_flags (value, gtk_entry_get_input_hints (entry)); + break; + + case PROP_ATTRIBUTES: + g_value_set_boxed (value, priv->attrs); + 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; @@ -2488,6 +2614,12 @@ gtk_entry_init (GtkEntry *entry) gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY); gtk_entry_update_cached_style_values (entry); + + priv->text_handle = _gtk_text_handle_new (GTK_WIDGET (entry)); + g_signal_connect (priv->text_handle, "handle-dragged", + G_CALLBACK (gtk_entry_handle_dragged), entry); + g_signal_connect (priv->text_handle, "drag-finished", + G_CALLBACK (gtk_entry_handle_drag_finished), entry); } static void @@ -2644,7 +2776,7 @@ gtk_entry_destroy (GtkWidget *widget) GtkEntryPrivate *priv = entry->priv; priv->current_pos = priv->selection_bound = 0; - _gtk_entry_reset_im_context (entry); + gtk_entry_reset_im_context (entry); gtk_entry_reset_layout (entry); if (priv->blink_timeout) @@ -2673,6 +2805,7 @@ gtk_entry_dispose (GObject *object) gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY, NULL); gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_SECONDARY, NULL); gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_SECONDARY, NULL); + gtk_entry_set_completion (entry, NULL); if (priv->buffer) { @@ -2712,8 +2845,6 @@ gtk_entry_finalize (GObject *object) } } - gtk_entry_set_completion (entry, NULL); - if (priv->cached_layout) g_object_unref (priv->cached_layout); @@ -2725,6 +2856,10 @@ gtk_entry_finalize (GObject *object) if (priv->recompute_idle) g_source_remove (priv->recompute_idle); + if (priv->selection_bubble) + gtk_widget_destroy (priv->selection_bubble); + + g_object_unref (priv->text_handle); g_free (priv->placeholder_text); g_free (priv->im_module); @@ -2745,10 +2880,10 @@ gtk_entry_get_display_mode (GtkEntry *entry) return DISPLAY_INVISIBLE; } -static gchar* -gtk_entry_get_display_text (GtkEntry *entry, - gint start_pos, - gint end_pos) +gchar* +_gtk_entry_get_display_text (GtkEntry *entry, + gint start_pos, + gint end_pos) { GtkEntryPasswordHint *password_hint; GtkEntryPrivate *priv; @@ -2766,7 +2901,7 @@ gtk_entry_get_display_text (GtkEntry *entry, text = gtk_entry_buffer_get_text (get_buffer (entry)); length = gtk_entry_buffer_get_length (get_buffer (entry)); - if (end_pos < 0) + if (end_pos < 0 || end_pos > length) end_pos = length; if (start_pos > length) start_pos = length; @@ -2886,7 +3021,7 @@ realize_icon_info (GtkWidget *widget, icon_info->window = gdk_window_new (gtk_widget_get_window (widget), &attributes, attributes_mask); - gdk_window_set_user_data (icon_info->window, widget); + gtk_widget_register_window (widget, icon_info->window); gtk_widget_queue_resize (widget); } @@ -2905,6 +3040,7 @@ construct_icon_info (GtkWidget *widget, priv->icons[icon_pos] = icon_info; icon_info->icon_helper = _gtk_icon_helper_new (); + _gtk_icon_helper_set_force_scale_pixbuf (icon_info->icon_helper, TRUE); if (gtk_widget_get_realized (widget)) realize_icon_info (widget, icon_pos); @@ -2945,6 +3081,9 @@ gtk_entry_unmap (GtkWidget *widget) EntryIconInfo *icon_info = NULL; gint i; + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + for (i = 0; i < MAX_ICONS; i++) { if ((icon_info = priv->icons[i]) != NULL) @@ -3009,7 +3148,7 @@ gtk_entry_realize (GtkWidget *widget) &attributes, attributes_mask); - gdk_window_set_user_data (priv->text_area, entry); + gtk_widget_register_window (widget, priv->text_area); if (attributes_mask & GDK_WA_CURSOR) g_object_unref (attributes.cursor); @@ -3018,7 +3157,7 @@ gtk_entry_realize (GtkWidget *widget) gtk_entry_adjust_scroll (entry); gtk_entry_update_primary_selection (entry); - + _gtk_text_handle_set_relative_to (priv->text_handle, priv->text_area); /* If the icon positions are already setup, create their windows. * Otherwise if they don't exist yet, then construct_icon_info() @@ -3046,6 +3185,7 @@ gtk_entry_unrealize (GtkWidget *widget) gtk_entry_reset_layout (entry); gtk_im_context_set_client_window (priv->im_context, NULL); + _gtk_text_handle_set_relative_to (priv->text_handle, NULL); clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY); if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (entry)) @@ -3053,7 +3193,7 @@ gtk_entry_unrealize (GtkWidget *widget) if (priv->text_area) { - gdk_window_set_user_data (priv->text_area, NULL); + gtk_widget_unregister_window (widget, priv->text_area); gdk_window_destroy (priv->text_area); priv->text_area = NULL; } @@ -3072,6 +3212,7 @@ gtk_entry_unrealize (GtkWidget *widget) { if (icon_info->window != NULL) { + gtk_widget_unregister_window (widget, icon_info->window); gdk_window_destroy (icon_info->window); icon_info->window = NULL; } @@ -3124,19 +3265,14 @@ gtk_entry_get_preferred_width (GtkWidget *widget, PangoFontMetrics *metrics; GtkBorder borders; PangoContext *context; - GtkStyleContext *style_context; - GtkStateFlags state; gint icon_widths = 0; gint icon_width, i; gint width; context = gtk_widget_get_pango_context (widget); - style_context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - metrics = pango_context_get_metrics (context, - gtk_style_context_get_font (style_context, state), + pango_context_get_font_description (context), pango_context_get_language (context)); _gtk_entry_get_borders (entry, &borders); @@ -3177,28 +3313,25 @@ gtk_entry_get_preferred_height (GtkWidget *widget, GtkEntryPrivate *priv = entry->priv; PangoFontMetrics *metrics; GtkBorder borders; - GtkStyleContext *style_context; - GtkStateFlags state; PangoContext *context; gint height; + PangoLayout *layout; + layout = gtk_entry_ensure_layout (entry, TRUE); context = gtk_widget_get_pango_context (widget); - style_context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - metrics = pango_context_get_metrics (context, - gtk_style_context_get_font (style_context, state), + pango_context_get_font_description (context), pango_context_get_language (context)); priv->ascent = pango_font_metrics_get_ascent (metrics); priv->descent = pango_font_metrics_get_descent (metrics); + pango_font_metrics_unref (metrics); _gtk_entry_get_borders (entry, &borders); + pango_layout_get_pixel_size (layout, NULL, &height); - height = PANGO_PIXELS (priv->ascent + priv->descent) + borders.top + borders.bottom; - - pango_font_metrics_unref (metrics); + height += borders.top + borders.bottom; *minimum = height; *natural = height; @@ -3309,12 +3442,11 @@ get_text_area_size (GtkEntry *entry, static void -get_frame_size (GtkEntry *entry, - gboolean relative_to_window, - gint *x, - gint *y, - gint *width, - gint *height) +gtk_entry_get_frame_size (GtkEntry *entry, + gint *x, + gint *y, + gint *width, + gint *height) { GtkEntryPrivate *priv = entry->priv; GtkAllocation allocation; @@ -3329,7 +3461,7 @@ get_frame_size (GtkEntry *entry, gtk_widget_get_allocation (widget, &allocation); if (x) - *x = relative_to_window ? allocation.x : 0; + *x = allocation.x; if (y) { @@ -3338,8 +3470,7 @@ get_frame_size (GtkEntry *entry, else *y = (allocation.height - req_height) / 2; - if (relative_to_window) - *y += allocation.y; + *y += allocation.y; } if (width) @@ -3354,6 +3485,36 @@ get_frame_size (GtkEntry *entry, } } +static void +get_frame_size (GtkEntry *entry, + gboolean relative_to_window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GtkEntryClass *class; + GtkWidget *widget = GTK_WIDGET (entry); + + g_return_if_fail (GTK_IS_ENTRY (entry)); + + class = GTK_ENTRY_GET_CLASS (entry); + + if (class->get_frame_size) + class->get_frame_size (entry, x, y, width, height); + + if (!relative_to_window) + { + GtkAllocation allocation; + gtk_widget_get_allocation (widget, &allocation); + + if (x) + *x -= allocation.x; + if (y) + *y -= allocation.y; + } +} + static void gtk_entry_size_allocate (GtkWidget *widget, GtkAllocation *allocation) @@ -3370,7 +3531,7 @@ gtk_entry_size_allocate (GtkWidget *widget, gtk_entry_recompute (entry); completion = gtk_entry_get_completion (entry); - if (completion && gtk_widget_get_mapped (completion->priv->popup_window)) + if (completion) _gtk_entry_completion_resize_popup (completion); } } @@ -3631,36 +3792,40 @@ gtk_entry_draw (GtkWidget *widget, GtkEntryPrivate *priv = entry->priv; int i; - context = gtk_widget_get_style_context (widget); + if (gtk_cairo_should_draw_window (cr, + gtk_widget_get_window (widget))) + { + context = gtk_widget_get_style_context (widget); - /* Draw entry_bg, shadow, progress and focus */ - gtk_entry_draw_frame (widget, context, cr); + /* Draw entry_bg, shadow, progress and focus */ + gtk_entry_draw_frame (widget, context, cr); - /* Draw text and cursor */ - cairo_save (cr); + /* Draw text and cursor */ + cairo_save (cr); - gtk_cairo_transform_to_window (cr, widget, priv->text_area); + gtk_cairo_transform_to_window (cr, widget, priv->text_area); - if (priv->dnd_position != -1) - gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND); - - gtk_entry_draw_text (GTK_ENTRY (widget), cr); + if (priv->dnd_position != -1) + gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND); - /* When no text is being displayed at all, don't show the cursor */ - if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK && - gtk_widget_has_focus (widget) && - priv->selection_bound == priv->current_pos && priv->cursor_visible) - gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD); + gtk_entry_draw_text (GTK_ENTRY (widget), cr); - cairo_restore (cr); + /* When no text is being displayed at all, don't show the cursor */ + if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK && + gtk_widget_has_focus (widget) && + priv->selection_bound == priv->current_pos && priv->cursor_visible) + gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD); - /* Draw icons */ - for (i = 0; i < MAX_ICONS; i++) - { - EntryIconInfo *icon_info = priv->icons[i]; + cairo_restore (cr); - if (icon_info != NULL) - draw_icon (widget, cr, i); + /* Draw icons */ + for (i = 0; i < MAX_ICONS; i++) + { + EntryIconInfo *icon_info = priv->icons[i]; + + if (icon_info != NULL) + draw_icon (widget, cr, i); + } } return FALSE; @@ -3787,7 +3952,108 @@ in_selection (GtkEntry *entry, g_free (ranges); return retval; } - + +static void +gtk_entry_move_handle (GtkEntry *entry, + GtkTextHandlePosition pos, + gint x, + gint y, + gint height) +{ + GtkEntryPrivate *priv = entry->priv; + + if (!_gtk_text_handle_get_is_dragged (priv->text_handle, pos) && + (x < 0 || x > gdk_window_get_width (priv->text_area))) + { + /* 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 + { + GdkRectangle rect; + + rect.x = CLAMP (x, 0, gdk_window_get_width (priv->text_area)); + rect.y = y; + rect.width = 1; + rect.height = height; + + _gtk_text_handle_set_visible (priv->text_handle, pos, TRUE); + _gtk_text_handle_set_position (priv->text_handle, pos, &rect); + } +} + +static gint +gtk_entry_get_selection_bound_location (GtkEntry *entry) +{ + GtkEntryPrivate *priv = entry->priv; + PangoLayout *layout; + PangoRectangle pos; + gint x; + const gchar *text; + gint index; + + layout = gtk_entry_ensure_layout (entry, FALSE); + text = pango_layout_get_text (layout); + index = g_utf8_offset_to_pointer (text, priv->selection_bound) - text; + pango_layout_index_to_pos (layout, index, &pos); + + if (gtk_widget_get_direction (GTK_WIDGET (entry)) == GTK_TEXT_DIR_RTL) + x = (pos.x + pos.width) / PANGO_SCALE; + else + x = pos.x / PANGO_SCALE; + + return x; +} + +static void +gtk_entry_update_handles (GtkEntry *entry, + GtkTextHandleMode mode) +{ + GtkEntryPrivate *priv = entry->priv; + gint strong_x, height; + gint cursor, bound; + + _gtk_text_handle_set_mode (priv->text_handle, mode); + + /* Wait for recomputation before repositioning */ + if (priv->recompute_idle != 0) + return; + + height = gdk_window_get_height (priv->text_area); + + gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL); + cursor = strong_x - priv->scroll_offset; + + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION) + { + gint start, end; + + bound = gtk_entry_get_selection_bound_location (entry) - priv->scroll_offset; + + if (priv->selection_bound > priv->current_pos) + { + start = cursor; + end = bound; + } + else + { + start = bound; + end = cursor; + } + + /* Update start selection bound */ + gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_START, + start, 0, height); + gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_SELECTION_END, + end, 0, height); + } + else + gtk_entry_move_handle (entry, GTK_TEXT_HANDLE_POSITION_CURSOR, + cursor, 0, height); +} + static gint gtk_entry_button_press (GtkWidget *widget, GdkEventButton *event) @@ -3800,6 +4066,8 @@ gtk_entry_button_press (GtkWidget *widget, gint sel_start, sel_end; gint i; + gtk_entry_selection_bubble_popup_unset (entry); + for (i = 0; i < MAX_ICONS; i++) { icon_info = priv->icons[i]; @@ -3855,6 +4123,12 @@ gtk_entry_button_press (GtkWidget *widget, else if (event->button == GDK_BUTTON_PRIMARY) { gboolean have_selection = gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end); + gboolean is_touchscreen; + GdkDevice *source; + + source = gdk_event_get_source_device ((GdkEvent *) event); + is_touchscreen = test_touchscreen || + gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN; priv->select_words = FALSE; priv->select_lines = FALSE; @@ -3863,11 +4137,11 @@ gtk_entry_button_press (GtkWidget *widget, gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION)) { - _gtk_entry_reset_im_context (entry); + gtk_entry_reset_im_context (entry); if (!have_selection) /* select from the current position to the clicked position */ sel_start = sel_end = priv->current_pos; - + if (tmp_pos > sel_start && tmp_pos < sel_end) { /* Truncate current selection, but keep it as big as possible */ @@ -3933,7 +4207,12 @@ gtk_entry_button_press (GtkWidget *widget, priv->drag_start_y = event->y; } else - gtk_editable_set_position (editable, tmp_pos); + { + gtk_editable_set_position (editable, tmp_pos); + + if (is_touchscreen) + gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR); + } break; case GDK_2BUTTON_PRESS: @@ -3944,6 +4223,9 @@ gtk_entry_button_press (GtkWidget *widget, priv->in_drag = FALSE; priv->select_words = TRUE; gtk_entry_select_word (entry); + + if (is_touchscreen) + gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION); break; case GDK_3BUTTON_PRESS: @@ -3954,6 +4236,8 @@ gtk_entry_button_press (GtkWidget *widget, priv->in_drag = FALSE; priv->select_lines = TRUE; gtk_entry_select_line (entry); + if (is_touchscreen) + gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_SELECTION); break; default: @@ -3962,7 +4246,9 @@ gtk_entry_button_press (GtkWidget *widget, return TRUE; } - else if (event->button == GDK_BUTTON_MIDDLE && event->type == GDK_BUTTON_PRESS) + else if (event->type == GDK_BUTTON_PRESS && + event->button == GDK_BUTTON_MIDDLE && + get_middle_click_paste (entry)) { if (priv->editable) { @@ -3986,6 +4272,8 @@ gtk_entry_button_release (GtkWidget *widget, GtkEntry *entry = GTK_ENTRY (widget); GtkEntryPrivate *priv = entry->priv; EntryIconInfo *icon_info = NULL; + gboolean is_touchscreen; + GdkDevice *source; gint i; for (i = 0; i < MAX_ICONS; i++) @@ -4018,14 +4306,23 @@ gtk_entry_button_release (GtkWidget *widget, if (event->window != priv->text_area || priv->button != event->button) return FALSE; + source = gdk_event_get_source_device ((GdkEvent *) event); + is_touchscreen = (test_touchscreen || + gdk_device_get_source (source) == GDK_SOURCE_TOUCHSCREEN); + if (priv->in_drag) { gint tmp_pos = gtk_entry_find_position (entry, priv->drag_start_x); gtk_editable_set_position (GTK_EDITABLE (entry), tmp_pos); + if (is_touchscreen) + gtk_entry_update_handles (entry, GTK_TEXT_HANDLE_MODE_CURSOR); + priv->in_drag = 0; } + else if (is_touchscreen) + gtk_entry_selection_bubble_popup_set (entry); priv->button = 0; priv->device = NULL; @@ -4145,13 +4442,22 @@ gtk_entry_motion_notify (GtkWidget *widget, } else { + GdkInputSource input_source; + GdkDevice *source; + guint length; + + length = gtk_entry_buffer_get_length (get_buffer (entry)); + if (event->y < 0) tmp_pos = 0; else if (event->y >= gdk_window_get_height (priv->text_area)) - tmp_pos = gtk_entry_buffer_get_length (get_buffer (entry)); + tmp_pos = length; else tmp_pos = gtk_entry_find_position (entry, event->x + priv->scroll_offset); + source = gdk_event_get_source_device ((GdkEvent *) event); + input_source = gdk_device_get_source (source); + if (priv->select_words) { gint min, max; @@ -4187,13 +4493,20 @@ gtk_entry_motion_notify (GtkWidget *widget, if (priv->current_pos != max) pos = min; } - + gtk_entry_set_positions (entry, pos, bound); } else gtk_entry_set_positions (entry, tmp_pos, -1); + + /* Update touch handles' position */ + if (test_touchscreen || input_source == GDK_SOURCE_TOUCHSCREEN) + gtk_entry_update_handles (entry, + (priv->current_pos == priv->selection_bound) ? + GTK_TEXT_HANDLE_MODE_CURSOR : + GTK_TEXT_HANDLE_MODE_SELECTION); } - + return TRUE; } @@ -4234,6 +4547,12 @@ gtk_entry_key_press (GtkWidget *widget, gtk_entry_reset_blink_time (entry); gtk_entry_pend_cursor_blink (entry); + gtk_entry_selection_bubble_popup_unset (entry); + + if (!event->send_event) + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + if (priv->editable) { if (gtk_im_context_filter_keypress (priv->im_context, event)) @@ -4244,21 +4563,11 @@ gtk_entry_key_press (GtkWidget *widget, } } - if (event->keyval == GDK_KEY_Return || - event->keyval == GDK_KEY_KP_Enter || - event->keyval == GDK_KEY_ISO_Enter || + if (event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_KP_Enter || + event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_Escape) - { - GtkEntryCompletion *completion = gtk_entry_get_completion (entry); - - if (completion && completion->priv->completion_timeout) - { - g_source_remove (completion->priv->completion_timeout); - completion->priv->completion_timeout = 0; - } - - _gtk_entry_reset_im_context (entry); - } + gtk_entry_reset_im_context (entry); if (GTK_WIDGET_CLASS (gtk_entry_parent_class)->key_press_event (widget, event)) /* Activate key bindings @@ -4337,6 +4646,10 @@ gtk_entry_focus_out (GtkWidget *widget, GtkEntryCompletion *completion; GdkKeymap *keymap; + gtk_entry_selection_bubble_popup_unset (entry); + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + gtk_widget_queue_draw (widget); keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget)); @@ -4514,7 +4827,7 @@ gtk_entry_real_set_position (GtkEditable *editable, if (position != priv->current_pos || position != priv->selection_bound) { - _gtk_entry_reset_im_context (entry); + gtk_entry_reset_im_context (entry); gtk_entry_set_positions (entry, position, position); } } @@ -4541,8 +4854,8 @@ gtk_entry_set_selection_bounds (GtkEditable *editable, start = length; if (end < 0) end = length; - - _gtk_entry_reset_im_context (entry); + + gtk_entry_reset_im_context (entry); gtk_entry_set_positions (entry, MIN (end, length), @@ -4616,8 +4929,6 @@ gtk_entry_style_updated (GtkWidget *widget) gtk_entry_update_cached_style_values (entry); - gtk_entry_recompute (entry); - icon_theme_changed (entry); } @@ -4919,7 +5230,7 @@ gtk_entry_move_cursor (GtkEntry *entry, GtkEntryPrivate *priv = entry->priv; gint new_pos = priv->current_pos; - _gtk_entry_reset_im_context (entry); + gtk_entry_reset_im_context (entry); if (priv->current_pos != priv->selection_bound && !extend_selection) { @@ -5036,8 +5347,7 @@ gtk_entry_insert_at_cursor (GtkEntry *entry, if (priv->editable) { - _gtk_entry_reset_im_context (entry); - + gtk_entry_reset_im_context (entry); gtk_editable_insert_text (editable, str, -1, &pos); gtk_editable_set_position (editable, pos); } @@ -5053,8 +5363,8 @@ gtk_entry_delete_from_cursor (GtkEntry *entry, gint start_pos = priv->current_pos; gint end_pos = priv->current_pos; gint old_n_bytes = gtk_entry_buffer_get_bytes (get_buffer (entry)); - - _gtk_entry_reset_im_context (entry); + + gtk_entry_reset_im_context (entry); if (!priv->editable) { @@ -5131,7 +5441,7 @@ gtk_entry_backspace (GtkEntry *entry) GtkEditable *editable = GTK_EDITABLE (entry); gint prev_pos; - _gtk_entry_reset_im_context (entry); + gtk_entry_reset_im_context (entry); if (!priv->editable) { @@ -5162,8 +5472,8 @@ gtk_entry_backspace (GtkEntry *entry) gchar *normalized_text; glong len; - cluster_text = gtk_entry_get_display_text (entry, prev_pos, - priv->current_pos); + cluster_text = _gtk_entry_get_display_text (entry, prev_pos, + priv->current_pos); normalized_text = g_utf8_normalize (cluster_text, strlen (cluster_text), G_NORMALIZE_NFD); @@ -5214,7 +5524,7 @@ gtk_entry_copy_clipboard (GtkEntry *entry) return; } - str = gtk_entry_get_display_text (entry, start, end); + str = _gtk_entry_get_display_text (entry, start, end); gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD), str, -1); @@ -5246,6 +5556,8 @@ gtk_entry_cut_clipboard (GtkEntry *entry) { gtk_widget_error_bell (GTK_WIDGET (entry)); } + + gtk_entry_selection_bubble_popup_unset (entry); } static void @@ -5372,7 +5684,7 @@ gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, gchar *text; /* XXXX ??? does this even make sense when text is not visible? Should we return FALSE? */ - text = gtk_entry_get_display_text (entry, 0, -1); + text = _gtk_entry_get_display_text (entry, 0, -1); gtk_im_context_set_surrounding (context, text, strlen (text), /* Length in bytes */ g_utf8_offset_to_pointer (text, priv->current_pos) - text); g_free (text); @@ -5518,10 +5830,17 @@ recompute_idle_func (gpointer data) if (gtk_widget_has_screen (GTK_WIDGET (entry))) { + GtkTextHandleMode handle_mode; + gtk_entry_adjust_scroll (entry); gtk_widget_queue_draw (GTK_WIDGET (entry)); update_im_cursor_location (entry); + + handle_mode = _gtk_text_handle_get_mode (priv->text_handle); + + if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE) + gtk_entry_update_handles (entry, handle_mode); } return FALSE; @@ -5577,9 +5896,9 @@ gtk_entry_create_layout (GtkEntry *entry, { GtkEntryPrivate *priv = entry->priv; GtkWidget *widget = GTK_WIDGET (entry); - PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL); - PangoAttrList *tmp_attrs = pango_attr_list_new (); - gboolean placeholder_layout = show_placeholder_text (entry); + PangoLayout *layout; + PangoAttrList *tmp_attrs; + gboolean placeholder_layout; gchar *preedit_string = NULL; gint preedit_length = 0; @@ -5588,9 +5907,14 @@ gtk_entry_create_layout (GtkEntry *entry, gchar *display; guint n_bytes; + layout = gtk_widget_create_pango_layout (widget, NULL); pango_layout_set_single_paragraph_mode (layout, TRUE); - display = placeholder_layout ? g_strdup (priv->placeholder_text) : gtk_entry_get_display_text (entry, 0, -1); + tmp_attrs = priv->attrs ? pango_attr_list_ref (priv->attrs) + : pango_attr_list_new (); + + placeholder_layout = show_placeholder_text (entry); + display = placeholder_layout ? g_strdup (priv->placeholder_text) : _gtk_entry_get_display_text (entry, 0, -1); n_bytes = strlen (display); if (!placeholder_layout && include_preedit) @@ -5617,12 +5941,9 @@ gtk_entry_create_layout (GtkEntry *entry, gint cursor_index = g_utf8_offset_to_pointer (display, priv->current_pos) - display; g_string_insert (tmp_string, cursor_index, preedit_string); - pango_layout_set_text (layout, tmp_string->str, tmp_string->len); - pango_attr_list_splice (tmp_attrs, preedit_attrs, cursor_index, preedit_length); - g_string_free (tmp_string, TRUE); } else @@ -5708,7 +6029,7 @@ get_layout_position (GtkEntry *entry, layout = gtk_entry_ensure_layout (entry, TRUE); - gtk_entry_get_text_area_size (entry, NULL, NULL, &area_width, &area_height); + get_text_area_size (entry, NULL, NULL, &area_width, &area_height); area_height = PANGO_SCALE * area_height; line = pango_layout_get_lines_readonly (layout)->data; @@ -5953,18 +6274,81 @@ gtk_entry_draw_cursor (GtkEntry *entry, } } -void -_gtk_entry_reset_im_context (GtkEntry *entry) +static void +gtk_entry_handle_dragged (GtkTextHandle *handle, + GtkTextHandlePosition pos, + gint x, + gint y, + GtkEntry *entry) { + gint cursor_pos, selection_bound_pos, tmp_pos; GtkEntryPrivate *priv = entry->priv; + GtkTextHandleMode mode; + gint *min, *max; - if (priv->need_im_reset) + gtk_entry_selection_bubble_popup_unset (entry); + + cursor_pos = priv->current_pos; + selection_bound_pos = priv->selection_bound; + mode = _gtk_text_handle_get_mode (handle); + tmp_pos = gtk_entry_find_position (entry, x + priv->scroll_offset); + + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR || + cursor_pos >= selection_bound_pos) { - priv->need_im_reset = FALSE; - gtk_im_context_reset (priv->im_context); + max = &cursor_pos; + min = &selection_bound_pos; + } + else + { + max = &selection_bound_pos; + min = &cursor_pos; + } + + if (pos == GTK_TEXT_HANDLE_POSITION_SELECTION_END) + { + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION) + { + gint min_pos; + + min_pos = MAX (*min + 1, 0); + tmp_pos = MAX (tmp_pos, min_pos); + } + + *max = tmp_pos; } + else + { + if (mode == GTK_TEXT_HANDLE_MODE_SELECTION) + { + gint max_pos; + + max_pos = *max - 1; + *min = MIN (tmp_pos, max_pos); + } + } + + if (cursor_pos != priv->current_pos || + selection_bound_pos != priv->selection_bound) + { + if (mode == GTK_TEXT_HANDLE_MODE_CURSOR) + gtk_entry_set_positions (entry, cursor_pos, cursor_pos); + else + gtk_entry_set_positions (entry, cursor_pos, selection_bound_pos); + + gtk_entry_update_handles (entry, mode); + } +} + +static void +gtk_entry_handle_drag_finished (GtkTextHandle *handle, + GtkTextHandlePosition pos, + GtkEntry *entry) +{ + gtk_entry_selection_bubble_popup_set (entry); } + /** * gtk_entry_reset_im_context: * @entry: a #GtkEntry @@ -5979,9 +6363,15 @@ _gtk_entry_reset_im_context (GtkEntry *entry) void gtk_entry_reset_im_context (GtkEntry *entry) { + GtkEntryPrivate *priv = entry->priv; + g_return_if_fail (GTK_IS_ENTRY (entry)); - _gtk_entry_reset_im_context (entry); + if (priv->need_im_reset) + { + priv->need_im_reset = FALSE; + gtk_im_context_reset (priv->im_context); + } } /** @@ -6115,6 +6505,23 @@ gtk_entry_get_cursor_locations (GtkEntry *entry, } } +static gboolean +gtk_entry_get_is_selection_handle_dragged (GtkEntry *entry) +{ + GtkEntryPrivate *priv = entry->priv; + GtkTextHandlePosition pos; + + if (_gtk_text_handle_get_mode (priv->text_handle) != GTK_TEXT_HANDLE_MODE_SELECTION) + return FALSE; + + if (priv->current_pos >= priv->selection_bound) + pos = GTK_TEXT_HANDLE_POSITION_SELECTION_START; + else + pos = GTK_TEXT_HANDLE_POSITION_SELECTION_END; + + return _gtk_text_handle_get_is_dragged (priv->text_handle, pos); +} + static void gtk_entry_adjust_scroll (GtkEntry *entry) { @@ -6127,6 +6534,7 @@ gtk_entry_adjust_scroll (GtkEntry *entry) PangoLayout *layout; PangoLayoutLine *line; PangoRectangle logical_rect; + GtkTextHandleMode handle_mode; if (!gtk_widget_get_realized (GTK_WIDGET (entry))) return; @@ -6163,22 +6571,33 @@ gtk_entry_adjust_scroll (GtkEntry *entry) priv->scroll_offset = CLAMP (priv->scroll_offset, min_offset, max_offset); - /* And make sure cursors are on screen. Note that the cursor is - * actually drawn one pixel into the INNER_BORDER space on - * the right, when the scroll is at the utmost right. This - * looks better to to me than confining the cursor inside the - * border entirely, though it means that the cursor gets one - * pixel closer to the edge of the widget on the right than - * on the left. This might need changing if one changed - * INNER_BORDER from 2 to 1, as one would do on a - * small-screen-real-estate display. - * - * We always make sure that the strong cursor is on screen, and - * put the weak cursor on screen if possible. - */ + if (gtk_entry_get_is_selection_handle_dragged (entry)) + { + /* The text handle corresponding to the selection bound is + * being dragged, ensure it stays onscreen even if we scroll + * cursors away, this is so both handles can cause content + * to scroll. + */ + strong_x = weak_x = gtk_entry_get_selection_bound_location (entry); + } + else + { + /* And make sure cursors are on screen. Note that the cursor is + * actually drawn one pixel into the INNER_BORDER space on + * the right, when the scroll is at the utmost right. This + * looks better to to me than confining the cursor inside the + * border entirely, though it means that the cursor gets one + * pixel closer to the edge of the widget on the right than + * on the left. This might need changing if one changed + * INNER_BORDER from 2 to 1, as one would do on a + * small-screen-real-estate display. + * + * We always make sure that the strong cursor is on screen, and + * put the weak cursor on screen if possible. + */ + gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x); + } - gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, &weak_x); - strong_xoffset = strong_x - priv->scroll_offset; if (strong_xoffset < 0) @@ -6205,6 +6624,11 @@ gtk_entry_adjust_scroll (GtkEntry *entry) } g_object_notify (G_OBJECT (entry), "scroll-offset"); + + handle_mode = _gtk_text_handle_get_mode (priv->text_handle); + + if (handle_mode != GTK_TEXT_HANDLE_MODE_NONE) + gtk_entry_update_handles (entry, handle_mode); } static void @@ -6215,8 +6639,6 @@ gtk_entry_move_adjustments (GtkEntry *entry) GtkAdjustment *adjustment; PangoContext *context; PangoFontMetrics *metrics; - GtkStyleContext *style_context; - GtkStateFlags state; GtkBorder borders; gint x, layout_x; gint char_width; @@ -6227,7 +6649,7 @@ gtk_entry_move_adjustments (GtkEntry *entry) gtk_widget_get_allocation (widget, &allocation); - /* Cursor position, layout offset, border width, and widget allocation */ + /* Cursor/char position, layout offset, border width, and widget allocation */ gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &x, NULL); get_layout_position (entry, &layout_x, NULL); _gtk_entry_get_borders (entry, &borders); @@ -6235,11 +6657,9 @@ gtk_entry_move_adjustments (GtkEntry *entry) /* Approximate width of a char, so user can see what is ahead/behind */ context = gtk_widget_get_pango_context (widget); - style_context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); metrics = pango_context_get_metrics (context, - gtk_style_context_get_font (style_context, state), + pango_context_get_font_description (context), pango_context_get_language (context)); char_width = pango_font_metrics_get_approximate_char_width (metrics) / PANGO_SCALE; @@ -6555,7 +6975,7 @@ primary_get_cb (GtkClipboard *clipboard, if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) { - gchar *str = gtk_entry_get_display_text (entry, start, end); + gchar *str = _gtk_entry_get_display_text (entry, start, end); gtk_selection_data_set_text (selection_data, str, -1); g_free (str); } @@ -6896,14 +7316,19 @@ gtk_entry_set_text (GtkEntry *entry, * @visible: %TRUE if the contents of the entry are displayed * as plaintext * - * Sets whether the contents of the entry are visible or not. - * When visibility is set to %FALSE, characters are displayed - * as the invisible char, and will also appear that way when + * Sets whether the contents of the entry are visible or not. + * When visibility is set to %FALSE, characters are displayed + * as the invisible char, and will also appear that way when * the text in the entry widget is copied elsewhere. * * By default, GTK+ picks the best invisible character available * in the current font, but it can be changed with * gtk_entry_set_invisible_char(). + * + * Note that you probably want to set #GtkEntry:input-purpose + * to %GTK_INPUT_PURPOSE_PASSWORD or %GTK_INPUT_PURPOSE_PIN to + * inform input methods about the purpose of this entry, + * in addition to setting visibility to %FALSE. */ void gtk_entry_set_visibility (GtkEntry *entry, @@ -7190,7 +7615,7 @@ gtk_entry_get_text_length (GtkEntry *entry) * * (For experts: if @setting is %TRUE, the entry calls * gtk_window_activate_default() on the window containing the entry, in - * the default handler for the #GtkWidget::activate signal.) + * the default handler for the #GtkEntry::activate signal.) **/ void gtk_entry_set_activates_default (GtkEntry *entry, @@ -7331,8 +7756,9 @@ gtk_entry_get_has_frame (GtkEntry *entry) * * Since: 2.10 * - * Deprecated: 3.4: Use the standard border and padding CSS properties; - * the value set with this function is ignored by #GtkEntry. + * Deprecated: 3.4: Use the standard border and padding CSS properties (through + * objects like #GtkStyleContext and #GtkCssProvider); the value set with + * this function is ignored by #GtkEntry. **/ void gtk_entry_set_inner_border (GtkEntry *entry, @@ -7354,8 +7780,9 @@ gtk_entry_set_inner_border (GtkEntry *entry, * * Since: 2.10 * - * Deprecated: 3.4: Use the standard border and padding CSS properties; - * the value returned by this function is ignored by #GtkEntry. + * Deprecated: 3.4: Use the standard border and padding CSS properties (through + * objects like #GtkStyleContext and #GtkCssProvider); the value returned by + * this function is ignored by #GtkEntry. **/ const GtkBorder * gtk_entry_get_inner_border (GtkEntry *entry) @@ -7611,6 +8038,8 @@ gtk_entry_set_icon_from_pixbuf (GtkEntry *entry, if (pixbuf) { _gtk_icon_helper_set_pixbuf (icon_info->icon_helper, pixbuf); + _gtk_icon_helper_set_icon_size (icon_info->icon_helper, + GTK_ICON_SIZE_MENU); if (icon_pos == GTK_ENTRY_ICON_PRIMARY) { @@ -7937,7 +8366,8 @@ gtk_entry_get_icon_pixbuf (GtkEntry *entry, * the icon helper's cache ref directly. */ pixbuf = gtk_entry_ensure_pixbuf (entry, icon_pos); - g_object_unref (pixbuf); + if (pixbuf) + g_object_unref (pixbuf); return pixbuf; } @@ -8193,7 +8623,7 @@ gtk_entry_get_icon_at_pos (GtkEntry *entry, /** * gtk_entry_set_icon_drag_source: - * @entry: a #GtkIconEntry + * @entry: a #GtkEntry * @icon_pos: icon position * @target_list: the targets (data formats) in which the data can be provided * @actions: a bitmask of the allowed drag actions @@ -8438,6 +8868,9 @@ gtk_entry_set_icon_tooltip_text (GtkEntry *entry, icon_info->tooltip = tooltip ? g_markup_escape_text (tooltip, -1) : NULL; ensure_has_tooltip (entry); + + g_object_notify (G_OBJECT (entry), + icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-tooltip-text" : "secondary-icon-tooltip-text"); } /** @@ -8486,7 +8919,7 @@ gtk_entry_get_icon_tooltip_markup (GtkEntry *entry, * Use %NULL for @tooltip to remove an existing tooltip. * * See also gtk_widget_set_tooltip_markup() and - * gtk_enty_set_icon_tooltip_text(). + * gtk_entry_set_icon_tooltip_text(). * * Since: 2.16 */ @@ -8856,6 +9289,151 @@ gtk_entry_popup_menu (GtkWidget *widget) return TRUE; } +static void +activate_bubble_cb (GtkWidget *item, + GtkEntry *entry) +{ + const gchar *signal = g_object_get_data (G_OBJECT (item), "gtk-signal"); + g_signal_emit_by_name (entry, signal); + _gtk_bubble_window_popdown (GTK_BUBBLE_WINDOW (entry->priv->selection_bubble)); +} + +static void +append_bubble_action (GtkEntry *entry, + 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), entry); + 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) +{ + GtkEntry *entry = user_data; + GtkEntryPrivate *priv = entry->priv; + cairo_rectangle_int_t rect; + GtkAllocation allocation; + gint start_x, end_x; + gboolean has_selection; + gboolean has_clipboard; + DisplayMode mode; + GtkWidget *toolbar; + + has_selection = gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), + NULL, NULL); + if (!has_selection && !priv->editable) + { + priv->selection_bubble_timeout_id = 0; + return; + } + + if (priv->selection_bubble) + gtk_widget_destroy (priv->selection_bubble); + + 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); + + has_clipboard = gtk_selection_data_targets_include_text (data); + mode = gtk_entry_get_display_mode (entry); + + append_bubble_action (entry, toolbar, GTK_STOCK_CUT, "cut-clipboard", + priv->editable && has_selection && mode == DISPLAY_NORMAL); + + append_bubble_action (entry, toolbar, GTK_STOCK_COPY, "copy-clipboard", + has_selection && mode == DISPLAY_NORMAL); + + append_bubble_action (entry, toolbar, GTK_STOCK_PASTE, "paste-clipboard", + priv->editable && has_clipboard); + + if (priv->populate_all) + g_signal_emit (entry, signals[POPULATE_POPUP], 0, toolbar); + + gtk_widget_get_allocation (GTK_WIDGET (entry), &allocation); + + gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &start_x, NULL); + + start_x -= priv->scroll_offset; + start_x = CLAMP (start_x, 0, gdk_window_get_width (priv->text_area)); + + rect.y = 0; + rect.height = gdk_window_get_height (priv->text_area); + + if (has_selection) + { + end_x = gtk_entry_get_selection_bound_location (entry) - priv->scroll_offset; + end_x = CLAMP (end_x, 0, gdk_window_get_width (priv->text_area)); + + rect.x = MIN (start_x, end_x); + rect.width = MAX (start_x, end_x) - rect.x; + } + else + { + rect.x = start_x; + rect.width = 0; + } + + _gtk_bubble_window_popup (GTK_BUBBLE_WINDOW (priv->selection_bubble), + priv->text_area, &rect, GTK_POS_TOP); + + priv->selection_bubble_timeout_id = 0; +} + +static gboolean +gtk_entry_selection_bubble_popup_cb (gpointer user_data) +{ + GtkEntry *entry = user_data; + + gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD), + gdk_atom_intern_static_string ("TARGETS"), + bubble_targets_received, + entry); + return G_SOURCE_REMOVE; +} + +static void +gtk_entry_selection_bubble_popup_unset (GtkEntry *entry) +{ + GtkEntryPrivate *priv; + + priv = entry->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_entry_selection_bubble_popup_set (GtkEntry *entry) +{ + GtkEntryPrivate *priv; + + priv = entry->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_entry_selection_bubble_popup_cb, entry); +} + static void gtk_entry_drag_begin (GtkWidget *widget, GdkDragContext *context) @@ -9089,7 +9667,7 @@ gtk_entry_drag_data_get (GtkWidget *widget, if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end)) { - gchar *str = gtk_entry_get_display_text (GTK_ENTRY (widget), sel_start, sel_end); + gchar *str = _gtk_entry_get_display_text (GTK_ENTRY (widget), sel_start, sel_end); gtk_selection_data_set_text (selection_data, str, -1); @@ -9157,6 +9735,18 @@ cursor_blinks (GtkEntry *entry) return FALSE; } +static gboolean +get_middle_click_paste (GtkEntry *entry) +{ + GtkSettings *settings; + gboolean paste; + + settings = gtk_widget_get_settings (GTK_WIDGET (entry)); + g_object_get (settings, "gtk-enable-primary-paste", &paste, NULL); + + return paste; +} + static gint get_cursor_time (GtkEntry *entry) { @@ -9317,509 +9907,6 @@ gtk_entry_reset_blink_time (GtkEntry *entry) priv->blink_time = 0; } - -/* completion */ -static gint -gtk_entry_completion_timeout (gpointer data) -{ - GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data); - GtkEntryPrivate *completion_entry_priv = GTK_ENTRY (completion->priv->entry)->priv; - - completion->priv->completion_timeout = 0; - - if (completion->priv->filter_model && - g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry)), -1) - >= completion->priv->minimum_key_length) - { - gint matches; - gint actions; - GtkTreeSelection *s; - gboolean popup_single; - - gtk_entry_completion_complete (completion); - matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL); - - gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view))); - - s = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)); - - gtk_tree_selection_unselect_all (s); - - actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL); - - g_object_get (completion, "popup-single-match", &popup_single, NULL); - if ((matches > (popup_single ? 0: 1)) || actions > 0) - { - if (gtk_widget_get_visible (completion->priv->popup_window)) - _gtk_entry_completion_resize_popup (completion); - else - _gtk_entry_completion_popup (completion, completion_entry_priv->completion_device); - } - else - _gtk_entry_completion_popdown (completion); - } - else if (gtk_widget_get_visible (completion->priv->popup_window)) - _gtk_entry_completion_popdown (completion); - - return FALSE; -} - -static inline gboolean -keyval_is_cursor_move (guint keyval) -{ - if (keyval == GDK_KEY_Up || keyval == GDK_KEY_KP_Up) - return TRUE; - - if (keyval == GDK_KEY_Down || keyval == GDK_KEY_KP_Down) - return TRUE; - - if (keyval == GDK_KEY_Page_Up) - return TRUE; - - if (keyval == GDK_KEY_Page_Down) - return TRUE; - - return FALSE; -} - -static gboolean -gtk_entry_completion_key_press (GtkWidget *widget, - GdkEventKey *event, - gpointer user_data) -{ - gint matches, actions = 0; - GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data); - - if (!gtk_widget_get_mapped (completion->priv->popup_window)) - return FALSE; - - matches = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->filter_model), NULL); - - if (completion->priv->actions) - actions = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (completion->priv->actions), NULL); - - if (keyval_is_cursor_move (event->keyval)) - { - GtkTreePath *path = NULL; - - if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up) - { - if (completion->priv->current_selected < 0) - completion->priv->current_selected = matches + actions - 1; - else - completion->priv->current_selected--; - } - else if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down) - { - if (completion->priv->current_selected < matches + actions - 1) - completion->priv->current_selected++; - else - completion->priv->current_selected = -1; - } - else if (event->keyval == GDK_KEY_Page_Up) - { - if (completion->priv->current_selected < 0) - completion->priv->current_selected = matches + actions - 1; - else if (completion->priv->current_selected == 0) - completion->priv->current_selected = -1; - else if (completion->priv->current_selected < matches) - { - completion->priv->current_selected -= PAGE_STEP; - if (completion->priv->current_selected < 0) - completion->priv->current_selected = 0; - } - else - { - completion->priv->current_selected -= PAGE_STEP; - if (completion->priv->current_selected < matches - 1) - completion->priv->current_selected = matches - 1; - } - } - else if (event->keyval == GDK_KEY_Page_Down) - { - if (completion->priv->current_selected < 0) - completion->priv->current_selected = 0; - else if (completion->priv->current_selected < matches - 1) - { - completion->priv->current_selected += PAGE_STEP; - if (completion->priv->current_selected > matches - 1) - completion->priv->current_selected = matches - 1; - } - else if (completion->priv->current_selected == matches + actions - 1) - { - completion->priv->current_selected = -1; - } - else - { - completion->priv->current_selected += PAGE_STEP; - if (completion->priv->current_selected > matches + actions - 1) - completion->priv->current_selected = matches + actions - 1; - } - } - - if (completion->priv->current_selected < 0) - { - gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view))); - gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view))); - - if (completion->priv->inline_selection && - completion->priv->completion_prefix) - { - gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), - completion->priv->completion_prefix); - gtk_editable_set_position (GTK_EDITABLE (widget), -1); - } - } - else if (completion->priv->current_selected < matches) - { - gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view))); - - path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1); - gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view), - path, NULL, FALSE); - - if (completion->priv->inline_selection) - { - - GtkTreeIter iter; - GtkTreeIter child_iter; - GtkTreeModel *model = NULL; - GtkTreeSelection *sel; - gboolean entry_set; - - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)); - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) - return FALSE; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter); - model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model)); - - if (completion->priv->completion_prefix == NULL) - completion->priv->completion_prefix = g_strdup (gtk_entry_get_text (GTK_ENTRY (completion->priv->entry))); - - g_signal_emit_by_name (completion, "cursor-on-match", model, - &child_iter, &entry_set); - } - } - else if (completion->priv->current_selected - matches >= 0) - { - gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view))); - - path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1); - gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->action_view), - path, NULL, FALSE); - - if (completion->priv->inline_selection && - completion->priv->completion_prefix) - { - gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), - completion->priv->completion_prefix); - gtk_editable_set_position (GTK_EDITABLE (widget), -1); - } - } - - gtk_tree_path_free (path); - - return TRUE; - } - else if (event->keyval == GDK_KEY_Escape || - event->keyval == GDK_KEY_Left || - event->keyval == GDK_KEY_KP_Left || - event->keyval == GDK_KEY_Right || - event->keyval == GDK_KEY_KP_Right) - { - gboolean retval = TRUE; - - _gtk_entry_reset_im_context (GTK_ENTRY (widget)); - _gtk_entry_completion_popdown (completion); - - if (completion->priv->current_selected < 0) - { - retval = FALSE; - goto keypress_completion_out; - } - else if (completion->priv->inline_selection) - { - /* Escape rejects the tentative completion */ - if (event->keyval == GDK_KEY_Escape) - { - if (completion->priv->completion_prefix) - gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), - completion->priv->completion_prefix); - else - gtk_entry_set_text (GTK_ENTRY (completion->priv->entry), ""); - } - - /* Move the cursor to the end for Right/Esc, to the - beginning for Left */ - if (event->keyval == GDK_KEY_Right || - event->keyval == GDK_KEY_KP_Right || - event->keyval == GDK_KEY_Escape) - gtk_editable_set_position (GTK_EDITABLE (widget), -1); - else - gtk_editable_set_position (GTK_EDITABLE (widget), 0); - } - -keypress_completion_out: - if (completion->priv->inline_selection) - { - g_free (completion->priv->completion_prefix); - completion->priv->completion_prefix = NULL; - } - - return retval; - } - else if (event->keyval == GDK_KEY_Tab || - event->keyval == GDK_KEY_KP_Tab || - event->keyval == GDK_KEY_ISO_Left_Tab) - { - _gtk_entry_reset_im_context (GTK_ENTRY (widget)); - _gtk_entry_completion_popdown (completion); - - g_free (completion->priv->completion_prefix); - completion->priv->completion_prefix = NULL; - - return FALSE; - } - else if (event->keyval == GDK_KEY_ISO_Enter || - event->keyval == GDK_KEY_KP_Enter || - event->keyval == GDK_KEY_Return) - { - GtkTreeIter iter; - GtkTreeModel *model = NULL; - GtkTreeModel *child_model; - GtkTreeIter child_iter; - GtkTreeSelection *sel; - gboolean retval = TRUE; - - _gtk_entry_reset_im_context (GTK_ENTRY (widget)); - _gtk_entry_completion_popdown (completion); - - if (completion->priv->current_selected < matches) - { - gboolean entry_set; - - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)); - if (gtk_tree_selection_get_selected (sel, &model, &iter)) - { - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter); - child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model)); - g_signal_handler_block (widget, completion->priv->changed_id); - g_signal_emit_by_name (completion, "match-selected", - child_model, &child_iter, &entry_set); - g_signal_handler_unblock (widget, completion->priv->changed_id); - - if (!entry_set) - { - gchar *str = NULL; - - gtk_tree_model_get (model, &iter, - completion->priv->text_column, &str, - -1); - - gtk_entry_set_text (GTK_ENTRY (widget), str); - - /* move the cursor to the end */ - gtk_editable_set_position (GTK_EDITABLE (widget), -1); - - g_free (str); - } - } - else - retval = FALSE; - } - else if (completion->priv->current_selected - matches >= 0) - { - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)); - if (gtk_tree_selection_get_selected (sel, &model, &iter)) - { - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1); - g_signal_emit_by_name (completion, "action-activated", - gtk_tree_path_get_indices (path)[0]); - gtk_tree_path_free (path); - } - else - retval = FALSE; - } - - g_free (completion->priv->completion_prefix); - completion->priv->completion_prefix = NULL; - - return retval; - } - - return FALSE; -} - -static void -gtk_entry_completion_changed (GtkWidget *widget, - gpointer user_data) -{ - GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (user_data); - GtkEntry *entry = GTK_ENTRY (widget); - GtkEntryPrivate *priv = entry->priv; - GdkDevice *device; - - /* (re)install completion timeout */ - if (completion->priv->completion_timeout) - g_source_remove (completion->priv->completion_timeout); - - if (!gtk_entry_get_text (entry)) - return; - - /* no need to normalize for this test */ - if (completion->priv->minimum_key_length > 0 && - strcmp ("", gtk_entry_get_text (entry)) == 0) - { - if (gtk_widget_get_visible (completion->priv->popup_window)) - _gtk_entry_completion_popdown (completion); - return; - } - - device = gtk_get_current_event_device (); - - if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) - device = gdk_device_get_associated_device (device); - - if (device) - priv->completion_device = device; - - completion->priv->completion_timeout = - gdk_threads_add_timeout (COMPLETION_TIMEOUT, - gtk_entry_completion_timeout, - completion); -} - -static gboolean -check_completion_callback (GtkEntryCompletion *completion) -{ - completion->priv->check_completion_idle = NULL; - - gtk_entry_completion_complete (completion); - gtk_entry_completion_insert_prefix (completion); - - return FALSE; -} - -static void -clear_completion_callback (GtkEntry *entry, - GParamSpec *pspec) -{ - if (pspec->name == I_("cursor-position") || - pspec->name == I_("selection-bound")) - { - GtkEntryCompletion *completion = gtk_entry_get_completion (entry); - - completion->priv->has_completion = FALSE; - } -} - -static gboolean -accept_completion_callback (GtkEntry *entry) -{ - GtkEntryCompletion *completion = gtk_entry_get_completion (entry); - - if (completion->priv->has_completion) - gtk_editable_set_position (GTK_EDITABLE (entry), - gtk_entry_buffer_get_length (get_buffer (entry))); - - return FALSE; -} - -static void -completion_insert_text_callback (GtkEntry *entry, - const gchar *text, - gint length, - gint position, - GtkEntryCompletion *completion) -{ - /* idle to update the selection based on the file list */ - if (completion->priv->check_completion_idle == NULL) - { - completion->priv->check_completion_idle = g_idle_source_new (); - g_source_set_priority (completion->priv->check_completion_idle, G_PRIORITY_HIGH); - g_source_set_closure (completion->priv->check_completion_idle, - g_cclosure_new_object (G_CALLBACK (check_completion_callback), - G_OBJECT (completion))); - g_source_attach (completion->priv->check_completion_idle, NULL); - } -} - -static void -completion_changed (GtkEntryCompletion *completion, - GParamSpec *pspec, - gpointer data) -{ - GtkEntry *entry = GTK_ENTRY (data); - - if (pspec->name == I_("popup-completion") || - pspec->name == I_("inline-completion")) - { - disconnect_completion_signals (entry, completion); - connect_completion_signals (entry, completion); - } -} - -static void -disconnect_completion_signals (GtkEntry *entry, - GtkEntryCompletion *completion) -{ - g_signal_handlers_disconnect_by_func (completion, - G_CALLBACK (completion_changed), entry); - if (completion->priv->changed_id > 0 && - g_signal_handler_is_connected (entry, completion->priv->changed_id)) - { - g_signal_handler_disconnect (entry, completion->priv->changed_id); - completion->priv->changed_id = 0; - } - g_signal_handlers_disconnect_by_func (entry, - G_CALLBACK (gtk_entry_completion_key_press), completion); - if (completion->priv->insert_text_id > 0 && - g_signal_handler_is_connected (entry, completion->priv->insert_text_id)) - { - g_signal_handler_disconnect (entry, completion->priv->insert_text_id); - completion->priv->insert_text_id = 0; - } - g_signal_handlers_disconnect_by_func (entry, - G_CALLBACK (completion_insert_text_callback), completion); - g_signal_handlers_disconnect_by_func (entry, - G_CALLBACK (clear_completion_callback), completion); - g_signal_handlers_disconnect_by_func (entry, - G_CALLBACK (accept_completion_callback), completion); -} - -static void -connect_completion_signals (GtkEntry *entry, - GtkEntryCompletion *completion) -{ - if (completion->priv->popup_completion) - { - completion->priv->changed_id = - g_signal_connect (entry, "changed", - G_CALLBACK (gtk_entry_completion_changed), completion); - g_signal_connect (entry, "key-press-event", - G_CALLBACK (gtk_entry_completion_key_press), completion); - } - - if (completion->priv->inline_completion) - { - completion->priv->insert_text_id = - g_signal_connect (entry, "insert-text", - G_CALLBACK (completion_insert_text_callback), completion); - g_signal_connect (entry, "notify", - G_CALLBACK (clear_completion_callback), completion); - g_signal_connect (entry, "activate", - G_CALLBACK (accept_completion_callback), completion); - g_signal_connect (entry, "focus-out-event", - G_CALLBACK (accept_completion_callback), completion); - } - - g_signal_connect (completion, "notify", - G_CALLBACK (completion_changed), entry); -} - /** * gtk_entry_set_completion: * @entry: A #GtkEntry @@ -9848,24 +9935,7 @@ gtk_entry_set_completion (GtkEntry *entry, if (old) { - if (old->priv->completion_timeout) - { - g_source_remove (old->priv->completion_timeout); - old->priv->completion_timeout = 0; - } - - if (old->priv->check_completion_idle) - { - g_source_destroy (old->priv->check_completion_idle); - old->priv->check_completion_idle = NULL; - } - - if (gtk_widget_get_mapped (old->priv->popup_window)) - _gtk_entry_completion_popdown (old); - - disconnect_completion_signals (entry, old); - old->priv->entry = NULL; - + _gtk_entry_completion_disconnect (old); g_object_unref (old); } @@ -9878,8 +9948,8 @@ gtk_entry_set_completion (GtkEntry *entry, /* hook into the entry */ g_object_ref (completion); - connect_completion_signals (entry, completion); - completion->priv->entry = GTK_WIDGET (entry); + _gtk_entry_completion_connect (completion, entry); + g_object_set_data (G_OBJECT (entry), I_(GTK_ENTRY_COMPLETION_KEY), completion); g_object_notify (G_OBJECT (entry), "completion"); @@ -10242,12 +10312,7 @@ keymap_state_changed (GdkKeymap *keymap, if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL && priv->caps_lock_warning) { - if (gdk_keymap_get_num_lock_state (keymap) - && gdk_keymap_get_caps_lock_state (keymap)) - text = _("Caps Lock and Num Lock are on"); - else if (gdk_keymap_get_num_lock_state (keymap)) - text = _("Num Lock is on"); - else if (gdk_keymap_get_caps_lock_state (keymap)) + if (gdk_keymap_get_caps_lock_state (keymap)) text = _("Caps Lock is on"); } @@ -10274,3 +10339,153 @@ _gtk_entry_set_is_cell_renderer (GtkEntry *entry, { entry->priv->is_cell_renderer = is_cell_renderer; } + +/** + * gtk_entry_set_input_purpose: + * @entry: a #GtkEntry + * @purpose: the purpose + * + * Sets the #GtkEntry:input-purpose property which + * can be used by on-screen keyboards and other input + * methods to adjust their behaviour. + * + * Since: 3.6 + */ +void +gtk_entry_set_input_purpose (GtkEntry *entry, + GtkInputPurpose purpose) + +{ + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (gtk_entry_get_input_purpose (entry) != purpose) + { + g_object_set (G_OBJECT (entry->priv->im_context), + "input-purpose", purpose, + NULL); + + g_object_notify (G_OBJECT (entry), "input-purpose"); + } +} + +/** + * gtk_entry_get_input_purpose: + * @entry: a #GtkEntry + * + * Gets the value of the #GtkEntry:input-purpose property. + * + * Since: 3.6 + */ +GtkInputPurpose +gtk_entry_get_input_purpose (GtkEntry *entry) +{ + GtkInputPurpose purpose; + + g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_INPUT_PURPOSE_FREE_FORM); + + g_object_get (G_OBJECT (entry->priv->im_context), + "input-purpose", &purpose, + NULL); + + return purpose; +} + +/** + * gtk_entry_set_input_hints: + * @entry: a #GtkEntry + * @hints: the hints + * + * Sets the #GtkEntry:input-hints property, which + * allows input methods to fine-tune their behaviour. + * + * Since: 3.6 + */ +void +gtk_entry_set_input_hints (GtkEntry *entry, + GtkInputHints hints) + +{ + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (gtk_entry_get_input_hints (entry) != hints) + { + g_object_set (G_OBJECT (entry->priv->im_context), + "input-hints", hints, + NULL); + + g_object_notify (G_OBJECT (entry), "input-hints"); + } +} + +/** + * gtk_entry_get_input_hints: + * @entry: a #GtkEntry + * + * Gets the value of the #GtkEntry:input-hints property. + * + * Since: 3.6 + */ +GtkInputHints +gtk_entry_get_input_hints (GtkEntry *entry) +{ + GtkInputHints hints; + + g_return_val_if_fail (GTK_IS_ENTRY (entry), GTK_INPUT_HINT_NONE); + + g_object_get (G_OBJECT (entry->priv->im_context), + "input-hints", &hints, + NULL); + + return hints; +} + +/** + * gtk_entry_set_attributes: + * @entry: a #GtkEntry + * @attrs: a #PangoAttrList + * + * Sets a #PangoAttrList; the attributes in the list are applied to the + * entry text. + * + * Since: 3.6 + */ +void +gtk_entry_set_attributes (GtkEntry *entry, + PangoAttrList *attrs) +{ + GtkEntryPrivate *priv = entry->priv; + g_return_if_fail (GTK_IS_ENTRY (entry)); + + if (attrs) + pango_attr_list_ref (attrs); + + if (priv->attrs) + pango_attr_list_unref (priv->attrs); + priv->attrs = attrs; + + g_object_notify (G_OBJECT (entry), "attributes"); + + gtk_entry_recompute (entry); + gtk_widget_queue_resize (GTK_WIDGET (entry)); +} + +/** + * gtk_entry_get_attributes: + * @entry: a #GtkEntry + * + * Gets the attribute list that was set on the entry using + * gtk_entry_set_attributes(), if any. + * + * Return value: (transfer none): the attribute list, or %NULL + * if none was set. + * + * Since: 3.6 + */ +PangoAttrList * +gtk_entry_get_attributes (GtkEntry *entry) +{ + g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); + + return entry->priv->attrs; +} +