summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--demos/gtk-demo/Makefile.am1
-rw-r--r--demos/gtk-demo/entry_buffer.c65
-rw-r--r--docs/reference/gtk/gtk-docs.sgml1
-rw-r--r--docs/reference/gtk/gtk-sections.txt31
-rw-r--r--docs/reference/gtk/gtk.types1
-rw-r--r--docs/reference/gtk/tmpl/gtk-unused.sgml19
-rw-r--r--docs/reference/gtk/tmpl/gtkentry.sgml32
-rw-r--r--docs/reference/gtk/tmpl/gtkentrybuffer.sgml168
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtkentry.c979
-rw-r--r--gtk/gtkentry.h15
-rw-r--r--gtk/gtkentrybuffer.c741
-rw-r--r--gtk/gtkentrybuffer.h133
-rw-r--r--gtk/gtkmarshalers.list2
15 files changed, 1754 insertions, 437 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index 23eed00ee..22662c43b 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 000000000..2be770e3b
--- /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 16a59b0f1..9b65c5ec7 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 bb19dec3a..331157dfb 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 126f673bb..d743aa1e1 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 cb2a663ef..420d94539 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 385e7235e..59a95c68f 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 000000000..712f01315
--- /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 02463f037..48b1f74ec 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 \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index b91a6badf..211c2d253 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -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 784667c12..f147b3f16 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 be0444752..d7f4bfb0f 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 000000000..7c6d2186a
--- /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 000000000..275aaa1b3
--- /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 b625cba47..533a266ce 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