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
48enum {
49 PROP_0,
50 PROP_TEXT,
51 PROP_LENGTH,
52 PROP_MAX_LENGTH,
53 NUM_PROPERTIES
54};
55
56static GParamSpec *entry_buffer_props[NUM_PROPERTIES] = { NULL, };
57
58enum {
59 INSERTED_TEXT,
60 DELETED_TEXT,
61 LAST_SIGNAL
62};
63
64static guint signals[LAST_SIGNAL] = { 0 };
65
66typedef struct _GtkEntryBufferPrivate GtkEntryBufferPrivate;
67struct _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
78G_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. */
89static void
90trash_area (char *area,
91 gsize len)
92{
93 volatile char *varea = (volatile char *)area;
94 while (len-- > 0)
95 *varea++ = 0;
96}
97
98static const char *
99gtk_entry_buffer_normal_get_text (GtkEntryBuffer *buffer,
100 gsize *n_bytes)
101{
102 GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (self: 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
113static guint
114gtk_entry_buffer_normal_get_length (GtkEntryBuffer *buffer)
115{
116 GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (self: buffer);
117
118 return priv->normal_text_chars;
119}
120
121static guint
122gtk_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 (self: buffer);
128 gsize prev_size;
129 gsize n_bytes;
130 gsize at;
131
132 n_bytes = g_utf8_offset_to_pointer (str: chars, offset: 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 (str: chars, p: chars + n_bytes + 1) - chars;
157 n_chars = g_utf8_strlen (p: chars, max: 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 (n_bytes: pv->normal_text_size);
166 memcpy (dest: et_new, src: pv->normal_text, MIN (prev_size, pv->normal_text_size));
167 trash_area (area: pv->normal_text, len: prev_size);
168 g_free (mem: pv->normal_text);
169 pv->normal_text = et_new;
170 }
171
172 /* Actual text insertion */
173 at = g_utf8_offset_to_pointer (str: pv->normal_text, offset: position) - pv->normal_text;
174 memmove (dest: pv->normal_text + at + n_bytes, src: pv->normal_text + at, n: pv->normal_text_bytes - at);
175 memcpy (dest: pv->normal_text + at, src: chars, n: 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
186static guint
187gtk_entry_buffer_normal_delete_text (GtkEntryBuffer *buffer,
188 guint position,
189 guint n_chars)
190{
191 GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (self: 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
208static void
209gtk_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), pspec: entry_buffer_props[PROP_TEXT]);
215 g_object_notify_by_pspec (G_OBJECT (buffer), pspec: entry_buffer_props[PROP_LENGTH]);
216}
217
218static void
219gtk_entry_buffer_real_deleted_text (GtkEntryBuffer *buffer,
220 guint position,
221 guint n_chars)
222{
223 GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (self: buffer);
224 gsize start, end;
225
226 start = g_utf8_offset_to_pointer (str: pv->normal_text, offset: position) - pv->normal_text;
227 end = g_utf8_offset_to_pointer (str: pv->normal_text, offset: position + n_chars) - pv->normal_text;
228
229 memmove (dest: pv->normal_text + start, src: pv->normal_text + end, n: 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 (area: pv->normal_text + pv->normal_text_bytes + 1, len: end - start - 1);
239
240 g_object_notify_by_pspec (G_OBJECT (buffer), pspec: entry_buffer_props[PROP_TEXT]);
241 g_object_notify_by_pspec (G_OBJECT (buffer), pspec: entry_buffer_props[PROP_LENGTH]);
242}
243
244/* --------------------------------------------------------------------------------
245 *
246 */
247
248static void
249gtk_entry_buffer_init (GtkEntryBuffer *buffer)
250{
251 GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (self: 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
259static void
260gtk_entry_buffer_finalize (GObject *obj)
261{
262 GtkEntryBuffer *buffer = GTK_ENTRY_BUFFER (obj);
263 GtkEntryBufferPrivate *pv = gtk_entry_buffer_get_instance_private (self: buffer);
264
265 if (pv->normal_text)
266 {
267 trash_area (area: pv->normal_text, len: pv->normal_text_size);
268 g_free (mem: 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
277static void
278gtk_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, chars: g_value_get_string (value), n_chars: -1);
289 break;
290 case PROP_MAX_LENGTH:
291 gtk_entry_buffer_set_max_length (buffer, max_length: 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
299static void
300gtk_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, v_string: gtk_entry_buffer_get_text (buffer));
311 break;
312 case PROP_LENGTH:
313 g_value_set_uint (value, v_uint: gtk_entry_buffer_get_length (buffer));
314 break;
315 case PROP_MAX_LENGTH:
316 g_value_set_int (value, v_int: 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
324static void
325gtk_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 (name: "text",
348 P_("Text"),
349 P_("The contents of the buffer"),
350 default_value: "",
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 (name: "length",
360 P_("Text length"),
361 P_("Length of the text currently in the buffer"),
362 minimum: 0, GTK_ENTRY_BUFFER_MAX_SIZE, default_value: 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 (name: "max-length",
372 P_("Maximum length"),
373 P_("Maximum number of characters for this entry. Zero if no maximum"),
374 minimum: 0, GTK_ENTRY_BUFFER_MAX_SIZE, default_value: 0,
375 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
376
377 g_object_class_install_properties (oclass: gobject_class, n_pspecs: NUM_PROPERTIES, pspecs: 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 signal_flags: G_SIGNAL_RUN_FIRST,
391 G_STRUCT_OFFSET (GtkEntryBufferClass, inserted_text),
392 NULL, NULL,
393 c_marshaller: _gtk_marshal_VOID__UINT_STRING_UINT,
394 G_TYPE_NONE, n_params: 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 signal_flags: G_SIGNAL_RUN_LAST,
413 G_STRUCT_OFFSET (GtkEntryBufferClass, deleted_text),
414 NULL, NULL,
415 c_marshaller: _gtk_marshal_VOID__UINT_UINT,
416 G_TYPE_NONE, n_params: 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 */
436GtkEntryBuffer*
437gtk_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, chars: initial_chars, n_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 **/
454guint
455gtk_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 **/
477gsize
478gtk_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 */
505const char *
506gtk_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 **/
532void
533gtk_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, position: 0, n_chars: -1);
542 gtk_entry_buffer_insert_text (buffer, position: 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 */
558void
559gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer,
560 int max_length)
561{
562 GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (self: 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, position: max_length, n_chars: -1);
573
574 priv->max_length = max_length;
575 g_object_notify_by_pspec (G_OBJECT (buffer), pspec: 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 */
587int
588gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer)
589{
590 GtkEntryBufferPrivate *priv = gtk_entry_buffer_get_instance_private (self: 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 */
616guint
617gtk_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 (self: 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 (p: chars, max: -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 */
675guint
676gtk_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 */
708void
709gtk_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 (instance: buffer, signal_id: signals[INSERTED_TEXT], detail: 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 */
726void
727gtk_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 (instance: buffer, signal_id: signals[DELETED_TEXT], detail: 0, position, n_chars);
733}
734

source code of gtk/gtk/gtkentrybuffer.c