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