1 /* gtkentrybuffer.c
2  * Copyright (C) 2009  Stefan Walter <stef@memberwebs.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "gtkentrybuffer.h"
21 #include "gtkintl.h"
22 #include "gtkmarshalers.h"
23 #include "gtkprivate.h"
24 #include "gtkwidget.h"
25 
26 #include <gdk/gdk.h>
27 
28 #include <string.h>
29 
30 /**
31  * GtkEntryBuffer:
32  *
33  * A `GtkEntryBuffer` hold the text displayed in a `GtkText` widget.
34  *
35  * A single `GtkEntryBuffer` object can be shared by multiple widgets
36  * which will then share the same text content, but not the cursor
37  * position, visibility attributes, icon etc.
38  *
39  * `GtkEntryBuffer` may be derived from. Such a derived class might allow
40  * text to be stored in an alternate location, such as non-pageable memory,
41  * useful in the case of important passwords. Or a derived class could
42  * integrate with an application’s concept of undo/redo.
43  */
44 
45 /* Initial size of buffer, in bytes */
46 #define MIN_SIZE 16
47 
48 enum {
49   PROP_0,
50   PROP_TEXT,
51   PROP_LENGTH,
52   PROP_MAX_LENGTH,
53   NUM_PROPERTIES
54 };
55 
56 static GParamSpec *entry_buffer_props[NUM_PROPERTIES] = { NULL, };
57 
58 enum {
59   INSERTED_TEXT,
60   DELETED_TEXT,
61   LAST_SIGNAL
62 };
63 
64 static guint signals[LAST_SIGNAL] = { 0 };
65 
66 typedef struct _GtkEntryBufferPrivate GtkEntryBufferPrivate;
67 struct _GtkEntryBufferPrivate
68 {
69   /* Only valid if this class is not derived */
70   char *normal_text;
71   gsize  normal_text_size;
72   gsize  normal_text_bytes;
73   guint  normal_text_chars;
74 
75   int    max_length;
76 };
77 
G_DEFINE_TYPE_WITH_PRIVATE(GtkEntryBuffer,gtk_entry_buffer,G_TYPE_OBJECT)78 G_DEFINE_TYPE_WITH_PRIVATE (GtkEntryBuffer, gtk_entry_buffer, G_TYPE_OBJECT)
79 
80 /* --------------------------------------------------------------------------------
81  * DEFAULT IMPLEMENTATIONS OF TEXT BUFFER
82  *
83  * These may be overridden by a derived class, behavior may be changed etc...
84  * The normal_text and normal_text_xxxx fields may not be valid when
85  * this class is derived from.
86  */
87 
88 /* Overwrite a memory that might contain sensitive information. */
89 static void
90 trash_area (char *area,
91             gsize  len)
92 {
93   volatile char *varea = (volatile char *)area;
94   while (len-- > 0)
95     *varea++ = 0;
96 }
97 
98 static const char *
gtk_entry_buffer_normal_get_text(GtkEntryBuffer * buffer,gsize * n_bytes)99 gtk_entry_buffer_normal_get_text (GtkEntryBuffer *buffer,
100                                   gsize          *n_bytes)
101 {
102   GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (buffer);
103 
104   if (n_bytes)
105     *n_bytes = priv->normal_text_bytes;
106 
107   if (!priv->normal_text)
108       return "";
109 
110   return priv->normal_text;
111 }
112 
113 static guint
gtk_entry_buffer_normal_get_length(GtkEntryBuffer * buffer)114 gtk_entry_buffer_normal_get_length (GtkEntryBuffer *buffer)
115 {
116   GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (buffer);
117 
118   return priv->normal_text_chars;
119 }
120 
121 static guint
gtk_entry_buffer_normal_insert_text(GtkEntryBuffer * buffer,guint position,const char * chars,guint n_chars)122 gtk_entry_buffer_normal_insert_text (GtkEntryBuffer *buffer,
123                                      guint           position,
124                                      const char     *chars,
125                                      guint           n_chars)
126 {
127   GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (buffer);
128   gsize prev_size;
129   gsize n_bytes;
130   gsize at;
131 
132   n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars;
133 
134   /* Need more memory */
135   if (n_bytes + pv->normal_text_bytes + 1 > pv->normal_text_size)
136     {
137       char *et_new;
138 
139       prev_size = pv->normal_text_size;
140 
141       /* Calculate our new buffer size */
142       while (n_bytes + pv->normal_text_bytes + 1 > pv->normal_text_size)
143         {
144           if (pv->normal_text_size == 0)
145             pv->normal_text_size = MIN_SIZE;
146           else
147             {
148               if (2 * pv->normal_text_size < GTK_ENTRY_BUFFER_MAX_SIZE)
149                 pv->normal_text_size *= 2;
150               else
151                 {
152                   pv->normal_text_size = GTK_ENTRY_BUFFER_MAX_SIZE;
153                   if (n_bytes > pv->normal_text_size - pv->normal_text_bytes - 1)
154                     {
155                       n_bytes = pv->normal_text_size - pv->normal_text_bytes - 1;
156                       n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars;
157                       n_chars = g_utf8_strlen (chars, n_bytes);
158                     }
159                   break;
160                 }
161             }
162         }
163 
164       /* Could be a password, so can't leave stuff in memory. */
165       et_new = g_malloc (pv->normal_text_size);
166       memcpy (et_new, pv->normal_text, MIN (prev_size, pv->normal_text_size));
167       trash_area (pv->normal_text, prev_size);
168       g_free (pv->normal_text);
169       pv->normal_text = et_new;
170     }
171 
172   /* Actual text insertion */
173   at = g_utf8_offset_to_pointer (pv->normal_text, position) - pv->normal_text;
174   memmove (pv->normal_text + at + n_bytes, pv->normal_text + at, pv->normal_text_bytes - at);
175   memcpy (pv->normal_text + at, chars, n_bytes);
176 
177   /* Book keeping */
178   pv->normal_text_bytes += n_bytes;
179   pv->normal_text_chars += n_chars;
180   pv->normal_text[pv->normal_text_bytes] = '\0';
181 
182   gtk_entry_buffer_emit_inserted_text (buffer, position, chars, n_chars);
183   return n_chars;
184 }
185 
186 static guint
gtk_entry_buffer_normal_delete_text(GtkEntryBuffer * buffer,guint position,guint n_chars)187 gtk_entry_buffer_normal_delete_text (GtkEntryBuffer *buffer,
188                                      guint           position,
189                                      guint           n_chars)
190 {
191   GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (buffer);
192 
193   if (position > pv->normal_text_chars)
194     position = pv->normal_text_chars;
195   if (position + n_chars > pv->normal_text_chars)
196     n_chars = pv->normal_text_chars - position;
197 
198   if (n_chars > 0)
199     gtk_entry_buffer_emit_deleted_text (buffer, position, n_chars);
200 
201   return n_chars;
202 }
203 
204 /* --------------------------------------------------------------------------------
205  *
206  */
207 
208 static void
gtk_entry_buffer_real_inserted_text(GtkEntryBuffer * buffer,guint position,const char * chars,guint n_chars)209 gtk_entry_buffer_real_inserted_text (GtkEntryBuffer *buffer,
210                                      guint           position,
211                                      const char     *chars,
212                                      guint           n_chars)
213 {
214   g_object_notify_by_pspec (G_OBJECT (buffer), entry_buffer_props[PROP_TEXT]);
215   g_object_notify_by_pspec (G_OBJECT (buffer), entry_buffer_props[PROP_LENGTH]);
216 }
217 
218 static void
gtk_entry_buffer_real_deleted_text(GtkEntryBuffer * buffer,guint position,guint n_chars)219 gtk_entry_buffer_real_deleted_text (GtkEntryBuffer *buffer,
220                                     guint           position,
221                                     guint           n_chars)
222 {
223   GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (buffer);
224   gsize start, end;
225 
226   start = g_utf8_offset_to_pointer (pv->normal_text, position) - pv->normal_text;
227   end = g_utf8_offset_to_pointer (pv->normal_text, position + n_chars) - pv->normal_text;
228 
229   memmove (pv->normal_text + start, pv->normal_text + end, pv->normal_text_bytes + 1 - end);
230   pv->normal_text_chars -= n_chars;
231   pv->normal_text_bytes -= (end - start);
232 
233   /*
234    * Could be a password, make sure we don't leave anything sensitive after
235    * the terminating zero.  Note, that the terminating zero already trashed
236    * one byte.
237    */
238   trash_area (pv->normal_text + pv->normal_text_bytes + 1, end - start - 1);
239 
240   g_object_notify_by_pspec (G_OBJECT (buffer), entry_buffer_props[PROP_TEXT]);
241   g_object_notify_by_pspec (G_OBJECT (buffer), entry_buffer_props[PROP_LENGTH]);
242 }
243 
244 /* --------------------------------------------------------------------------------
245  *
246  */
247 
248 static void
gtk_entry_buffer_init(GtkEntryBuffer * buffer)249 gtk_entry_buffer_init (GtkEntryBuffer *buffer)
250 {
251   GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (buffer);
252 
253   pv->normal_text = NULL;
254   pv->normal_text_chars = 0;
255   pv->normal_text_bytes = 0;
256   pv->normal_text_size = 0;
257 }
258 
259 static void
gtk_entry_buffer_finalize(GObject * obj)260 gtk_entry_buffer_finalize (GObject *obj)
261 {
262   GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj);
263   GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (buffer);
264 
265   if (pv->normal_text)
266     {
267       trash_area (pv->normal_text, pv->normal_text_size);
268       g_free (pv->normal_text);
269       pv->normal_text = NULL;
270       pv->normal_text_bytes = pv->normal_text_size = 0;
271       pv->normal_text_chars = 0;
272     }
273 
274   G_OBJECT_CLASS (gtk_entry_buffer_parent_class)->finalize (obj);
275 }
276 
277 static void
gtk_entry_buffer_set_property(GObject * obj,guint prop_id,const GValue * value,GParamSpec * pspec)278 gtk_entry_buffer_set_property (GObject      *obj,
279                                guint         prop_id,
280                                const GValue *value,
281                                GParamSpec   *pspec)
282 {
283   GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj);
284 
285   switch (prop_id)
286     {
287     case PROP_TEXT:
288       gtk_entry_buffer_set_text (buffer, g_value_get_string (value), -1);
289       break;
290     case PROP_MAX_LENGTH:
291       gtk_entry_buffer_set_max_length (buffer, g_value_get_int (value));
292       break;
293     default:
294       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
295       break;
296     }
297 }
298 
299 static void
gtk_entry_buffer_get_property(GObject * obj,guint prop_id,GValue * value,GParamSpec * pspec)300 gtk_entry_buffer_get_property (GObject    *obj,
301                                guint       prop_id,
302                                GValue     *value,
303                                GParamSpec *pspec)
304 {
305   GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj);
306 
307   switch (prop_id)
308     {
309     case PROP_TEXT:
310       g_value_set_string (value, gtk_entry_buffer_get_text (buffer));
311       break;
312     case PROP_LENGTH:
313       g_value_set_uint (value, gtk_entry_buffer_get_length (buffer));
314       break;
315     case PROP_MAX_LENGTH:
316       g_value_set_int (value, gtk_entry_buffer_get_max_length (buffer));
317       break;
318     default:
319       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
320       break;
321     }
322 }
323 
324 static void
gtk_entry_buffer_class_init(GtkEntryBufferClass * klass)325 gtk_entry_buffer_class_init (GtkEntryBufferClass *klass)
326 {
327   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
328 
329   gobject_class->finalize = gtk_entry_buffer_finalize;
330   gobject_class->set_property = gtk_entry_buffer_set_property;
331   gobject_class->get_property = gtk_entry_buffer_get_property;
332 
333   klass->get_text = gtk_entry_buffer_normal_get_text;
334   klass->get_length = gtk_entry_buffer_normal_get_length;
335   klass->insert_text = gtk_entry_buffer_normal_insert_text;
336   klass->delete_text = gtk_entry_buffer_normal_delete_text;
337 
338   klass->inserted_text = gtk_entry_buffer_real_inserted_text;
339   klass->deleted_text = gtk_entry_buffer_real_deleted_text;
340 
341   /**
342    * GtkEntryBuffer:text: (attributes org.gtk.Property.get=gtk_entry_buffer_get_text org.gtk.Property.set=gtk_entry_buffer_set_text)
343    *
344    * The contents of the buffer.
345    */
346   entry_buffer_props[PROP_TEXT] =
347       g_param_spec_string ("text",
348                            P_("Text"),
349                            P_("The contents of the buffer"),
350                            "",
351                            GTK_PARAM_READWRITE);
352 
353   /**
354    * GtkEntryBuffer:length: (attributes org.gtk.Property.get=gtk_entry_buffer_get_length)
355    *
356    * The length (in characters) of the text in buffer.
357    */
358    entry_buffer_props[PROP_LENGTH] =
359        g_param_spec_uint ("length",
360                           P_("Text length"),
361                           P_("Length of the text currently in the buffer"),
362                           0, GTK_ENTRY_BUFFER_MAX_SIZE, 0,
363                           GTK_PARAM_READABLE);
364 
365   /**
366    * GtkEntryBuffer:max-length: (attributes org.gtk.Property.get=gtk_entry_buffer_get_max_length org.gtk.Property.set=gtk_entry_buffer_set_max_length)
367    *
368    * The maximum length (in characters) of the text in the buffer.
369    */
370   entry_buffer_props[PROP_MAX_LENGTH] =
371       g_param_spec_int ("max-length",
372                         P_("Maximum length"),
373                         P_("Maximum number of characters for this entry. Zero if no maximum"),
374                         0, GTK_ENTRY_BUFFER_MAX_SIZE, 0,
375                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
376 
377   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, entry_buffer_props);
378 
379   /**
380    * GtkEntryBuffer::inserted-text:
381    * @buffer: a `GtkEntryBuffer`
382    * @position: the position the text was inserted at.
383    * @chars: The text that was inserted.
384    * @n_chars: The number of characters that were inserted.
385    *
386    * This signal is emitted after text is inserted into the buffer.
387    */
388   signals[INSERTED_TEXT] = g_signal_new (I_("inserted-text"),
389                                          GTK_TYPE_ENTRY_BUFFER,
390                                          G_SIGNAL_RUN_FIRST,
391                                          G_STRUCT_OFFSET (GtkEntryBufferClass, inserted_text),
392                                          NULL, NULL,
393                                          _gtk_marshal_VOID__UINT_STRING_UINT,
394                                          G_TYPE_NONE, 3,
395                                          G_TYPE_UINT,
396                                          G_TYPE_STRING,
397                                          G_TYPE_UINT);
398 
399   /**
400    * GtkEntryBuffer::deleted-text:
401    * @buffer: a `GtkEntryBuffer`
402    * @position: the position the text was deleted at.
403    * @n_chars: The number of characters that were deleted.
404    *
405    * The text is altered in the default handler for this signal.
406    *
407    * If you want access to the text after the text has been modified,
408    * use %G_CONNECT_AFTER.
409    */
410   signals[DELETED_TEXT] =  g_signal_new (I_("deleted-text"),
411                                          GTK_TYPE_ENTRY_BUFFER,
412                                          G_SIGNAL_RUN_LAST,
413                                          G_STRUCT_OFFSET (GtkEntryBufferClass, deleted_text),
414                                          NULL, NULL,
415                                          _gtk_marshal_VOID__UINT_UINT,
416                                          G_TYPE_NONE, 2,
417                                          G_TYPE_UINT,
418                                          G_TYPE_UINT);
419 }
420 
421 /* --------------------------------------------------------------------------------
422  *
423  */
424 
425 /**
426  * gtk_entry_buffer_new:
427  * @initial_chars: (nullable): initial buffer text
428  * @n_initial_chars: number of characters in @initial_chars, or -1
429  *
430  * Create a new `GtkEntryBuffer` object.
431  *
432  * Optionally, specify initial text to set in the buffer.
433  *
434  * Returns: A new `GtkEntryBuffer` object.
435  */
436 GtkEntryBuffer*
gtk_entry_buffer_new(const char * initial_chars,int n_initial_chars)437 gtk_entry_buffer_new (const char *initial_chars,
438                       int          n_initial_chars)
439 {
440   GtkEntryBuffer *buffer = g_object_new (GTK_TYPE_ENTRY_BUFFER, NULL);
441   if (initial_chars)
442     gtk_entry_buffer_set_text (buffer, initial_chars, n_initial_chars);
443   return buffer;
444 }
445 
446 /**
447  * gtk_entry_buffer_get_length: (attributes org.gtk.Method.get_property=length)
448  * @buffer: a `GtkEntryBuffer`
449  *
450  * Retrieves the length in characters of the buffer.
451  *
452  * Returns: The number of characters in the buffer.
453  **/
454 guint
gtk_entry_buffer_get_length(GtkEntryBuffer * buffer)455 gtk_entry_buffer_get_length (GtkEntryBuffer *buffer)
456 {
457   GtkEntryBufferClass *klass;
458 
459   g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0);
460 
461   klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer);
462   g_return_val_if_fail (klass->get_length != NULL, 0);
463 
464   return (*klass->get_length) (buffer);
465 }
466 
467 /**
468  * gtk_entry_buffer_get_bytes:
469  * @buffer: a `GtkEntryBuffer`
470  *
471  * Retrieves the length in bytes of the buffer.
472  *
473  * See [method@Gtk.EntryBuffer.get_length].
474  *
475  * Returns: The byte length of the buffer.
476  **/
477 gsize
gtk_entry_buffer_get_bytes(GtkEntryBuffer * buffer)478 gtk_entry_buffer_get_bytes (GtkEntryBuffer *buffer)
479 {
480   GtkEntryBufferClass *klass;
481   gsize bytes = 0;
482 
483   g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0);
484 
485   klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer);
486   g_return_val_if_fail (klass->get_text != NULL, 0);
487 
488   (*klass->get_text) (buffer, &bytes);
489   return bytes;
490 }
491 
492 /**
493  * gtk_entry_buffer_get_text: (attributes org.gtk.Method.get_property=text)
494  * @buffer: a `GtkEntryBuffer`
495  *
496  * Retrieves the contents of the buffer.
497  *
498  * The memory pointer returned by this call will not change
499  * unless this object emits a signal, or is finalized.
500  *
501  * Returns: a pointer to the contents of the widget as a
502  *   string. This string points to internally allocated storage
503  *   in the buffer and must not be freed, modified or stored.
504  */
505 const char *
gtk_entry_buffer_get_text(GtkEntryBuffer * buffer)506 gtk_entry_buffer_get_text (GtkEntryBuffer *buffer)
507 {
508   GtkEntryBufferClass *klass;
509 
510   g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), NULL);
511 
512   klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer);
513   g_return_val_if_fail (klass->get_text != NULL, NULL);
514 
515   return (*klass->get_text) (buffer, NULL);
516 }
517 
518 /**
519  * gtk_entry_buffer_set_text: (attributes org.gtk.Method.set_property=text)
520  * @buffer: a `GtkEntryBuffer`
521  * @chars: the new text
522  * @n_chars: the number of characters in @text, or -1
523  *
524  * Sets the text in the buffer.
525  *
526  * This is roughly equivalent to calling
527  * [method@Gtk.EntryBuffer.delete_text] and
528  * [method@Gtk.EntryBuffer.insert_text].
529  *
530  * Note that @n_chars is in characters, not in bytes.
531  **/
532 void
gtk_entry_buffer_set_text(GtkEntryBuffer * buffer,const char * chars,int n_chars)533 gtk_entry_buffer_set_text (GtkEntryBuffer *buffer,
534                            const char     *chars,
535                            int             n_chars)
536 {
537   g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer));
538   g_return_if_fail (chars != NULL);
539 
540   g_object_freeze_notify (G_OBJECT (buffer));
541   gtk_entry_buffer_delete_text (buffer, 0, -1);
542   gtk_entry_buffer_insert_text (buffer, 0, chars, n_chars);
543   g_object_thaw_notify (G_OBJECT (buffer));
544 }
545 
546 /**
547  * gtk_entry_buffer_set_max_length: (attributes org.gtk.Method.set_property=max-length)
548  * @buffer: a `GtkEntryBuffer`
549  * @max_length: the maximum length of the entry buffer, or 0 for no maximum.
550  *   (other than the maximum length of entries.) The value passed in will
551  *   be clamped to the range 0-65536.
552  *
553  * Sets the maximum allowed length of the contents of the buffer.
554  *
555  * If the current contents are longer than the given length, then
556  * they will be truncated to fit.
557  */
558 void
gtk_entry_buffer_set_max_length(GtkEntryBuffer * buffer,int max_length)559 gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer,
560                                  int             max_length)
561 {
562   GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (buffer);
563 
564   g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer));
565 
566   max_length = CLAMP (max_length, 0, GTK_ENTRY_BUFFER_MAX_SIZE);
567 
568   if (priv->max_length == max_length)
569     return;
570 
571   if (max_length > 0 && gtk_entry_buffer_get_length (buffer) > max_length)
572     gtk_entry_buffer_delete_text (buffer, max_length, -1);
573 
574   priv->max_length = max_length;
575   g_object_notify_by_pspec (G_OBJECT (buffer), entry_buffer_props[PROP_MAX_LENGTH]);
576 }
577 
578 /**
579  * gtk_entry_buffer_get_max_length: (attributes org.gtk.Method.get_property=max-length)
580  * @buffer: a `GtkEntryBuffer`
581  *
582  * Retrieves the maximum allowed length of the text in @buffer.
583  *
584  * Returns: the maximum allowed number of characters
585  *   in `GtkEntryBuffer`, or 0 if there is no maximum.
586  */
587 int
gtk_entry_buffer_get_max_length(GtkEntryBuffer * buffer)588 gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer)
589 {
590   GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (buffer);
591 
592   g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0);
593 
594   return priv->max_length;
595 }
596 
597 /**
598  * gtk_entry_buffer_insert_text:
599  * @buffer: a `GtkEntryBuffer`
600  * @position: the position at which to insert text.
601  * @chars: the text to insert into the buffer.
602  * @n_chars: the length of the text in characters, or -1
603  *
604  * Inserts @n_chars characters of @chars into the contents of the
605  * buffer, at position @position.
606  *
607  * If @n_chars is negative, then characters from chars will be inserted
608  * until a null-terminator is found. If @position or @n_chars are out of
609  * bounds, or the maximum buffer text length is exceeded, then they are
610  * coerced to sane values.
611  *
612  * Note that the position and length are in characters, not in bytes.
613  *
614  * Returns: The number of characters actually inserted.
615  */
616 guint
gtk_entry_buffer_insert_text(GtkEntryBuffer * buffer,guint position,const char * chars,int n_chars)617 gtk_entry_buffer_insert_text (GtkEntryBuffer *buffer,
618                               guint           position,
619                               const char     *chars,
620                               int             n_chars)
621 {
622   GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (buffer);
623   GtkEntryBufferClass *klass;
624   guint length;
625 
626   g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0);
627 
628   length = gtk_entry_buffer_get_length (buffer);
629 
630   if (n_chars < 0)
631     n_chars = g_utf8_strlen (chars, -1);
632 
633   /* Bring position into bounds */
634   if (position > length)
635     position = length;
636 
637   /* Make sure not entering too much data */
638   if (pv->max_length > 0)
639     {
640       if (length >= pv->max_length)
641         n_chars = 0;
642       else if (length + n_chars > pv->max_length)
643         n_chars -= (length + n_chars) - pv->max_length;
644     }
645 
646   if (n_chars == 0)
647     return 0;
648 
649   klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer);
650   g_return_val_if_fail (klass->insert_text != NULL, 0);
651 
652   return (*klass->insert_text) (buffer, position, chars, n_chars);
653 }
654 
655 /**
656  * gtk_entry_buffer_delete_text:
657  * @buffer: a `GtkEntryBuffer`
658  * @position: position at which to delete text
659  * @n_chars: number of characters to delete
660  *
661  * Deletes a sequence of characters from the buffer.
662  *
663  * @n_chars characters are deleted starting at @position.
664  * If @n_chars is negative, then all characters until the
665  * end of the text are deleted.
666  *
667  * If @position or @n_chars are out of bounds, then they
668  * are coerced to sane values.
669  *
670  * Note that the positions are specified in characters,
671  * not bytes.
672  *
673  * Returns: The number of characters deleted.
674  */
675 guint
gtk_entry_buffer_delete_text(GtkEntryBuffer * buffer,guint position,int n_chars)676 gtk_entry_buffer_delete_text (GtkEntryBuffer *buffer,
677                               guint           position,
678                               int             n_chars)
679 {
680   GtkEntryBufferClass *klass;
681   guint length;
682 
683   g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0);
684 
685   length = gtk_entry_buffer_get_length (buffer);
686   if (n_chars < 0)
687     n_chars = length;
688   if (position > length)
689     position = length;
690   if (position + n_chars > length)
691     n_chars = length - position;
692 
693   klass = GTK_ENTRY_BUFFER_GET_CLASS (buffer);
694   g_return_val_if_fail (klass->delete_text != NULL, 0);
695 
696   return (*klass->delete_text) (buffer, position, n_chars);
697 }
698 
699 /**
700  * gtk_entry_buffer_emit_inserted_text:
701  * @buffer: a `GtkEntryBuffer`
702  * @position: position at which text was inserted
703  * @chars: text that was inserted
704  * @n_chars: number of characters inserted
705  *
706  * Used when subclassing `GtkEntryBuffer`.
707  */
708 void
gtk_entry_buffer_emit_inserted_text(GtkEntryBuffer * buffer,guint position,const char * chars,guint n_chars)709 gtk_entry_buffer_emit_inserted_text (GtkEntryBuffer *buffer,
710                                      guint           position,
711                                      const char     *chars,
712                                      guint           n_chars)
713 {
714   g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer));
715   g_signal_emit (buffer, signals[INSERTED_TEXT], 0, position, chars, n_chars);
716 }
717 
718 /**
719  * gtk_entry_buffer_emit_deleted_text:
720  * @buffer: a `GtkEntryBuffer`
721  * @position: position at which text was deleted
722  * @n_chars: number of characters deleted
723  *
724  * Used when subclassing `GtkEntryBuffer`.
725  */
726 void
gtk_entry_buffer_emit_deleted_text(GtkEntryBuffer * buffer,guint position,guint n_chars)727 gtk_entry_buffer_emit_deleted_text (GtkEntryBuffer *buffer,
728                                     guint           position,
729                                     guint           n_chars)
730 {
731   g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer));
732   g_signal_emit (buffer, signals[DELETED_TEXT], 0, position, n_chars);
733 }
734