diff options
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/entry_buffer.c | 65 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-sections.txt | 31 | ||||
-rw-r--r-- | docs/reference/gtk/gtk.types | 1 | ||||
-rw-r--r-- | docs/reference/gtk/tmpl/gtk-unused.sgml | 19 | ||||
-rw-r--r-- | docs/reference/gtk/tmpl/gtkentry.sgml | 32 | ||||
-rw-r--r-- | docs/reference/gtk/tmpl/gtkentrybuffer.sgml | 168 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkentry.c | 979 | ||||
-rw-r--r-- | gtk/gtkentry.h | 15 | ||||
-rw-r--r-- | gtk/gtkentrybuffer.c | 741 | ||||
-rw-r--r-- | gtk/gtkentrybuffer.h | 133 | ||||
-rw-r--r-- | gtk/gtkmarshalers.list | 2 |
15 files changed, 1754 insertions, 437 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 23eed00eec..22662c43b0 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -17,6 +17,7 @@ demos = \ dialog.c \ drawingarea.c \ editable_cells.c \ + entry_buffer.c \ entry_completion.c \ expander.c \ hypertext.c \ diff --git a/demos/gtk-demo/entry_buffer.c b/demos/gtk-demo/entry_buffer.c new file mode 100644 index 0000000000..2be770e3bf --- /dev/null +++ b/demos/gtk-demo/entry_buffer.c @@ -0,0 +1,65 @@ +/* Entry/Entry Buffer + * + * GtkEntryBuffer provides the text content in a GtkEntry. + * + */ + +#include <gtk/gtk.h> + +static GtkWidget *window = NULL; + +GtkWidget * +do_entry_buffer (GtkWidget *do_widget) +{ + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *entry; + GtkEntryBuffer *buffer; + + if (!window) + { + window = gtk_dialog_new_with_buttons ("GtkEntryBuffer", + GTK_WINDOW (do_widget), + 0, + GTK_STOCK_CLOSE, + GTK_RESPONSE_NONE, + NULL); + gtk_window_set_resizable (GTK_WINDOW (window), FALSE); + + g_signal_connect (window, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), &window); + + vbox = gtk_vbox_new (FALSE, 5); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), vbox, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + + label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (label), "Entries share a buffer. Typing in one is reflected in the other."); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + + /* Create a buffer */ + buffer = gtk_entry_buffer_new (NULL, 0); + + /* Create our first entry */ + entry = gtk_entry_new_with_buffer (buffer); + gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); + + /* Create the second entry */ + entry = gtk_entry_new_with_buffer (buffer); + gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE); + gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0); + + g_object_unref (buffer); + } + + if (!GTK_WIDGET_VISIBLE (window)) + gtk_widget_show_all (window); + else + gtk_widget_destroy (window); + + return window; +} + + diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index 16a59b0f1e..9b65c5ec77 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -177,6 +177,7 @@ that is, GUI components such as #GtkButton or #GtkTextView. <chapter id="NumericEntry"> <title>Numeric/Text Data Entry</title> <xi:include href="xml/gtkentry.xml" /> + <xi:include href="xml/gtkentrybuffer.xml" /> <xi:include href="xml/gtkentrycompletion.xml" /> <xi:include href="xml/gtkhscale.xml" /> <xi:include href="xml/gtkvscale.xml" /> diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt index bb19dec3a6..331157dfb4 100644 --- a/docs/reference/gtk/gtk-sections.txt +++ b/docs/reference/gtk/gtk-sections.txt @@ -1268,7 +1268,10 @@ gtk_old_editable_get_type <TITLE>GtkEntry</TITLE> GtkEntry gtk_entry_new +gtk_entry_new_with_buffer gtk_entry_new_with_max_length +gtk_entry_get_buffer +gtk_entry_set_buffer gtk_entry_set_text gtk_entry_append_text gtk_entry_prepend_text @@ -1343,6 +1346,34 @@ gtk_entry_get_type </SECTION> <SECTION> +<FILE>gtkentrybuffer</FILE> +<TITLE>GtkEntryBuffer</TITLE> +GtkEntryBuffer +gtk_entry_buffer_new +gtk_entry_buffer_get_text +gtk_entry_buffer_set_text +gtk_entry_buffer_get_bytes +gtk_entry_buffer_get_length +gtk_entry_buffer_get_max_length +gtk_entry_buffer_set_max_length +gtk_entry_buffer_insert_text +gtk_entry_buffer_delete_text +gtk_entry_buffer_emit_deleted_text +gtk_entry_buffer_emit_inserted_text + +<SUBSECTION Standard> +GTK_ENTRY_BUFFER +GTK_IS_ENTRY_BUFFER +GTK_TYPE_ENTRY_BUFFER +GTK_ENTRY_BUFFER_CLASS +GTK_IS_ENTRY_BUFFER_CLASS +GTK_ENTRY_BUFFER_GET_CLASS +GTK_ENTRY_BUFFER_MAX_SIZE +<SUBSECTION Private> +gtk_entry_buffer_get_type +</SECTION> + +<SECTION> <FILE>gtkentrycompletion</FILE> <TITLE>GtkEntryCompletion</TITLE> GtkEntryCompletion diff --git a/docs/reference/gtk/gtk.types b/docs/reference/gtk/gtk.types index 126f673bb1..d743aa1e1a 100644 --- a/docs/reference/gtk/gtk.types +++ b/docs/reference/gtk/gtk.types @@ -48,6 +48,7 @@ gtk_curve_get_type gtk_dialog_get_type gtk_drawing_area_get_type gtk_editable_get_type +gtk_entry_buffer_get_type gtk_entry_completion_get_type gtk_entry_get_type gtk_event_box_get_type diff --git a/docs/reference/gtk/tmpl/gtk-unused.sgml b/docs/reference/gtk/tmpl/gtk-unused.sgml index cb2a663eff..420d945397 100644 --- a/docs/reference/gtk/tmpl/gtk-unused.sgml +++ b/docs/reference/gtk/tmpl/gtk-unused.sgml @@ -1033,6 +1033,25 @@ You may not attach these to signals created with the </para> +<!-- ##### STRUCT GtkEntryBufferClass ##### --> +<para> + +</para> + +@parent_class: +@inserted_text: +@deleted_text: +@get_text: +@get_length: +@insert_text: +@delete_text: +@_gtk_reserved0: +@_gtk_reserved1: +@_gtk_reserved2: +@_gtk_reserved3: +@_gtk_reserved4: +@_gtk_reserved5: + <!-- ##### ARG GtkFileChooser:file-system ##### --> <para> diff --git a/docs/reference/gtk/tmpl/gtkentry.sgml b/docs/reference/gtk/tmpl/gtkentry.sgml index 385e7235e7..59a95c68fb 100644 --- a/docs/reference/gtk/tmpl/gtkentry.sgml +++ b/docs/reference/gtk/tmpl/gtkentry.sgml @@ -173,6 +173,11 @@ The #GtkEntry-struct struct contains only private data. </para> +<!-- ##### ARG GtkEntry:buffer ##### --> +<para> + +</para> + <!-- ##### ARG GtkEntry:caps-lock-warning ##### --> <para> @@ -401,6 +406,15 @@ The #GtkEntry-struct struct contains only private data. @Returns: +<!-- ##### FUNCTION gtk_entry_new_with_buffer ##### --> +<para> + +</para> + +@buffer: +@Returns: + + <!-- ##### FUNCTION gtk_entry_new_with_max_length ##### --> <para> </para> @@ -409,6 +423,24 @@ The #GtkEntry-struct struct contains only private data. @Returns: +<!-- ##### FUNCTION gtk_entry_get_buffer ##### --> +<para> + +</para> + +@entry: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_set_buffer ##### --> +<para> + +</para> + +@entry: +@buffer: + + <!-- ##### FUNCTION gtk_entry_set_text ##### --> <para> diff --git a/docs/reference/gtk/tmpl/gtkentrybuffer.sgml b/docs/reference/gtk/tmpl/gtkentrybuffer.sgml new file mode 100644 index 0000000000..712f013153 --- /dev/null +++ b/docs/reference/gtk/tmpl/gtkentrybuffer.sgml @@ -0,0 +1,168 @@ +<!-- ##### SECTION Title ##### --> +GtkEntryBuffer + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### SECTION Stability_Level ##### --> + + +<!-- ##### STRUCT GtkEntryBuffer ##### --> +<para> + +</para> + + +<!-- ##### SIGNAL GtkEntryBuffer::deleted-text ##### --> +<para> + +</para> + +@entrybuffer: the object which received the signal. +@arg1: +@arg2: + +<!-- ##### SIGNAL GtkEntryBuffer::inserted-text ##### --> +<para> + +</para> + +@entrybuffer: the object which received the signal. +@arg1: +@arg2: +@arg3: + +<!-- ##### ARG GtkEntryBuffer:length ##### --> +<para> + +</para> + +<!-- ##### ARG GtkEntryBuffer:max-length ##### --> +<para> + +</para> + +<!-- ##### ARG GtkEntryBuffer:text ##### --> +<para> + +</para> + +<!-- ##### FUNCTION gtk_entry_buffer_new ##### --> +<para> + +</para> + +@initial_chars: +@n_initial_chars: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_get_text ##### --> +<para> + +</para> + +@buffer: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_set_text ##### --> +<para> + +</para> + +@buffer: +@chars: +@n_chars: + + +<!-- ##### FUNCTION gtk_entry_buffer_get_bytes ##### --> +<para> + +</para> + +@buffer: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_get_length ##### --> +<para> + +</para> + +@buffer: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_get_max_length ##### --> +<para> + +</para> + +@buffer: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_set_max_length ##### --> +<para> + +</para> + +@buffer: +@max_length: + + +<!-- ##### FUNCTION gtk_entry_buffer_insert_text ##### --> +<para> + +</para> + +@buffer: +@position: +@chars: +@n_chars: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_delete_text ##### --> +<para> + +</para> + +@buffer: +@position: +@n_chars: +@Returns: + + +<!-- ##### FUNCTION gtk_entry_buffer_emit_deleted_text ##### --> +<para> + +</para> + +@buffer: +@position: +@n_chars: + + +<!-- ##### FUNCTION gtk_entry_buffer_emit_inserted_text ##### --> +<para> + +</para> + +@buffer: +@position: +@chars: +@n_chars: + + diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 02463f0371..48b1f74ecf 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -197,6 +197,7 @@ gtk_public_h_sources = \ gtkdrawingarea.h \ gtkeditable.h \ gtkentry.h \ + gtkentrybuffer.h \ gtkentrycompletion.h \ gtkenums.h \ gtkeventbox.h \ @@ -447,6 +448,7 @@ gtk_base_c_sources = \ gtkdrawingarea.c \ gtkeditable.c \ gtkentry.c \ + gtkentrybuffer.c \ gtkentrycompletion.c \ gtkeventbox.c \ gtkexpander.c \ @@ -78,6 +78,7 @@ #include <gtk/gtkdrawingarea.h> #include <gtk/gtkeditable.h> #include <gtk/gtkentry.h> +#include <gtk/gtkentrybuffer.h> #include <gtk/gtkentrycompletion.h> #include <gtk/gtkenums.h> #include <gtk/gtkeventbox.h> diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index 784667c128..f147b3f16d 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -40,6 +40,7 @@ #include "gtkclipboard.h" #include "gtkdnd.h" #include "gtkentry.h" +#include "gtkentrybuffer.h" #include "gtkimagemenuitem.h" #include "gtkimcontextsimple.h" #include "gtkimmulticontext.h" @@ -73,12 +74,6 @@ #define COMPLETION_TIMEOUT 300 #define PASSWORD_HINT_MAX 8 -/* Initial size of buffer, in bytes */ -#define MIN_SIZE 16 - -/* Maximum size of text buffer, in bytes */ -#define MAX_SIZE G_MAXUSHORT - #define MAX_ICONS 2 #define IS_VALID_ICON_POSITION(pos) \ @@ -117,6 +112,8 @@ typedef struct struct _GtkEntryPrivate { + GtkEntryBuffer* buffer; + gfloat xalign; gint insert_pos; guint blink_time; /* time in msec the cursor has blinked since last user event */ @@ -148,10 +145,8 @@ typedef struct _GtkEntryPasswordHint GtkEntryPasswordHint; struct _GtkEntryPasswordHint { - gchar password_hint[PASSWORD_HINT_MAX]; - guint password_hint_timeout_id; - gint password_hint_length; - gint password_hint_position; + gint position; /* Position (in text) of the last password hint */ + guint source_id; /* Timeout source id */ }; typedef struct _GtkEntryCapslockFeedback GtkEntryCapslockFeedback; @@ -181,6 +176,7 @@ enum { enum { PROP_0, + PROP_BUFFER, PROP_CURSOR_POSITION, PROP_SELECTION_BOUND, PROP_EDITABLE, @@ -230,10 +226,20 @@ typedef enum { CURSOR_DND } CursorType; +typedef enum +{ + DISPLAY_NORMAL, /* The entry text is being shown */ + DISPLAY_INVISIBLE, /* In invisible mode, text replaced by (eg) bullets */ + DISPLAY_BLANK /* In invisible mode, nothing shown at all */ +} DisplayMode; + /* GObject, GtkObject methods */ static void gtk_entry_editable_init (GtkEditableClass *iface); static void gtk_entry_cell_editable_init (GtkCellEditableIface *iface); +static GObject* gtk_entry_constructor (GType type, + guint n_props, + GObjectConstructParam *props); static void gtk_entry_set_property (GObject *object, guint prop_id, const GValue *value, @@ -439,9 +445,6 @@ static gint gtk_entry_move_backward_word (GtkEntry *entry, static void gtk_entry_delete_whitespace (GtkEntry *entry); static void gtk_entry_select_word (GtkEntry *entry); static void gtk_entry_select_line (GtkEntry *entry); -static char * gtk_entry_get_public_chars (GtkEntry *entry, - gint start, - gint end); static void gtk_entry_paste (GtkEntry *entry, GdkAtom selection); static void gtk_entry_update_primary_selection (GtkEntry *entry); @@ -501,6 +504,27 @@ static void end_change (GtkEntry *entry); static void emit_changed (GtkEntry *entry); +static void buffer_inserted_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars, + GtkEntry *entry); +static void buffer_deleted_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars, + GtkEntry *entry); +static void buffer_notify_text (GtkEntryBuffer *buffer, + GParamSpec *spec, + GtkEntry *entry); +static void buffer_notify_length (GtkEntryBuffer *buffer, + GParamSpec *spec, + GtkEntry *entry); +static void buffer_notify_max_length (GtkEntryBuffer *buffer, + GParamSpec *spec, + GtkEntry *entry); +static void buffer_connect_signals (GtkEntry *entry); +static void buffer_disconnect_signals (GtkEntry *entry); + G_DEFINE_TYPE_WITH_CODE (GtkEntry, gtk_entry, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, gtk_entry_editable_init) @@ -541,6 +565,7 @@ gtk_entry_class_init (GtkEntryClass *class) widget_class = (GtkWidgetClass*) class; gtk_object_class = (GtkObjectClass *)class; + gobject_class->constructor = gtk_entry_constructor; gobject_class->dispose = gtk_entry_dispose; gobject_class->finalize = gtk_entry_finalize; gobject_class->set_property = gtk_entry_set_property; @@ -600,12 +625,20 @@ gtk_entry_class_init (GtkEntryClass *class) quark_capslock_feedback = g_quark_from_static_string ("gtk-entry-capslock-feedback"); g_object_class_install_property (gobject_class, + PROP_BUFFER, + g_param_spec_object ("buffer", + P_("Text Buffer"), + P_("Text buffer object which actually stores entry text"), + GTK_TYPE_ENTRY_BUFFER, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (gobject_class, PROP_CURSOR_POSITION, g_param_spec_int ("cursor-position", P_("Cursor Position"), P_("The current position of the insertion cursor in chars"), 0, - MAX_SIZE, + GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READABLE)); @@ -615,7 +648,7 @@ gtk_entry_class_init (GtkEntryClass *class) P_("Selection Bound"), P_("The position of the opposite end of the selection from the cursor in chars"), 0, - MAX_SIZE, + GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READABLE)); @@ -633,7 +666,7 @@ gtk_entry_class_init (GtkEntryClass *class) P_("Maximum length"), P_("Maximum number of characters for this entry. Zero if no maximum"), 0, - MAX_SIZE, + GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READWRITE)); g_object_class_install_property (gobject_class, @@ -1754,6 +1787,10 @@ gtk_entry_set_property (GObject *object, switch (prop_id) { + case PROP_BUFFER: + gtk_entry_set_buffer (entry, g_value_get_object (value)); + break; + case PROP_EDITABLE: { gboolean new_value = g_value_get_boolean (value); @@ -1969,6 +2006,10 @@ gtk_entry_get_property (GObject *object, switch (prop_id) { + case PROP_BUFFER: + g_value_set_object (value, gtk_entry_get_buffer (entry)); + break; + case PROP_CURSOR_POSITION: g_value_set_int (value, entry->current_pos); break; @@ -1982,7 +2023,7 @@ gtk_entry_get_property (GObject *object, break; case PROP_MAX_LENGTH: - g_value_set_int (value, entry->text_max_length); + g_value_set_int (value, gtk_entry_buffer_get_max_length (priv->buffer)); break; case PROP_VISIBILITY: @@ -2034,7 +2075,7 @@ gtk_entry_get_property (GObject *object, break; case PROP_TEXT_LENGTH: - g_value_set_uint (value, entry->text_length); + g_value_set_uint (value, gtk_entry_buffer_get_length (priv->buffer)); break; case PROP_INVISIBLE_CHAR_SET: @@ -2212,6 +2253,29 @@ find_invisible_char (GtkWidget *widget) return '*'; } +static GObject* +gtk_entry_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *obj = G_OBJECT_CLASS (gtk_entry_parent_class)->constructor (type, n_props, props); + GtkEntryPrivate *priv; + GtkEntryBuffer *buffer; + + if (obj != NULL) + { + priv = GTK_ENTRY_GET_PRIVATE (obj); + if (!priv->buffer) + { + buffer = gtk_entry_buffer_new (NULL, 0); + gtk_entry_set_buffer (GTK_ENTRY (obj), buffer); + g_object_unref (buffer); + } + } + + return obj; +} + static void gtk_entry_init (GtkEntry *entry) { @@ -2219,10 +2283,6 @@ gtk_entry_init (GtkEntry *entry) GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS); - entry->text_size = MIN_SIZE; - entry->text = g_malloc (entry->text_size); - entry->text[0] = '\0'; - entry->editable = TRUE; entry->visible = TRUE; entry->invisible_char = find_invisible_char (GTK_WIDGET (entry)); @@ -2361,24 +2421,12 @@ emit_changed (GtkEntry *entry) priv->real_changed = TRUE; } -/* - * Overwrite a memory that might contain sensitive information. - */ -static void -trash_area (gchar *area, gsize len) -{ - volatile gchar *varea = (volatile gchar *)area; - while (len-- > 0) - *varea++ = 0; -} - static void gtk_entry_destroy (GtkObject *object) { GtkEntry *entry = GTK_ENTRY (object); - entry->n_bytes = 0; - entry->current_pos = entry->selection_bound = entry->text_length = 0; + entry->current_pos = entry->selection_bound = 0; _gtk_entry_reset_im_context (entry); gtk_entry_reset_layout (entry); @@ -2394,12 +2442,6 @@ gtk_entry_destroy (GtkObject *object) entry->recompute_idle = 0; } - if (!entry->visible) - { - /* We want to trash the text here because the entry might be leaked. */ - trash_area (entry->text, strlen (entry->text)); - } - GTK_OBJECT_CLASS (gtk_entry_parent_class)->destroy (object); } @@ -2452,19 +2494,96 @@ gtk_entry_finalize (GObject *object) if (entry->recompute_idle) g_source_remove (entry->recompute_idle); - entry->text_size = 0; + g_free (priv->im_module); + + /* COMPAT: entry->text is a deprecated field, and the allocation + is owned by the buffer. */ - if (entry->text) + gtk_entry_set_buffer (entry, NULL); + + G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object); +} + +static DisplayMode +gtk_entry_get_display_mode (GtkEntry *entry) +{ + GtkEntryPrivate *priv; + if (entry->visible) + return DISPLAY_NORMAL; + priv = GTK_ENTRY_GET_PRIVATE (entry); + if (entry->invisible_char == 0 && priv->invisible_char_set) + return DISPLAY_BLANK; + return DISPLAY_INVISIBLE; +} + +static gchar* +gtk_entry_get_display_text (GtkEntry *entry, + gint start_pos, + gint end_pos) +{ + GtkEntryPasswordHint *password_hint; + GtkEntryPrivate *priv; + gunichar invisible_char; + const gchar *start; + const gchar *end; + const gchar *text; + gchar char_str[7]; + gint char_len; + GString *str; + guint length; + gint i; + + priv = GTK_ENTRY_GET_PRIVATE (entry); + text = gtk_entry_buffer_get_text (priv->buffer); + length = gtk_entry_buffer_get_length (priv->buffer); + + if (end_pos < 0) + end_pos = length; + if (start_pos > length) + start_pos = length; + + if (end_pos <= start_pos) + return g_strdup (""); + else if (entry->visible) { - if (!entry->visible) - trash_area (entry->text, strlen (entry->text)); - g_free (entry->text); - entry->text = NULL; + start = g_utf8_offset_to_pointer (text, start_pos); + end = g_utf8_offset_to_pointer (start, end_pos - start_pos); + return g_strndup (start, end - start); } + else + { + str = g_string_sized_new (length * 2); - g_free (priv->im_module); + /* Figure out what our invisible char is and encode it */ + if (!entry->invisible_char) + invisible_char = priv->invisible_char_set ? ' ' : '*'; + else + invisible_char = entry->invisible_char; + char_len = g_unichar_to_utf8 (invisible_char, char_str); + + /* + * Add hidden characters for each character in the text + * buffer. If there is a password hint, then keep that + * character visible. + */ + + password_hint = g_object_get_qdata (G_OBJECT (entry), quark_password_hint); + for (i = start_pos; i < end_pos; ++i) + { + if (password_hint && i == password_hint->position) + { + start = g_utf8_offset_to_pointer (text, i); + g_string_append_len (str, start, g_utf8_next_char (start) - start); + } + else + { + g_string_append_len (str, char_str, char_len); + } + } + + return g_string_free (str, FALSE); + } - G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object); } static void @@ -3350,10 +3469,11 @@ gtk_entry_expose (GtkWidget *widget, gtk_entry_draw_text (GTK_ENTRY (widget)); - if ((entry->visible || entry->invisible_char != 0) && + /* 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) && - entry->selection_bound == entry->current_pos && entry->cursor_visible) - gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD); + entry->selection_bound == entry->current_pos && entry->cursor_visible) + gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD); } else { @@ -3822,7 +3942,7 @@ gtk_entry_motion_notify (GtkWidget *widget, if (entry->in_drag) { - if (entry->visible && + if (gtk_entry_get_display_mode (entry) == DISPLAY_NORMAL && gtk_drag_check_threshold (widget, entry->drag_start_x, entry->drag_start_y, event->x + entry->scroll_offset, event->y)) @@ -3868,7 +3988,7 @@ gtk_entry_motion_notify (GtkWidget *widget, if (event->y < 0) tmp_pos = 0; else if (event->y >= height) - tmp_pos = entry->text_length; + tmp_pos = gtk_entry_buffer_get_length (priv->buffer); else tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset); @@ -4156,30 +4276,14 @@ gtk_entry_insert_text (GtkEditable *editable, gint new_text_length, gint *position) { - GtkEntry *entry = GTK_ENTRY (editable); - gchar buf[64]; - gchar *text; - - if (*position < 0 || *position > entry->text_length) - *position = entry->text_length; - g_object_ref (editable); - - if (new_text_length <= 63) - text = buf; - else - text = g_new (gchar, new_text_length + 1); - text[new_text_length] = '\0'; - strncpy (text, new_text, new_text_length); - - g_signal_emit_by_name (editable, "insert-text", text, new_text_length, position); - - if (!entry->visible) - trash_area (text, new_text_length); + /* + * The incoming text may a password or other secret. We make sure + * not to copy it into temporary buffers. + */ - if (new_text_length > 63) - g_free (text); + g_signal_emit_by_name (editable, "insert-text", new_text, new_text_length, position); g_object_unref (editable); } @@ -4189,15 +4293,6 @@ gtk_entry_delete_text (GtkEditable *editable, gint start_pos, gint end_pos) { - GtkEntry *entry = GTK_ENTRY (editable); - - if (end_pos < 0 || end_pos > entry->text_length) - end_pos = entry->text_length; - if (start_pos < 0) - start_pos = 0; - if (start_pos > end_pos) - start_pos = end_pos; - g_object_ref (editable); g_signal_emit_by_name (editable, "delete-text", start_pos, end_pos); @@ -4210,19 +4305,7 @@ gtk_entry_get_chars (GtkEditable *editable, gint start_pos, gint end_pos) { - GtkEntry *entry = GTK_ENTRY (editable); - gint start_index, end_index; - - if (end_pos < 0) - end_pos = entry->text_length; - - start_pos = MIN (entry->text_length, start_pos); - end_pos = MIN (entry->text_length, end_pos); - - start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text; - end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text; - - return g_strndup (entry->text + start_index, end_index - start_index); + return gtk_entry_get_display_text (GTK_ENTRY (editable), start_pos, end_pos); } static void @@ -4230,9 +4313,13 @@ gtk_entry_real_set_position (GtkEditable *editable, gint position) { GtkEntry *entry = GTK_ENTRY (editable); + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + + guint length; - if (position < 0 || position > entry->text_length) - position = entry->text_length; + length = gtk_entry_buffer_get_length (priv->buffer); + if (position < 0 || position > length) + position = length; if (position != entry->current_pos || position != entry->selection_bound) @@ -4254,17 +4341,20 @@ gtk_entry_set_selection_bounds (GtkEditable *editable, gint end) { GtkEntry *entry = GTK_ENTRY (editable); + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + guint length; + length = gtk_entry_buffer_get_length (priv->buffer); if (start < 0) - start = entry->text_length; + start = length; if (end < 0) - end = entry->text_length; + end = length; _gtk_entry_reset_im_context (entry); gtk_entry_set_positions (entry, - MIN (end, entry->text_length), - MIN (start, entry->text_length)); + MIN (end, length), + MIN (start, length)); gtk_entry_update_primary_selection (entry); } @@ -4405,12 +4495,24 @@ gtk_entry_start_editing (GtkCellEditable *cell_editable, static void gtk_entry_password_hint_free (GtkEntryPasswordHint *password_hint) { - if (password_hint->password_hint_timeout_id) - g_source_remove (password_hint->password_hint_timeout_id); + if (password_hint->source_id) + g_source_remove (password_hint->source_id); g_slice_free (GtkEntryPasswordHint, password_hint); } + +static gboolean +gtk_entry_remove_password_hint (gpointer data) +{ + GtkEntryPasswordHint *password_hint = g_object_get_qdata (data, quark_password_hint); + password_hint->position = -1; + + /* Force the string to be redrawn, but now without a visible character */ + gtk_entry_recompute (GTK_ENTRY (data)); + return FALSE; +} + /* Default signal handlers */ static void @@ -4419,82 +4521,58 @@ gtk_entry_real_insert_text (GtkEditable *editable, gint new_text_length, gint *position) { - GtkEntry *entry = GTK_ENTRY (editable); - gint index; + guint n_inserted; gint n_chars; - if (new_text_length < 0) - new_text_length = strlen (new_text); - n_chars = g_utf8_strlen (new_text, new_text_length); - if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length) - { - gtk_widget_error_bell (GTK_WIDGET (entry)); - n_chars = entry->text_max_length - entry->text_length; - new_text_length = g_utf8_offset_to_pointer (new_text, n_chars) - new_text; - } - if (new_text_length + entry->n_bytes + 1 > entry->text_size) - { - gsize prev_size = entry->text_size; + /* + * The actual insertion into the buffer. This will end up firing the + * following signal handlers: buffer_inserted_text(), buffer_notify_display_text(), + * buffer_notify_text(), buffer_notify_length() + */ + n_inserted = gtk_entry_buffer_insert_text (GTK_ENTRY_GET_PRIVATE (editable)->buffer, *position, new_text, n_chars); - while (new_text_length + entry->n_bytes + 1 > entry->text_size) - { - if (entry->text_size == 0) - entry->text_size = MIN_SIZE; - else - { - if (2 * (guint)entry->text_size < MAX_SIZE && - 2 * (guint)entry->text_size > entry->text_size) - entry->text_size *= 2; - else - { - entry->text_size = MAX_SIZE; - if (new_text_length > (gint)entry->text_size - (gint)entry->n_bytes - 1) - { - new_text_length = (gint)entry->text_size - (gint)entry->n_bytes - 1; - new_text_length = g_utf8_find_prev_char (new_text, new_text + new_text_length + 1) - new_text; - n_chars = g_utf8_strlen (new_text, new_text_length); - } - break; - } - } - } + if (n_inserted != n_chars) + gtk_widget_error_bell (GTK_WIDGET (editable)); - if (entry->visible) - entry->text = g_realloc (entry->text, entry->text_size); - else - { - /* Same thing, just slower and without leaving stuff in memory. */ - gchar *et_new = g_malloc (entry->text_size); - memcpy (et_new, entry->text, MIN (prev_size, entry->text_size)); - trash_area (entry->text, prev_size); - g_free (entry->text); - entry->text = et_new; - } - } + *position += n_inserted; +} - index = g_utf8_offset_to_pointer (entry->text, *position) - entry->text; +static void +gtk_entry_real_delete_text (GtkEditable *editable, + gint start_pos, + gint end_pos) +{ + /* + * The actual deletion from the buffer. This will end up firing the + * following signal handlers: buffer_deleted_text(), buffer_notify_display_text(), + * buffer_notify_text(), buffer_notify_length() + */ - g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index); - memcpy (entry->text + index, new_text, new_text_length); + gtk_entry_buffer_delete_text (GTK_ENTRY_GET_PRIVATE (editable)->buffer, start_pos, end_pos - start_pos); +} - entry->n_bytes += new_text_length; - entry->text_length += n_chars; +/* GtkEntryBuffer signal handlers + */ +static void +buffer_inserted_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars, + GtkEntry *entry) +{ + guint password_hint_timeout; - /* NUL terminate for safety and convenience */ - entry->text[entry->n_bytes] = '\0'; - - if (entry->current_pos > *position) + if (entry->current_pos > position) entry->current_pos += n_chars; - - if (entry->selection_bound > *position) + + if (entry->selection_bound > position) entry->selection_bound += n_chars; - if (n_chars == 1 && !entry->visible && (new_text_length < PASSWORD_HINT_MAX)) + /* Calculate the password hint if it needs to be displayed. */ + if (n_chars == 1 && !entry->visible) { - guint password_hint_timeout; - g_object_get (gtk_widget_get_settings (GTK_WIDGET (entry)), "gtk-entry-password-hint-timeout", &password_hint_timeout, NULL); @@ -4503,85 +4581,115 @@ gtk_entry_real_insert_text (GtkEditable *editable, { GtkEntryPasswordHint *password_hint = g_object_get_qdata (G_OBJECT (entry), quark_password_hint); - if (!password_hint) { password_hint = g_slice_new0 (GtkEntryPasswordHint); - g_object_set_qdata_full (G_OBJECT (entry), quark_password_hint, - password_hint, - (GDestroyNotify) gtk_entry_password_hint_free); + g_object_set_qdata_full (G_OBJECT (entry), quark_password_hint, password_hint, + (GDestroyNotify)gtk_entry_password_hint_free); } - memset (&password_hint->password_hint, 0x0, PASSWORD_HINT_MAX); - password_hint->password_hint_length = new_text_length; - memcpy (&password_hint->password_hint, new_text, new_text_length); - password_hint->password_hint_position = *position + n_chars; - } + password_hint->position = position; + if (password_hint->source_id) + g_source_remove (password_hint->source_id); + password_hint->source_id = gdk_threads_add_timeout (password_hint_timeout, + (GSourceFunc)gtk_entry_remove_password_hint, entry); + } } - else +} + +static void +buffer_deleted_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars, + GtkEntry *entry) +{ + guint end_pos = position + n_chars; + gint selection_bound; + guint current_pos; + + current_pos = entry->current_pos; + if (current_pos > position) + current_pos -= MIN (current_pos, end_pos) - position; + + selection_bound = entry->selection_bound; + if (selection_bound > position) + selection_bound -= MIN (selection_bound, end_pos) - position; + + gtk_entry_set_positions (entry, current_pos, selection_bound); + + /* We might have deleted the selection */ + gtk_entry_update_primary_selection (entry); + + /* Disable the password hint if one exists. */ + if (!entry->visible) { - g_object_set_qdata (G_OBJECT (entry), quark_password_hint, NULL); + GtkEntryPasswordHint *password_hint = g_object_get_qdata (G_OBJECT (entry), + quark_password_hint); + if (password_hint) + { + if (password_hint->source_id) + g_source_remove (password_hint->source_id); + password_hint->source_id = 0; + password_hint->position = -1; + } } +} - *position += n_chars; +static void +buffer_notify_text (GtkEntryBuffer *buffer, + GParamSpec *spec, + GtkEntry *entry) +{ + /* COMPAT: Deprecated, not used. This struct field will be removed in GTK+ 3.x */ + entry->text = (gchar*)gtk_entry_buffer_get_text (buffer); gtk_entry_recompute (entry); - emit_changed (entry); - g_object_notify (G_OBJECT (editable), "text"); - g_object_notify (G_OBJECT (editable), "text-length"); + g_object_notify (G_OBJECT (entry), "text"); } static void -gtk_entry_real_delete_text (GtkEditable *editable, - gint start_pos, - gint end_pos) +buffer_notify_length (GtkEntryBuffer *buffer, + GParamSpec *spec, + GtkEntry *entry) { - GtkEntry *entry = GTK_ENTRY (editable); - - if (start_pos < 0) - start_pos = 0; - if (end_pos < 0 || end_pos > entry->text_length) - end_pos = entry->text_length; - - if (start_pos < end_pos) - { - gint start_index = g_utf8_offset_to_pointer (entry->text, start_pos) - entry->text; - gint end_index = g_utf8_offset_to_pointer (entry->text, end_pos) - entry->text; - gint current_pos; - gint selection_bound; - - g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes + 1 - end_index); - entry->text_length -= (end_pos - start_pos); - entry->n_bytes -= (end_index - start_index); + /* COMPAT: Deprecated, not used. This struct field will be removed in GTK+ 3.x */ + entry->text_length = gtk_entry_buffer_get_length (buffer); - /* In password-mode, make sure we don't leave anything sensitive after - * the terminating zero. Note, that the terminating zero already trashed - * one byte. - */ - if (!entry->visible) - trash_area (entry->text + entry->n_bytes + 1, end_index - start_index - 1); - - current_pos = entry->current_pos; - if (current_pos > start_pos) - current_pos -= MIN (current_pos, end_pos) - start_pos; + g_object_notify (G_OBJECT (entry), "text-length"); +} - selection_bound = entry->selection_bound; - if (selection_bound > start_pos) - selection_bound -= MIN (selection_bound, end_pos) - start_pos; +static void +buffer_notify_max_length (GtkEntryBuffer *buffer, + GParamSpec *spec, + GtkEntry *entry) +{ + /* COMPAT: Deprecated, not used. This struct field will be removed in GTK+ 3.x */ + entry->text_max_length = gtk_entry_buffer_get_max_length (buffer); - gtk_entry_set_positions (entry, current_pos, selection_bound); + g_object_notify (G_OBJECT (entry), "max-length"); +} - /* We might have deleted the selection - */ - gtk_entry_update_primary_selection (entry); - - gtk_entry_recompute (entry); +static void +buffer_connect_signals (GtkEntry *entry) +{ + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + g_signal_connect (priv->buffer, "inserted-text", G_CALLBACK (buffer_inserted_text), entry); + g_signal_connect (priv->buffer, "deleted-text", G_CALLBACK (buffer_deleted_text), entry); + g_signal_connect (priv->buffer, "notify::text", G_CALLBACK (buffer_notify_text), entry); + g_signal_connect (priv->buffer, "notify::length", G_CALLBACK (buffer_notify_length), entry); + g_signal_connect (priv->buffer, "notify::max-length", G_CALLBACK (buffer_notify_max_length), entry); +} - emit_changed (entry); - g_object_notify (G_OBJECT (editable), "text"); - g_object_notify (G_OBJECT (editable), "text-length"); - } +static void +buffer_disconnect_signals (GtkEntry *entry) +{ + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + g_signal_handlers_disconnect_by_func (priv->buffer, buffer_inserted_text, entry); + g_signal_handlers_disconnect_by_func (priv->buffer, buffer_deleted_text, entry); + g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_text, entry); + g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_length, entry); + g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_max_length, entry); } /* Compute the X position for an offset that corresponds to the "more important @@ -4622,6 +4730,7 @@ gtk_entry_move_cursor (GtkEntry *entry, gboolean extend_selection) { gint new_pos = entry->current_pos; + GtkEntryPrivate *priv; _gtk_entry_reset_im_context (entry); @@ -4653,7 +4762,8 @@ gtk_entry_move_cursor (GtkEntry *entry, case GTK_MOVEMENT_DISPLAY_LINE_ENDS: case GTK_MOVEMENT_PARAGRAPH_ENDS: case GTK_MOVEMENT_BUFFER_ENDS: - new_pos = count < 0 ? 0 : entry->text_length; + priv = GTK_ENTRY_GET_PRIVATE (entry); + new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (priv->buffer); break; case GTK_MOVEMENT_DISPLAY_LINES: case GTK_MOVEMENT_PARAGRAPHS: @@ -4710,7 +4820,8 @@ gtk_entry_move_cursor (GtkEntry *entry, case GTK_MOVEMENT_DISPLAY_LINE_ENDS: case GTK_MOVEMENT_PARAGRAPH_ENDS: case GTK_MOVEMENT_BUFFER_ENDS: - new_pos = count < 0 ? 0 : entry->text_length; + priv = GTK_ENTRY_GET_PRIVATE (entry); + new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (priv->buffer); if (entry->current_pos == new_pos) gtk_widget_error_bell (GTK_WIDGET (entry)); break; @@ -4752,9 +4863,10 @@ gtk_entry_delete_from_cursor (GtkEntry *entry, gint count) { GtkEditable *editable = GTK_EDITABLE (entry); + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); gint start_pos = entry->current_pos; gint end_pos = entry->current_pos; - gint old_n_bytes = entry->n_bytes; + gint old_n_bytes = gtk_entry_buffer_get_bytes (priv->buffer); _gtk_entry_reset_im_context (entry); @@ -4820,7 +4932,7 @@ gtk_entry_delete_from_cursor (GtkEntry *entry, break; } - if (entry->n_bytes == old_n_bytes) + if (gtk_entry_buffer_get_bytes (priv->buffer) == old_n_bytes) gtk_widget_error_bell (GTK_WIDGET (entry)); gtk_entry_pend_cursor_blink (entry); @@ -4834,7 +4946,7 @@ gtk_entry_backspace (GtkEntry *entry) _gtk_entry_reset_im_context (entry); - if (!entry->editable || !entry->text) + if (!entry->editable) { gtk_widget_error_bell (GTK_WIDGET (entry)); return; @@ -4856,16 +4968,15 @@ gtk_entry_backspace (GtkEntry *entry) pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - if (entry->visible && - log_attrs[entry->current_pos].backspace_deletes_character) + /* Deleting parts of characters */ + if (log_attrs[entry->current_pos].backspace_deletes_character) { gchar *cluster_text; gchar *normalized_text; glong len; - cluster_text = gtk_editable_get_chars (editable, - prev_pos, - entry->current_pos); + cluster_text = gtk_entry_get_display_text (entry, prev_pos, + entry->current_pos); normalized_text = g_utf8_normalize (cluster_text, strlen (cluster_text), G_NORMALIZE_NFD); @@ -4915,7 +5026,7 @@ gtk_entry_copy_clipboard (GtkEntry *entry) return; } - str = gtk_entry_get_public_chars (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); @@ -5053,10 +5164,13 @@ static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context, GtkEntry *entry) { - gtk_im_context_set_surrounding (context, - entry->text, - entry->n_bytes, - g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text); + 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); + gtk_im_context_set_surrounding (context, text, strlen (text), /* Length in bytes */ + g_utf8_offset_to_pointer (text, entry->current_pos) - text); + g_free (text); return TRUE; } @@ -5215,34 +5329,6 @@ gtk_entry_recompute (GtkEntry *entry) } } -static void -append_char (GString *str, - gunichar ch, - gint count) -{ - gint i; - gint char_len; - gchar buf[7]; - - char_len = g_unichar_to_utf8 (ch, buf); - - i = 0; - while (i < count) - { - g_string_append_len (str, buf, char_len); - ++i; - } -} - -static gboolean -gtk_entry_remove_password_hint (gpointer data) -{ - /* Force the string to be redrawn, but now without a visible character */ - gtk_entry_recompute (GTK_ENTRY (data)); - - return FALSE; -} - static PangoLayout * gtk_entry_create_layout (GtkEntry *entry, gboolean include_preedit) @@ -5250,13 +5336,19 @@ gtk_entry_create_layout (GtkEntry *entry, GtkWidget *widget = GTK_WIDGET (entry); PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL); PangoAttrList *tmp_attrs = pango_attr_list_new (); - + gchar *preedit_string = NULL; gint preedit_length = 0; PangoAttrList *preedit_attrs = NULL; + gchar *display; + guint n_bytes; + pango_layout_set_single_paragraph_mode (layout, TRUE); + display = gtk_entry_get_display_text (entry, 0, -1); + n_bytes = strlen (display); + if (include_preedit) { gtk_im_context_get_preedit_string (entry->im_context, @@ -5266,32 +5358,10 @@ gtk_entry_create_layout (GtkEntry *entry, if (preedit_length) { - GString *tmp_string = g_string_new (NULL); + GString *tmp_string = g_string_new (display); + gint cursor_index = g_utf8_offset_to_pointer (display, entry->current_pos) - display; - gint cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text; - - if (entry->visible) - { - g_string_prepend_len (tmp_string, entry->text, entry->n_bytes); - g_string_insert (tmp_string, cursor_index, preedit_string); - } - else - { - gint ch_len; - gunichar invisible_char; - - if (entry->invisible_char != 0) - invisible_char = entry->invisible_char; - else - invisible_char = ' '; /* just pick a char */ - - ch_len = g_utf8_strlen (entry->text, entry->n_bytes); - append_char (tmp_string, invisible_char, ch_len); - cursor_index = - g_utf8_offset_to_pointer (tmp_string->str, entry->current_pos) - - tmp_string->str; - g_string_insert (tmp_string, cursor_index, preedit_string); - } + g_string_insert (tmp_string, cursor_index, preedit_string); pango_layout_set_text (layout, tmp_string->str, tmp_string->len); @@ -5304,9 +5374,8 @@ gtk_entry_create_layout (GtkEntry *entry, { PangoDirection pango_dir; - if (entry->visible) - pango_dir = pango_find_base_dir (entry->text, entry->n_bytes); - + if (gtk_entry_get_display_mode (entry) == DISPLAY_NORMAL) + pango_dir = pango_find_base_dir (display, n_bytes); else pango_dir = PANGO_DIRECTION_NEUTRAL; @@ -5334,78 +5403,15 @@ gtk_entry_create_layout (GtkEntry *entry, pango_dir); entry->resolved_dir = pango_dir; - - if (entry->visible) - { - pango_layout_set_text (layout, entry->text, entry->n_bytes); - } - else - { - GString *str = g_string_new (NULL); - gunichar invisible_char; - guint password_hint_timeout; - GtkEntryPasswordHint *password_hint; - - g_object_get (gtk_widget_get_settings (widget), - "gtk-entry-password-hint-timeout", &password_hint_timeout, - NULL); - - if (entry->invisible_char != 0) - invisible_char = entry->invisible_char; - else - invisible_char = ' '; /* just pick a char */ - - password_hint = g_object_get_qdata (G_OBJECT (entry), - quark_password_hint); - if (password_hint && password_hint->password_hint_timeout_id) - { - g_source_remove (password_hint->password_hint_timeout_id); - password_hint->password_hint_timeout_id = 0; - } - - if (password_hint_timeout == 0 || password_hint == NULL || - (password_hint && password_hint->password_hint_length == 0)) - { - append_char (str, invisible_char, entry->text_length); - } - else if (password_hint) - { - /* Draw hidden characters upto the inserted position, - * then the real thing, pad up to full length - */ - if (password_hint->password_hint_position > 1) - append_char (str, invisible_char, - password_hint->password_hint_position - 1); - - g_string_append_len (str, password_hint->password_hint, - password_hint->password_hint_length); - - if (password_hint->password_hint_position < entry->text_length) - append_char (str, invisible_char, - entry->text_length - - password_hint->password_hint_position); - - /* Now remove this last input character, don't need - * it anymore - */ - memset (password_hint->password_hint, 0, PASSWORD_HINT_MAX); - password_hint->password_hint_length = 0; - - password_hint->password_hint_timeout_id = - gdk_threads_add_timeout (password_hint_timeout, - (GSourceFunc) gtk_entry_remove_password_hint, - entry); - } - - pango_layout_set_text (layout, str->str, str->len); - g_string_free (str, TRUE); - } + pango_layout_set_text (layout, display, n_bytes); } pango_layout_set_attributes (layout, tmp_attrs); g_free (preedit_string); + g_free (display); + if (preedit_attrs) pango_attr_list_unref (preedit_attrs); @@ -5543,7 +5549,8 @@ gtk_entry_draw_text (GtkEntry *entry) GtkWidget *widget = GTK_WIDGET (entry); cairo_t *cr; - if (!entry->visible && entry->invisible_char == 0) + /* Nothing to display at all */ + if (gtk_entry_get_display_mode (entry) == DISPLAY_BLANK) return; if (GTK_WIDGET_DRAWABLE (entry)) @@ -5809,7 +5816,11 @@ gtk_entry_get_cursor_locations (GtkEntry *entry, gint *strong_x, gint *weak_x) { - if (!entry->visible && !entry->invisible_char) + DisplayMode mode = gtk_entry_get_display_mode (entry); + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + + /* Nothing to display at all, so no cursor is relevant */ + if (mode == DISPLAY_BLANK) { if (strong_x) *strong_x = 0; @@ -5834,11 +5845,11 @@ gtk_entry_get_cursor_locations (GtkEntry *entry, if (entry->dnd_position > entry->current_pos) { - if (entry->visible) + if (mode == DISPLAY_NORMAL) index += entry->preedit_length; else { - gint preedit_len_chars = g_utf8_strlen (text, -1) - entry->text_length; + gint preedit_len_chars = g_utf8_strlen (text, -1) - gtk_entry_buffer_get_length (priv->buffer); index += preedit_len_chars * g_unichar_to_utf8 (entry->invisible_char, NULL); } } @@ -6042,14 +6053,18 @@ gtk_entry_move_logically (GtkEntry *entry, gint start, gint count) { + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); gint new_pos = start; + guint length; + + length = gtk_entry_buffer_get_length (priv->buffer); /* Prevent any leak of information */ - if (!entry->visible) + if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL) { - new_pos = CLAMP (start + count, 0, entry->text_length); + new_pos = CLAMP (start + count, 0, length); } - else if (entry->text) + else { PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); PangoLogAttr *log_attrs; @@ -6057,11 +6072,11 @@ gtk_entry_move_logically (GtkEntry *entry, pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - while (count > 0 && new_pos < entry->text_length) + while (count > 0 && new_pos < length) { do new_pos++; - while (new_pos < entry->text_length && !log_attrs[new_pos].is_cursor_position); + while (new_pos < length && !log_attrs[new_pos].is_cursor_position); count--; } @@ -6085,14 +6100,18 @@ gtk_entry_move_forward_word (GtkEntry *entry, gint start, gboolean allow_whitespace) { + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); gint new_pos = start; + guint length; + + length = gtk_entry_buffer_get_length (priv->buffer); /* Prevent any leak of information */ - if (!entry->visible) + if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL) { - new_pos = entry->text_length; + new_pos = length; } - else if (entry->text && (new_pos < entry->text_length)) + else if (new_pos < length) { PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); PangoLogAttr *log_attrs; @@ -6121,11 +6140,11 @@ gtk_entry_move_backward_word (GtkEntry *entry, gint new_pos = start; /* Prevent any leak of information */ - if (!entry->visible) + if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL) { new_pos = 0; } - else if (entry->text && start > 0) + else if (start > 0) { PangoLayout *layout = gtk_entry_ensure_layout (entry, FALSE); PangoLogAttr *log_attrs; @@ -6186,30 +6205,6 @@ gtk_entry_select_line (GtkEntry *entry) gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); } -/* - * Like gtk_editable_get_chars, but handle not-visible entries - * correctly. - */ -static char * -gtk_entry_get_public_chars (GtkEntry *entry, - gint start, - gint end) -{ - if (end < 0) - end = entry->text_length; - - if (entry->visible) - return gtk_editable_get_chars (GTK_EDITABLE (entry), start, end); - else if (!entry->invisible_char) - return g_strdup (""); - else - { - GString *str = g_string_new (NULL); - append_char (str, entry->invisible_char, end - start); - return g_string_free (str, FALSE); - } -} - static gint truncate_multiline (const gchar *text) { @@ -6253,7 +6248,8 @@ paste_received (GtkClipboard *clipboard, length = truncate_multiline (text); /* only complete if the selection is at the end */ - popup_completion = (entry->text_length == MAX (entry->current_pos, entry->selection_bound)); + popup_completion = (gtk_entry_buffer_get_length (priv->buffer) == + MAX (entry->current_pos, entry->selection_bound)); if (completion) { @@ -6303,7 +6299,7 @@ primary_get_cb (GtkClipboard *clipboard, if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end)) { - gchar *str = gtk_entry_get_public_chars (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); } @@ -6546,6 +6542,23 @@ gtk_entry_new (void) } /** + * gtk_entry_new_with_buffer: + * @buffer: The buffer to use for the new #GtkEntry. + * + * Creates a new entry with the specified text buffer. + * + * Return value: a new #GtkEntry + * + * Since: 2.18 + */ +GtkWidget* +gtk_entry_new_with_buffer (GtkEntryBuffer *buffer) +{ + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), NULL); + return g_object_new (GTK_TYPE_ENTRY, "buffer", buffer, NULL); +} + +/** * gtk_entry_new_with_max_length: * @max: the maximum length of the entry, or 0 for no maximum. * (other than the maximum length of entries.) The value passed in will @@ -6562,36 +6575,119 @@ gtk_entry_new_with_max_length (gint max) { GtkEntry *entry; - max = CLAMP (max, 0, MAX_SIZE); + max = CLAMP (max, 0, GTK_ENTRY_BUFFER_MAX_SIZE); entry = g_object_new (GTK_TYPE_ENTRY, NULL); - entry->text_max_length = max; + gtk_entry_buffer_set_max_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer, max); return GTK_WIDGET (entry); } /** + * gtk_entry_get_buffer: + * @entry: a #GtkEntry + * + * Get the #GtkEntryBuffer object which holds the text for + * this widget. + * + * Since: 2.18 + * + * Returns: A #GtkEntryBuffer object. + */ +GtkEntryBuffer* +gtk_entry_get_buffer (GtkEntry *entry) +{ + g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); + return GTK_ENTRY_GET_PRIVATE (entry)->buffer; +} + +/** + * gtk_entry_set_buffer: + * @entry: a #GtkEntry + * @buffer: a #GtkEntryBuffer + * + * Set the #GtkEntryBuffer object which holds the text for + * this widget. + * + * Since: 2.18 + */ +void +gtk_entry_set_buffer (GtkEntry *entry, + GtkEntryBuffer *buffer) +{ + GtkEntryPrivate *priv; + GObject *obj; + + g_return_if_fail (GTK_IS_ENTRY (entry)); + priv = GTK_ENTRY_GET_PRIVATE (entry); + + if (buffer) + { + g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer)); + g_object_ref (buffer); + } + + if (priv->buffer) + { + buffer_disconnect_signals (entry); + g_object_unref (priv->buffer); + + /* COMPAT: Deprecated. Not used. Setting these fields no longer necessary in GTK 3.x */ + entry->text = NULL; + entry->text_length = 0; + entry->text_max_length = 0; + } + + priv->buffer = buffer; + + if (priv->buffer) + { + buffer_connect_signals (entry); + + /* COMPAT: Deprecated. Not used. Setting these fields no longer necessary in GTK 3.x */ + entry->text = (char*)gtk_entry_buffer_get_text (priv->buffer); + entry->text_length = gtk_entry_buffer_get_length (priv->buffer); + entry->text_max_length = gtk_entry_buffer_get_max_length (priv->buffer); + } + + obj = G_OBJECT (entry); + g_object_freeze_notify (obj); + g_object_notify (obj, "buffer"); + g_object_notify (obj, "text"); + g_object_notify (obj, "text-length"); + g_object_notify (obj, "max-length"); + g_object_notify (obj, "visibility"); + g_object_notify (obj, "invisible-char"); + g_object_notify (obj, "invisible-char-set"); + g_object_thaw_notify (obj); +} + + +/** * gtk_entry_set_text: * @entry: a #GtkEntry * @text: the new text * * Sets the text in the widget to the given * value, replacing the current contents. + * + * See gtk_entry_buffer_set_text(). */ void gtk_entry_set_text (GtkEntry *entry, const gchar *text) { - gint tmp_pos; GtkEntryCompletion *completion; + GtkEntryPrivate *priv; g_return_if_fail (GTK_IS_ENTRY (entry)); g_return_if_fail (text != NULL); + priv = GTK_ENTRY_GET_PRIVATE (entry); /* Actually setting the text will affect the cursor and selection; * if the contents don't actually change, this will look odd to the user. */ - if (strcmp (entry->text, text) == 0) + if (strcmp (gtk_entry_buffer_get_text (priv->buffer), text) == 0) return; completion = gtk_entry_get_completion (entry); @@ -6600,10 +6696,7 @@ gtk_entry_set_text (GtkEntry *entry, begin_change (entry); g_object_freeze_notify (G_OBJECT (entry)); - gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1); - - tmp_pos = 0; - gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos); + gtk_entry_buffer_set_text (priv->buffer, text, -1); g_object_thaw_notify (G_OBJECT (entry)); end_change (entry); @@ -6624,12 +6717,14 @@ void gtk_entry_append_text (GtkEntry *entry, const gchar *text) { + GtkEntryPrivate *priv; gint tmp_pos; g_return_if_fail (GTK_IS_ENTRY (entry)); g_return_if_fail (text != NULL); + priv = GTK_ENTRY_GET_PRIVATE (entry); - tmp_pos = entry->text_length; + tmp_pos = gtk_entry_buffer_get_length (priv->buffer); gtk_editable_insert_text (GTK_EDITABLE (entry), text, -1, &tmp_pos); } @@ -6886,6 +6981,12 @@ gtk_entry_get_overwrite_mode (GtkEntry *entry) * Retrieves the contents of the entry widget. * See also gtk_editable_get_chars(). * + * This is equivalent to: + * + * <informalexample><programlisting> + * gtk_entry_buffer_get_text (gtk_entry_get_buffer (entry)); + * </programlisting></informalexample> + * * Return value: a pointer to the contents of the widget as a * string. This string points to internally allocated * storage in the widget and must not be freed, modified or @@ -6895,8 +6996,7 @@ G_CONST_RETURN gchar* gtk_entry_get_text (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); - - return entry->text; + return gtk_entry_buffer_get_text (GTK_ENTRY_GET_PRIVATE (entry)->buffer); } /** @@ -6931,20 +7031,19 @@ gtk_entry_select_region (GtkEntry *entry, * Sets the maximum allowed length of the contents of the widget. If * the current contents are longer than the given length, then they * will be truncated to fit. + * + * This is equivalent to: + * + * <informalexample><programlisting> + * gtk_entry_buffer_set_max_length (gtk_entry_get_buffer (entry), max); + * </programlisting></informalexample> **/ void gtk_entry_set_max_length (GtkEntry *entry, gint max) { g_return_if_fail (GTK_IS_ENTRY (entry)); - - max = CLAMP (max, 0, MAX_SIZE); - - if (max > 0 && entry->text_length > max) - gtk_editable_delete_text (GTK_EDITABLE (entry), max, -1); - - entry->text_max_length = max; - g_object_notify (G_OBJECT (entry), "max-length"); + gtk_entry_buffer_set_max_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer, max); } /** @@ -6954,6 +7053,12 @@ gtk_entry_set_max_length (GtkEntry *entry, * Retrieves the maximum allowed length of the text in * @entry. See gtk_entry_set_max_length(). * + * This is equivalent to: + * + * <informalexample><programlisting> + * gtk_entry_buffer_get_max_length (gtk_entry_get_buffer (entry)); + * </programlisting></informalexample> + * * Return value: the maximum allowed number of characters * in #GtkEntry, or 0 if there is no maximum. **/ @@ -6961,8 +7066,7 @@ gint gtk_entry_get_max_length (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), 0); - - return entry->text_max_length; + return gtk_entry_buffer_get_max_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer); } /** @@ -6972,6 +7076,12 @@ gtk_entry_get_max_length (GtkEntry *entry) * Retrieves the current length of the text in * @entry. * + * This is equivalent to: + * + * <informalexample><programlisting> + * gtk_entry_buffer_get_length (gtk_entry_get_buffer (entry)); + * </programlisting></informalexample> + * * Return value: the current number of characters * in #GtkEntry, or 0 if there are none. * @@ -6981,8 +7091,7 @@ guint16 gtk_entry_get_text_length (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), 0); - - return entry->text_length; + return gtk_entry_buffer_get_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer); } /** @@ -8390,6 +8499,7 @@ popup_targets_received (GtkClipboard *clipboard, if (GTK_WIDGET_REALIZED (entry)) { + DisplayMode mode; gboolean clipboard_contains_text; GtkWidget *menuitem; GtkWidget *submenu; @@ -8406,13 +8516,18 @@ popup_targets_received (GtkClipboard *clipboard, GTK_WIDGET (entry), popup_menu_detach); + mode = gtk_entry_get_display_mode (entry); append_action_signal (entry, entry->popup_menu, GTK_STOCK_CUT, "cut-clipboard", - entry->editable && entry->visible && entry->current_pos != entry->selection_bound); + entry->editable && mode == DISPLAY_NORMAL && + entry->current_pos != entry->selection_bound); + append_action_signal (entry, entry->popup_menu, GTK_STOCK_COPY, "copy-clipboard", - entry->visible && entry->current_pos != entry->selection_bound); + mode == DISPLAY_NORMAL && + entry->current_pos != entry->selection_bound); + append_action_signal (entry, entry->popup_menu, GTK_STOCK_PASTE, "paste-clipboard", entry->editable && clipboard_contains_text); - + menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL); gtk_widget_set_sensitive (menuitem, entry->editable && entry->current_pos != entry->selection_bound); g_signal_connect_swapped (menuitem, "activate", @@ -8761,7 +8876,7 @@ gtk_entry_drag_data_get (GtkWidget *widget, if (gtk_editable_get_selection_bounds (editable, &sel_start, &sel_end)) { - gchar *str = gtk_entry_get_public_chars (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); @@ -9370,7 +9485,7 @@ accept_completion_callback (GtkEntry *entry) if (completion->priv->has_completion) gtk_editable_set_position (GTK_EDITABLE (entry), - entry->text_length); + gtk_entry_buffer_get_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer)); return FALSE; } @@ -9823,7 +9938,7 @@ keymap_state_changed (GdkKeymap *keymap, GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); char *text = NULL; - if (!entry->visible && priv->caps_lock_warning) + if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL && priv->caps_lock_warning) { if (gdk_keymap_get_caps_lock_state (keymap)) text = _("Caps Lock is on"); diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h index be0444752b..d7f4bfb0f5 100644 --- a/gtk/gtkentry.h +++ b/gtk/gtkentry.h @@ -39,6 +39,7 @@ #include <gtk/gtkeditable.h> #include <gtk/gtkimcontext.h> #include <gtk/gtkmenu.h> +#include <gtk/gtkentrybuffer.h> #include <gtk/gtkentrycompletion.h> #include <gtk/gtkimage.h> #include <gtk/gtkselection.h> @@ -66,7 +67,7 @@ struct _GtkEntry { GtkWidget widget; - gchar *GSEAL (text); + gchar *GSEAL (text); /* COMPAT: Deprecated, not used. Remove in GTK+ 3.x */ guint GSEAL (editable) : 1; guint GSEAL (visible) : 1; @@ -74,8 +75,8 @@ struct _GtkEntry guint GSEAL (in_drag) : 1; /* FIXME: Should be private? Dragging within the selection */ - guint16 GSEAL (text_length); /* length in use, in chars */ - guint16 GSEAL (text_max_length); + guint16 GSEAL (text_length); /* COMPAT: Deprecated, not used. Remove in GTK+ 3.x */ + guint16 GSEAL (text_max_length); /* COMPAT: Deprecated, not used. Remove in GTK+ 3.x */ /*< private >*/ GdkWindow *GSEAL (text_area); @@ -108,8 +109,8 @@ struct _GtkEntry gint GSEAL (ascent); /* font ascent in pango units */ gint GSEAL (descent); /* font descent in pango units */ - guint16 GSEAL (text_size); /* allocated size, in bytes */ - guint16 GSEAL (n_bytes); /* length in use, in bytes */ + guint16 GSEAL (x_text_size); /* allocated size, in bytes */ + guint16 GSEAL (x_n_bytes); /* length in use, in bytes */ guint16 GSEAL (preedit_length); /* length of preedit string, in bytes */ guint16 GSEAL (preedit_cursor); /* offset of cursor within preedit string, in chars */ @@ -164,6 +165,10 @@ struct _GtkEntryClass GType gtk_entry_get_type (void) G_GNUC_CONST; GtkWidget* gtk_entry_new (void); +GtkWidget* gtk_entry_new_with_buffer (GtkEntryBuffer *buffer); +GtkEntryBuffer* gtk_entry_get_buffer (GtkEntry *entry); +void gtk_entry_set_buffer (GtkEntry *entry, + GtkEntryBuffer *buffer); void gtk_entry_set_visibility (GtkEntry *entry, gboolean visible); gboolean gtk_entry_get_visibility (GtkEntry *entry); diff --git a/gtk/gtkentrybuffer.c b/gtk/gtkentrybuffer.c new file mode 100644 index 0000000000..7c6d2186a5 --- /dev/null +++ b/gtk/gtkentrybuffer.c @@ -0,0 +1,741 @@ +/* gtkentrybuffer.c + * Copyright (C) 2009 Stefan Walter <stef@memberwebs.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +#include "config.h" + +#include "gtkentrybuffer.h" +#include "gtkintl.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtkwidget.h" + +#include <gdk/gdk.h> + +#include <string.h> + +/** + * SECTION:gtkentrybuffer + * @title: GtkEntryBuffer + * @short_description: Text buffer for GtkEntry + * + * The #GtkEntryBuffer class contains the actual text displayed in a + * #GtkEntry widget. + * + * A single #GtkEntryBuffer object can be shared by multiple #GtkEntry + * widgets which will then share the same text content, but not the cursor + * position, visibility attributes, icon etc. + * + * #GtkEntryBuffer may be derived from. Such a derived class might allow + * text to be stored in an alternate location, such as non-pageable memory, + * useful in the case of important passwords. Or a derived class could + * integrate with an application's concept of undo/redo. + * + * Since: 2.18 + */ + +/* Initial size of buffer, in bytes */ +#define MIN_SIZE 16 + +enum { + PROP_0, + PROP_TEXT, + PROP_LENGTH, + PROP_MAX_LENGTH, +}; + +enum { + INSERTED_TEXT, + DELETED_TEXT, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct _GtkEntryBufferPrivate +{ + guint max_length; + + /* Only valid if this class is not derived */ + gchar *normal_text; + gsize normal_text_size; + gsize normal_text_bytes; + guint normal_text_chars; +}; + +G_DEFINE_TYPE (GtkEntryBuffer, gtk_entry_buffer, G_TYPE_OBJECT); + +/* -------------------------------------------------------------------------------- + * DEFAULT IMPLEMENTATIONS OF TEXT BUFFER + * + * These may be overridden by a derived class, behavior may be changed etc... + * The normal_text and normal_text_xxxx fields may not be valid when + * this class is derived from. + */ + +/* Overwrite a memory that might contain sensitive information. */ +static void +trash_area (gchar *area, + gsize len) +{ + volatile gchar *varea = (volatile gchar *)area; + while (len-- > 0) + *varea++ = 0; +} + +static const gchar* +gtk_entry_buffer_normal_get_text (GtkEntryBuffer *buffer, + gsize *n_bytes) +{ + if (n_bytes) + *n_bytes = buffer->priv->normal_text_bytes; + if (!buffer->priv->normal_text) + return ""; + return buffer->priv->normal_text; +} + +static guint +gtk_entry_buffer_normal_get_length (GtkEntryBuffer *buffer) +{ + return buffer->priv->normal_text_chars; +} + +static guint +gtk_entry_buffer_normal_insert_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars) +{ + GtkEntryBufferPrivate *pv = buffer->priv; + gsize prev_size; + gsize n_bytes; + gsize at; + + n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars; + + /* Need more memory */ + if (n_bytes + pv->normal_text_bytes + 1 > pv->normal_text_size) + { + prev_size = pv->normal_text_size; + + /* Calculate our new buffer size */ + while (n_bytes + pv->normal_text_bytes + 1 > pv->normal_text_size) + { + if (pv->normal_text_size == 0) + pv->normal_text_size = MIN_SIZE; + else + { + if (2 * pv->normal_text_size < GTK_ENTRY_BUFFER_MAX_SIZE) + pv->normal_text_size *= 2; + else + { + pv->normal_text_size = GTK_ENTRY_BUFFER_MAX_SIZE; + if (n_bytes > pv->normal_text_size - pv->normal_text_bytes - 1) + { + n_bytes = pv->normal_text_size - pv->normal_text_bytes - 1; + n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars; + n_chars = g_utf8_strlen (chars, n_bytes); + } + break; + } + } + } + + /* Could be a password, so can't leave stuff in memory. */ + gchar *et_new = g_malloc (pv->normal_text_size); + memcpy (et_new, pv->normal_text, MIN (prev_size, pv->normal_text_size)); + trash_area (pv->normal_text, prev_size); + g_free (pv->normal_text); + pv->normal_text = et_new; + } + + /* Actual text insertion */ + at = g_utf8_offset_to_pointer (pv->normal_text, position) - pv->normal_text; + g_memmove (pv->normal_text + at + n_bytes, pv->normal_text + at, pv->normal_text_bytes - at); + memcpy (pv->normal_text + at, chars, n_bytes); + + /* Book keeping */ + pv->normal_text_bytes += n_bytes; + pv->normal_text_chars += n_chars; + pv->normal_text[pv->normal_text_bytes] = '\0'; + + gtk_entry_buffer_emit_inserted_text (buffer, position, chars, n_chars); + return n_chars; +} + +static guint +gtk_entry_buffer_normal_delete_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars) +{ + GtkEntryBufferPrivate *pv = buffer->priv; + gsize start, end; + + if (position > pv->normal_text_chars) + position = pv->normal_text_chars; + if (position + n_chars > pv->normal_text_chars) + n_chars = pv->normal_text_chars - position; + + if (n_chars > 0) + { + start = g_utf8_offset_to_pointer (pv->normal_text, position) - pv->normal_text; + end = g_utf8_offset_to_pointer (pv->normal_text, position + n_chars) - pv->normal_text; + + g_memmove (pv->normal_text + start, pv->normal_text + end, pv->normal_text_bytes + 1 - end); + pv->normal_text_chars -= n_chars; + pv->normal_text_bytes -= (end - start); + + /* + * Could be a password, make sure we don't leave anything sensitive after + * the terminating zero. Note, that the terminating zero already trashed + * one byte. + */ + trash_area (pv->normal_text + pv->normal_text_bytes + 1, end - start - 1); + + gtk_entry_buffer_emit_deleted_text (buffer, position, n_chars); + } + + return n_chars; +} + +/* -------------------------------------------------------------------------------- + * + */ + +static void +gtk_entry_buffer_real_inserted_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars) +{ + g_object_notify (G_OBJECT (buffer), "text"); + g_object_notify (G_OBJECT (buffer), "length"); +} + +static void +gtk_entry_buffer_real_deleted_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars) +{ + g_object_notify (G_OBJECT (buffer), "text"); + g_object_notify (G_OBJECT (buffer), "length"); +} + +/* -------------------------------------------------------------------------------- + * + */ + +static void +gtk_entry_buffer_init (GtkEntryBuffer *buffer) +{ + GtkEntryBufferPrivate *pv; + + pv = buffer->priv = G_TYPE_INSTANCE_GET_PRIVATE (buffer, GTK_TYPE_ENTRY_BUFFER, GtkEntryBufferPrivate); + + pv->normal_text = NULL; + pv->normal_text_chars = 0; + pv->normal_text_bytes = 0; + pv->normal_text_size = 0; +} + +static void +gtk_entry_buffer_finalize (GObject *obj) +{ + GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj); + GtkEntryBufferPrivate *pv = buffer->priv; + + if (pv->normal_text) + { + trash_area (pv->normal_text, pv->normal_text_size); + g_free (pv->normal_text); + pv->normal_text = NULL; + pv->normal_text_bytes = pv->normal_text_size = 0; + pv->normal_text_chars = 0; + } + + G_OBJECT_CLASS (gtk_entry_buffer_parent_class)->finalize (obj); +} + +static void +gtk_entry_buffer_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj); + + switch (prop_id) + { + case PROP_TEXT: + gtk_entry_buffer_set_text (buffer, g_value_get_string (value), -1); + break; + case PROP_MAX_LENGTH: + gtk_entry_buffer_set_max_length (buffer, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gtk_entry_buffer_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj); + + switch (prop_id) + { + case PROP_TEXT: + g_value_set_string (value, gtk_entry_buffer_get_text (buffer)); + break; + case PROP_LENGTH: + g_value_set_uint (value, gtk_entry_buffer_get_length (buffer)); + break; + case PROP_MAX_LENGTH: + g_value_set_uint (value, gtk_entry_buffer_get_max_length (buffer)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gtk_entry_buffer_class_init (GtkEntryBufferClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gtk_entry_buffer_finalize; + gobject_class->set_property = gtk_entry_buffer_set_property; + gobject_class->get_property = gtk_entry_buffer_get_property; + + klass->get_text = gtk_entry_buffer_normal_get_text; + klass->get_length = gtk_entry_buffer_normal_get_length; + klass->insert_text = gtk_entry_buffer_normal_insert_text; + klass->delete_text = gtk_entry_buffer_normal_delete_text; + + klass->inserted_text = gtk_entry_buffer_real_inserted_text; + klass->deleted_text = gtk_entry_buffer_real_deleted_text; + + g_type_class_add_private (gobject_class, sizeof (GtkEntryBufferPrivate)); + + /** + * GtkEntryBuffer:text: + * + * The contents of the buffer. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, PROP_TEXT, + g_param_spec_string ("text", P_("Text"), + P_("The contents of the buffer"), + "", GTK_PARAM_READWRITE)); + + /** + * GtkEntryBuffer:length: + * + * The length (in characters) of the text in buffer. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, PROP_LENGTH, + g_param_spec_uint ("length", P_("Text length"), + P_("Length of the text currently in the buffer"), + 0, GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READABLE)); + + /** + * GtkEntryBuffer:max-length: + * + * The maximum length (in characters) of the text in the buffer. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, + g_param_spec_uint ("max-length", P_("Maximum length"), + P_("Maximum number of characters for this entry. Zero if no maximum"), + 0, GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READWRITE)); + + /** + * GtkEntry::inserted-text: + * @buffer: a #GtkEntryBuffer + * @position: the position the text was inserted at. + * @chars: The text that was inserted. + * @n_chars: The number of characters that were inserted. + * + * This signal is emitted after text is inserted into the buffer. + * + * Since: 2.18 + */ + signals[INSERTED_TEXT] = g_signal_new (I_("inserted-text"), + GTK_TYPE_ENTRY_BUFFER, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkEntryBufferClass, inserted_text), + NULL, NULL, + _gtk_marshal_VOID__UINT_STRING_UINT, + G_TYPE_NONE, 3, + G_TYPE_UINT, + G_TYPE_STRING, + G_TYPE_UINT); + + /** + * GtkEntry::deleted-text: + * @buffer: a #GtkEntryBuffer + * @position: the position the text was deleted at. + * @n_chars: The number of characters that were deleted. + * + * This signal is emitted after text is deleted from the buffer. + * + * Since: 2.18 + */ + signals[DELETED_TEXT] = g_signal_new (I_("deleted-text"), + GTK_TYPE_ENTRY_BUFFER, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkEntryBufferClass, deleted_text), + NULL, NULL, + _gtk_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, + G_TYPE_UINT); +} + +/* -------------------------------------------------------------------------------- + * + */ + +/** + * gtk_entry_buffer_new: + * @initial_chars: initial buffer text, or %NULL + * @n_initial_chars: number of characters in @initial_chars, or -1 + * + * Create a new GtkEntryBuffer object. + * + * Optionally, specify initial text to set in the buffer. + * + * Return value: A new GtkEntryBuffer object. + * + * Since: 2.18 + **/ +GtkEntryBuffer* +gtk_entry_buffer_new (const gchar *initial_chars, + gint n_initial_chars) +{ + GtkEntryBuffer *buffer = g_object_new (GTK_TYPE_ENTRY_BUFFER, NULL); + if (initial_chars) + gtk_entry_buffer_set_text (buffer, initial_chars, n_initial_chars); + return buffer; +} + +/** + * gtk_entry_buffer_get_length: + * @buffer: a #GtkEntryBuffer + * + * Retrieves the length in characters of the buffer. + * + * Return value: The number of characters in the buffer. + * + * Since: 2.18 + **/ +guint +gtk_entry_buffer_get_length (GtkEntryBuffer *buffer) +{ + GtkEntryBufferClass *klass; + + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0); + + klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer); + g_return_val_if_fail (klass->get_length != NULL, 0); + + return (*klass->get_length) (buffer); +} + +/** + * gtk_entry_buffer_get_bytes: + * @buffer: a #GtkEntryBuffer + * + * Retrieves the length in bytes of the buffer. + * See gtk_entry_buffer_get_length(). + * + * Return value: The byte length of the buffer. + * + * Since: 2.18 + **/ +gsize +gtk_entry_buffer_get_bytes (GtkEntryBuffer *buffer) +{ + GtkEntryBufferClass *klass; + gsize bytes = 0; + + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0); + + klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer); + g_return_val_if_fail (klass->get_text != NULL, 0); + + (*klass->get_text) (buffer, &bytes); + return bytes; +} + +/** + * gtk_entry_buffer_get_text: + * @buffer: a #GtkEntryBuffer + * + * Retrieves the contents of the buffer. + * + * The memory pointer returned by this call will not change + * unless this object emits a signal, or is finalized. + * + * Return value: a pointer to the contents of the widget as a + * string. This string points to internally allocated + * storage in the buffer and must not be freed, modified or + * stored. + * + * Since: 2.18 + **/ +const gchar* +gtk_entry_buffer_get_text (GtkEntryBuffer *buffer) +{ + GtkEntryBufferClass *klass; + + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), NULL); + + klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer); + g_return_val_if_fail (klass->get_text != NULL, NULL); + + return (*klass->get_text) (buffer, NULL); +} + +/** + * gtk_entry_buffer_set_text: + * @buffer: a #GtkEntryBuffer + * @chars: the new text + * @n_chars: the number of characters in @text, or -1 + * + * Sets the text in the buffer. + * + * This is roughly equivalent to calling gtk_entry_buffer_delete_text() + * and gtk_entry_buffer_insert_text(). + * + * Note that @n_chars is in characters, not in bytes. + * + * Since: 2.18 + **/ +void +gtk_entry_buffer_set_text (GtkEntryBuffer *buffer, + const gchar *chars, + gint n_chars) +{ + g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer)); + g_return_if_fail (chars != NULL); + + g_object_freeze_notify (G_OBJECT (buffer)); + gtk_entry_buffer_delete_text (buffer, 0, -1); + gtk_entry_buffer_insert_text (buffer, 0, chars, n_chars); + g_object_thaw_notify (G_OBJECT (buffer)); +} + +/** + * gtk_entry_buffer_set_max_length: + * @buffer: a #GtkEntryBuffer + * @max_length: the maximum length of the entry buffer, or 0 for no maximum. + * (other than the maximum length of entries.) The value passed in will + * be clamped to the range 0-65536. + * + * Sets the maximum allowed length of the contents of the buffer. If + * the current contents are longer than the given length, then they + * will be truncated to fit. + * + * Since: 2.18 + **/ +void +gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer, + guint max_length) +{ + g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer)); + + if (max_length > GTK_ENTRY_BUFFER_MAX_SIZE) + max_length = GTK_ENTRY_BUFFER_MAX_SIZE; + + if (max_length > 0 && gtk_entry_buffer_get_length (buffer) > max_length) + gtk_entry_buffer_delete_text (buffer, max_length, -1); + + buffer->priv->max_length = max_length; + g_object_notify (G_OBJECT (buffer), "max-length"); +} + +/** + * gtk_entry_buffer_get_max_length: + * @buffer: a #GtkEntryBuffer + * + * Retrieves the maximum allowed length of the text in + * @buffer. See gtk_entry_buffer_set_max_length(). + * + * Return value: the maximum allowed number of characters + * in #GtkEntryBuffer, or 0 if there is no maximum. + * + * Since: 2.18 + **/ +guint +gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer) +{ + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0); + return buffer->priv->max_length; +} + +/** + * gtk_entry_buffer_insert_text: + * @buffer: a #GtkEntryBuffer + * @position: the position at which to insert text. + * @chars: the text to insert into the buffer. + * @n_chars: the length of the text in characters, or -1 + * + * Inserts @n_chars characters of @chars into the contents of the + * buffer, at position @position. + * + * If @n_chars is negative, then characters from chars will be inserted + * until a null-terminator is found. If @position or @n_chars are out of + * bounds, or the maximum buffer text length is exceeded, then they are + * coerced to sane values. + * + * Note that the position and length are in characters, not in bytes. + * + * Returns: The number of characters actually inserted. + * + * Since: 2.18 + */ +guint +gtk_entry_buffer_insert_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + gint n_chars) +{ + GtkEntryBufferClass *klass; + GtkEntryBufferPrivate *pv; + guint length; + + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0); + + length = gtk_entry_buffer_get_length (buffer); + pv = buffer->priv; + + if (n_chars < 0) + n_chars = g_utf8_strlen (chars, -1); + + /* Bring position into bounds */ + if (position > length) + position = length; + + /* Make sure not entering too much data */ + if (pv->max_length > 0) + { + if (length >= pv->max_length) + n_chars = 0; + else if (length + n_chars > pv->max_length) + n_chars -= (length + n_chars) - pv->max_length; + } + + klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer); + g_return_val_if_fail (klass->insert_text != NULL, 0); + + return (*klass->insert_text) (buffer, position, chars, n_chars); +} + +/** + * gtk_entry_buffer_delete_text: + * @buffer: a #GtkEntryBuffer + * @position: position at which to delete text + * @n_chars: number of characters to delete + * + * Deletes a sequence of characters from the buffer. @n_chars characters are + * deleted starting at @position. If @n_chars is negative, then all characters + * until the end of the text are deleted. + * + * If @position or @n_chars are out of bounds, then they are coerced to sane + * values. + * + * Note that the positions are specified in characters, not bytes. + * + * Returns: The number of characters deleted. + * + * Since: 2.18 + */ +guint +gtk_entry_buffer_delete_text (GtkEntryBuffer *buffer, + guint position, + gint n_chars) +{ + GtkEntryBufferClass *klass; + guint length; + + g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0); + + length = gtk_entry_buffer_get_length (buffer); + if (n_chars < 0) + n_chars = length; + if (position > length) + position = length; + if (position + n_chars > length) + n_chars = length - position; + + klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer); + g_return_val_if_fail (klass->delete_text != NULL, 0); + + return (*klass->delete_text) (buffer, position, n_chars); +} + +/** + * gtk_entry_buffer_emit_inserted_text: + * @buffer: a #GtkEntryBuffer + * @position: position at which text was inserted + * @chars: text that was inserted + * @n_chars: number of characters inserted + * + * Used when subclassing #GtkEntryBuffer + * + * Since: 2.18 + */ +void +gtk_entry_buffer_emit_inserted_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars) +{ + g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer)); + g_signal_emit (buffer, signals[INSERTED_TEXT], 0, position, chars, n_chars); +} + +/** + * gtk_entry_buffer_emit_deleted_text: + * @buffer: a #GtkEntryBuffer + * @position: position at which text was deleted + * @n_chars: number of characters deleted + * + * Used when subclassing #GtkEntryBuffer + * + * Since: 2.18 + */ +void +gtk_entry_buffer_emit_deleted_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars) +{ + g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer)); + g_signal_emit (buffer, signals[DELETED_TEXT], 0, position, n_chars); +} + diff --git a/gtk/gtkentrybuffer.h b/gtk/gtkentrybuffer.h new file mode 100644 index 0000000000..275aaa1b3b --- /dev/null +++ b/gtk/gtkentrybuffer.h @@ -0,0 +1,133 @@ +/* gtkentrybuffer.h + * Copyright (C) 2009 Stefan Walter <stef@memberwebs.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#ifndef __GTK_ENTRY_BUFFER_H__ +#define __GTK_ENTRY_BUFFER_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +/* Maximum size of text buffer, in bytes */ +#define GTK_ENTRY_BUFFER_MAX_SIZE G_MAXUSHORT + +#define GTK_TYPE_ENTRY_BUFFER (gtk_entry_buffer_get_type ()) +#define GTK_ENTRY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_ENTRY_BUFFER, GtkEntryBuffer)) +#define GTK_ENTRY_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_ENTRY_BUFFER, GtkEntryBufferClass)) +#define GTK_IS_ENTRY_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_ENTRY_BUFFER)) +#define GTK_IS_ENTRY_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_ENTRY_BUFFER)) +#define GTK_ENTRY_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_ENTRY_BUFFER, GtkEntryBufferClass)) + +typedef struct _GtkEntryBuffer GtkEntryBuffer; +typedef struct _GtkEntryBufferClass GtkEntryBufferClass; +typedef struct _GtkEntryBufferPrivate GtkEntryBufferPrivate; + +struct _GtkEntryBuffer +{ + GObject parent_instance; + + /*< private >*/ + GtkEntryBufferPrivate *priv; +}; + +struct _GtkEntryBufferClass +{ + GObjectClass parent_class; + + /* Signals */ + + void (*inserted_text) (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars); + + void (*deleted_text) (GtkEntryBuffer *buffer, + guint position, + guint n_chars); + + /* Virtual Methods */ + + const gchar* (*get_text) (GtkEntryBuffer *buffer, + gsize *n_bytes); + + guint (*get_length) (GtkEntryBuffer *buffer); + + guint (*insert_text) (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars); + + guint (*delete_text) (GtkEntryBuffer *buffer, + guint position, + guint n_chars); + + /* Padding for future expansion */ + void (*_gtk_reserved0) (void); + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); +}; + +GType gtk_entry_buffer_get_type (void) G_GNUC_CONST; + +GtkEntryBuffer* gtk_entry_buffer_new (const gchar *initial_chars, + gint n_initial_chars); + +gsize gtk_entry_buffer_get_bytes (GtkEntryBuffer *buffer); + +guint gtk_entry_buffer_get_length (GtkEntryBuffer *buffer); + +const gchar* gtk_entry_buffer_get_text (GtkEntryBuffer *buffer); + +void gtk_entry_buffer_set_text (GtkEntryBuffer *buffer, + const gchar *chars, + gint n_chars); + +void gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer, + guint max_length); + +guint gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer); + +guint gtk_entry_buffer_insert_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + gint n_chars); + +guint gtk_entry_buffer_delete_text (GtkEntryBuffer *buffer, + guint position, + gint n_chars); + +void gtk_entry_buffer_emit_inserted_text (GtkEntryBuffer *buffer, + guint position, + const gchar *chars, + guint n_chars); + +void gtk_entry_buffer_emit_deleted_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars); + +G_END_DECLS + +#endif /* __GTK_ENTRY_BUFFER_H__ */ diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index b625cba470..533a266ced 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -107,5 +107,7 @@ VOID:UINT,UINT VOID:UINT,STRING VOID:UINT,BOXED,UINT,FLAGS,FLAGS VOID:UINT,OBJECT,UINT,FLAGS,FLAGS +VOID:UINT,STRING,UINT +VOID:UINT,UINT VOID:VOID OBJECT:OBJECT,INT,INT |