1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 /**
26  * GtkEditable:
27  *
28  * `GtkEditable` is an interface for text editing widgets.
29  *
30  * Typical examples of editable widgets are [class@Gtk.Entry] and
31  * [class@Gtk.SpinButton]. It contains functions for generically manipulating
32  * an editable widget, a large number of action signals used for key bindings,
33  * and several signals that an application can connect to modify the behavior
34  * of a widget.
35  *
36  * As an example of the latter usage, by connecting the following handler to
37  * [signal@Gtk.Editable::insert-text], an application can convert all entry
38  * into a widget into uppercase.
39  *
40  * ## Forcing entry to uppercase.
41  *
42  * ```c
43  * #include <ctype.h>
44  *
45  * void
46  * insert_text_handler (GtkEditable *editable,
47  *                      const char  *text,
48  *                      int          length,
49  *                      int         *position,
50  *                      gpointer     data)
51  * {
52  *   char *result = g_utf8_strup (text, length);
53  *
54  *   g_signal_handlers_block_by_func (editable,
55  *                                (gpointer) insert_text_handler, data);
56  *   gtk_editable_insert_text (editable, result, length, position);
57  *   g_signal_handlers_unblock_by_func (editable,
58  *                                      (gpointer) insert_text_handler, data);
59  *
60  *   g_signal_stop_emission_by_name (editable, "insert_text");
61  *
62  *   g_free (result);
63  * }
64  * ```
65  *
66  * ## Implementing GtkEditable
67  *
68  * The most likely scenario for implementing `GtkEditable` on your own widget
69  * is that you will embed a `GtkText` inside a complex widget, and want to
70  * delegate the editable functionality to that text widget. `GtkEditable`
71  * provides some utility functions to make this easy.
72  *
73  * In your class_init function, call [func@Gtk.Editable.install_properties],
74  * passing the first available property ID:
75  *
76  * ```c
77  * static void
78  * my_class_init (MyClass *class)
79  * {
80  *   ...
81  *   g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
82  *   gtk_editable_install_properties (object_clas, NUM_PROPERTIES);
83  *   ...
84  * }
85  * ```
86  *
87  * In your interface_init function for the `GtkEditable` interface, provide
88  * an implementation for the get_delegate vfunc that returns your text widget:
89  *
90  * ```c
91  * GtkEditable *
92  * get_editable_delegate (GtkEditable *editable)
93  * {
94  *   return GTK_EDITABLE (MY_WIDGET (editable)->text_widget);
95  * }
96  *
97  * static void
98  * my_editable_init (GtkEditableInterface *iface)
99  * {
100  *   iface->get_delegate = get_editable_delegate;
101  * }
102  * ```
103  *
104  * You don't need to provide any other vfuncs. The default implementations
105  * work by forwarding to the delegate that the GtkEditableInterface.get_delegate()
106  * vfunc returns.
107  *
108  * In your instance_init function, create your text widget, and then call
109  * [method@Gtk.Editable.init_delegate]:
110  *
111  * ```c
112  * static void
113  * my_widget_init (MyWidget *self)
114  * {
115  *   ...
116  *   self->text_widget = gtk_text_new ();
117  *   gtk_editable_init_delegate (GTK_EDITABLE (self));
118  *   ...
119  * }
120  * ```
121  *
122  * In your dispose function, call [method@Gtk.Editable.finish_delegate] before
123  * destroying your text widget:
124  *
125  * ```c
126  * static void
127  * my_widget_dispose (GObject *object)
128  * {
129  *   ...
130  *   gtk_editable_finish_delegate (GTK_EDITABLE (self));
131  *   g_clear_pointer (&self->text_widget, gtk_widget_unparent);
132  *   ...
133  * }
134  * ```
135  *
136  * Finally, use [func@Gtk.Editable.delegate_set_property] in your `set_property`
137  * function (and similar for `get_property`), to set the editable properties:
138  *
139  * ```c
140  *   ...
141  *   if (gtk_editable_delegate_set_property (object, prop_id, value, pspec))
142  *     return;
143  *
144  *   switch (prop_id)
145  *   ...
146  * ```
147  *
148  * It is important to note that if you create a `GtkEditable` that uses
149  * a delegate, the low level [signal@Gtk.Editable::insert-text] and
150  * [signal@Gtk.Editable::delete-text] signals will be propagated from the
151  * "wrapper" editable to the delegate, but they will not be propagated from
152  * the delegate to the "wrapper" editable, as they would cause an infinite
153  * recursion. If you wish to connect to the [signal@Gtk.Editable::insert-text]
154  * and [signal@Gtk.Editable::delete-text] signals, you will need to connect
155  * to them on the delegate obtained via [method@Gtk.Editable.get_delegate].
156  */
157 
158 #include "config.h"
159 #include <string.h>
160 
161 #include "gtkeditable.h"
162 #include "gtkentrybuffer.h"
163 #include "gtkmarshalers.h"
164 #include "gtkintl.h"
165 #include "gtkprivate.h"
166 
167 G_DEFINE_INTERFACE (GtkEditable, gtk_editable, GTK_TYPE_WIDGET)
168 
169 enum {
170   CHANGED,
171   DELETE_TEXT,
172   INSERT_TEXT,
173   N_SIGNALS
174 };
175 
176 static GQuark quark_editable_data;
177 static guint signals[N_SIGNALS];
178 
179 static GtkEditable *
get_delegate(GtkEditable * editable)180 get_delegate (GtkEditable *editable)
181 {
182   GtkEditableInterface *iface = GTK_EDITABLE_GET_IFACE (editable);
183 
184   if (iface->get_delegate)
185     return iface->get_delegate (editable);
186 
187   return NULL;
188 }
189 
190 static void
gtk_editable_default_do_insert_text(GtkEditable * editable,const char * text,int length,int * position)191 gtk_editable_default_do_insert_text (GtkEditable *editable,
192                                      const char  *text,
193                                      int          length,
194                                      int         *position)
195 {
196   g_signal_emit (editable, signals[INSERT_TEXT], 0, text, length, position);
197 }
198 
199 #define warn_no_delegate(func) \
200   g_critical ("GtkEditable %s: default implementation called without a delegate", func);
201 
202 static void
gtk_editable_default_insert_text(GtkEditable * editable,const char * text,int length,int * position)203 gtk_editable_default_insert_text (GtkEditable *editable,
204                                   const char  *text,
205                                   int          length,
206                                   int         *position)
207 {
208   GtkEditable *delegate = get_delegate (editable);
209 
210   if (delegate)
211     gtk_editable_insert_text (delegate, text, length, position);
212   else
213     warn_no_delegate ("insert_text");
214 }
215 
216 static void
gtk_editable_default_do_delete_text(GtkEditable * editable,int start_pos,int end_pos)217 gtk_editable_default_do_delete_text (GtkEditable *editable,
218                                      int          start_pos,
219                                      int          end_pos)
220 {
221   g_signal_emit (editable, signals[DELETE_TEXT], 0, start_pos, end_pos);
222 }
223 
224 static void
gtk_editable_default_delete_text(GtkEditable * editable,int start_pos,int end_pos)225 gtk_editable_default_delete_text (GtkEditable *editable,
226                                   int          start_pos,
227                                   int          end_pos)
228 {
229   GtkEditable *delegate = get_delegate (editable);
230 
231   if (delegate)
232     gtk_editable_delete_text (delegate, start_pos, end_pos);
233   else
234     warn_no_delegate ("delete_text");
235 }
236 
237 static const char *
gtk_editable_default_get_text(GtkEditable * editable)238 gtk_editable_default_get_text (GtkEditable *editable)
239 {
240   GtkEditable *delegate = get_delegate (editable);
241 
242   if (delegate)
243     return gtk_editable_get_text (delegate);
244   else
245     warn_no_delegate ("get_text");
246 
247   return NULL;
248 }
249 
250 static void
gtk_editable_default_set_selection_bounds(GtkEditable * editable,int start_pos,int end_pos)251 gtk_editable_default_set_selection_bounds (GtkEditable *editable,
252                                            int          start_pos,
253                                            int          end_pos)
254 {
255   GtkEditable *delegate = get_delegate (editable);
256 
257   if (delegate)
258     gtk_editable_select_region (delegate, start_pos, end_pos);
259   else
260     warn_no_delegate ("select_region");
261 }
262 
263 static gboolean
gtk_editable_default_get_selection_bounds(GtkEditable * editable,int * start_pos,int * end_pos)264 gtk_editable_default_get_selection_bounds (GtkEditable *editable,
265                                            int         *start_pos,
266                                            int         *end_pos)
267 {
268   GtkEditable *delegate = get_delegate (editable);
269 
270   if (delegate)
271     return gtk_editable_get_selection_bounds (delegate, start_pos, end_pos);
272   else
273     warn_no_delegate ("select_region");
274 
275   return FALSE;
276 }
277 
278 static void
gtk_editable_default_init(GtkEditableInterface * iface)279 gtk_editable_default_init (GtkEditableInterface *iface)
280 {
281   quark_editable_data = g_quark_from_static_string ("GtkEditable-data");
282 
283   iface->insert_text = gtk_editable_default_insert_text;
284   iface->delete_text = gtk_editable_default_delete_text;
285   iface->get_text = gtk_editable_default_get_text;
286   iface->do_insert_text = gtk_editable_default_do_insert_text;
287   iface->do_delete_text = gtk_editable_default_do_delete_text;
288   iface->get_selection_bounds = gtk_editable_default_get_selection_bounds;
289   iface->set_selection_bounds = gtk_editable_default_set_selection_bounds;
290 
291   /**
292    * GtkEditable::insert-text:
293    * @editable: the object which received the signal
294    * @text: the new text to insert
295    * @length: the length of the new text, in bytes,
296    *     or -1 if new_text is nul-terminated
297    * @position: (inout) (type int): the position, in characters,
298    *     at which to insert the new text. this is an in-out
299    *     parameter.  After the signal emission is finished, it
300    *     should point after the newly inserted text.
301    *
302    * Emitted when text is inserted into the widget by the user.
303    *
304    * The default handler for this signal will normally be responsible
305    * for inserting the text, so by connecting to this signal and then
306    * stopping the signal with g_signal_stop_emission(), it is possible
307    * to modify the inserted text, or prevent it from being inserted entirely.
308    */
309   signals[INSERT_TEXT] =
310     g_signal_new (I_("insert-text"),
311                   GTK_TYPE_EDITABLE,
312                   G_SIGNAL_RUN_LAST,
313                   G_STRUCT_OFFSET (GtkEditableInterface, insert_text),
314                   NULL, NULL,
315                   _gtk_marshal_VOID__STRING_INT_POINTER,
316                   G_TYPE_NONE, 3,
317                   G_TYPE_STRING,
318                   G_TYPE_INT,
319                   G_TYPE_POINTER);
320   g_signal_set_va_marshaller (signals[INSERT_TEXT],
321                               G_TYPE_FROM_INTERFACE (iface),
322                               _gtk_marshal_VOID__STRING_INT_POINTERv);
323 
324   /**
325    * GtkEditable::delete-text:
326    * @editable: the object which received the signal
327    * @start_pos: the starting position
328    * @end_pos: the end position
329    *
330    * Emitted when text is deleted from the widget by the user.
331    *
332    * The default handler for this signal will normally be responsible for
333    * deleting the text, so by connecting to this signal and then stopping
334    * the signal with g_signal_stop_emission(), it is possible to modify the
335    * range of deleted text, or prevent it from being deleted entirely.
336    *
337    * The @start_pos and @end_pos parameters are interpreted as for
338    * [method@Gtk.Editable.delete_text].
339    */
340   signals[DELETE_TEXT] =
341     g_signal_new (I_("delete-text"),
342                   GTK_TYPE_EDITABLE,
343                   G_SIGNAL_RUN_LAST,
344                   G_STRUCT_OFFSET (GtkEditableInterface, delete_text),
345                   NULL, NULL,
346                   _gtk_marshal_VOID__INT_INT,
347                   G_TYPE_NONE, 2,
348                   G_TYPE_INT,
349                   G_TYPE_INT);
350   g_signal_set_va_marshaller (signals[DELETE_TEXT],
351                               G_TYPE_FROM_INTERFACE (iface),
352                               _gtk_marshal_VOID__INT_INTv);
353 
354   /**
355    * GtkEditable::changed:
356    * @editable: the object which received the signal
357    *
358    * Emitted at the end of a single user-visible operation on the
359    * contents.
360    *
361    * E.g., a paste operation that replaces the contents of the
362    * selection will cause only one signal emission (even though it
363    * is implemented by first deleting the selection, then inserting
364    * the new content, and may cause multiple ::notify::text signals
365    * to be emitted).
366    */
367   signals[CHANGED] =
368     g_signal_new (I_("changed"),
369                   GTK_TYPE_EDITABLE,
370                   G_SIGNAL_RUN_LAST,
371                   G_STRUCT_OFFSET (GtkEditableInterface, changed),
372                   NULL, NULL,
373                   NULL,
374                   G_TYPE_NONE, 0);
375 
376   /**
377    * GtkEditable:text: (attributes org.gtk.Property.get=gtk_editable_get_text org.gtk.Property.set=gtk_editable_set_text)
378    *
379    * The contents of the entry.
380    */
381   g_object_interface_install_property (iface,
382       g_param_spec_string ("text",
383                            P_("Text"),
384                            P_("The contents of the entry"),
385                            "",
386                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
387 
388   /**
389    * GtkEditable:cursor-position: (attributes org.gtk.Property.get=gtk_editable_get_position org.gtk.Property.set=gtk_editable_set_position)
390    *
391    * The current position of the insertion cursor in chars.
392    */
393   g_object_interface_install_property (iface,
394       g_param_spec_int ("cursor-position",
395                         P_("Cursor Position"),
396                         P_("The current position of the insertion cursor in chars"),
397                         0, GTK_ENTRY_BUFFER_MAX_SIZE,
398                         0,
399                         GTK_PARAM_READABLE));
400 
401   /**
402    * GtkEditable:enable-undo: (attributes org.gtk.Property.get=gtk_editable_get_enable_undo org.gtk.Property.setg=gtk_editable_set_enable_undo)
403    *
404    * If undo/redo should be enabled for the editable.
405    */
406   g_object_interface_install_property (iface,
407       g_param_spec_boolean ("enable-undo",
408                             P_("Enable Undo"),
409                             P_("If undo/redo should be enabled for the editable"),
410                             TRUE,
411                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
412 
413   /**
414    * GtkEditable:selection-bound:
415    *
416    * The position of the opposite end of the selection from the cursor in chars.
417    */
418   g_object_interface_install_property (iface,
419       g_param_spec_int ("selection-bound",
420                         P_("Selection Bound"),
421                         P_("The position of the opposite end of the selection from the cursor in chars"),
422                         0, GTK_ENTRY_BUFFER_MAX_SIZE,
423                         0,
424                         GTK_PARAM_READABLE));
425 
426   /**
427    * GtkEditable:editable: (attributes org.gtk.Property.get=gtk_editable_get_editable org.gtk.Property.set=gtk_editable_set_editable)
428    *
429    * Whether the entry contents can be edited.
430    */
431   g_object_interface_install_property (iface,
432       g_param_spec_boolean ("editable",
433                             P_("Editable"),
434                             P_("Whether the entry contents can be edited"),
435                             TRUE,
436                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
437 
438   /**
439    * GtkEditable:width-chars: (attributes org.gtk.Property.get=gtk_editable_get_width_chars org.gtk.Property.set=gtk_editable_set_width_chars)
440    *
441    * Number of characters to leave space for in the entry.
442    */
443   g_object_interface_install_property (iface,
444       g_param_spec_int ("width-chars",
445                         P_("Width in chars"),
446                         P_("Number of characters to leave space for in the entry"),
447                         -1, G_MAXINT,
448                         -1,
449                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
450 
451   /**
452    * GtkEditable:max-width-chars: (attributes org.gtk.Property.get=gtk_editable_get_max_width_chars org.gtk.Property.set=gtk_editable_set_max_width_chars)
453    *
454    * The desired maximum width of the entry, in characters.
455    */
456   g_object_interface_install_property (iface,
457       g_param_spec_int ("max-width-chars",
458                         P_("Maximum width in characters"),
459                         P_("The desired maximum width of the entry, in characters"),
460                         -1, G_MAXINT,
461                         -1,
462                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
463 
464   /**
465    * GtkEditable:xalign: (attributes org.gtk.Property.get=gtk_editable_get_alignment org.gtk.Property.set=gtk_editable_set_alignment)
466    *
467    * The horizontal alignment, from 0 (left) to 1 (right).
468    *
469    * Reversed for RTL layouts.
470    */
471   g_object_interface_install_property (iface,
472       g_param_spec_float ("xalign",
473                           P_("X align"),
474                           P_("The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL layouts."),
475                           0.0, 1.0,
476                           0.0,
477                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
478 }
479 
480 /**
481  * gtk_editable_insert_text: (virtual do_insert_text)
482  * @editable: a `GtkEditable`
483  * @text: the text to append
484  * @length: the length of the text in bytes, or -1
485  * @position: (inout): location of the position text will be inserted at
486  *
487  * Inserts @length bytes of @text into the contents of the
488  * widget, at position @position.
489  *
490  * Note that the position is in characters, not in bytes.
491  * The function updates @position to point after the newly
492  * inserted text.
493  */
494 void
gtk_editable_insert_text(GtkEditable * editable,const char * text,int length,int * position)495 gtk_editable_insert_text (GtkEditable *editable,
496                           const char  *text,
497                           int          length,
498                           int         *position)
499 {
500   g_return_if_fail (GTK_IS_EDITABLE (editable));
501   g_return_if_fail (position != NULL);
502 
503   if (length < 0)
504     length = strlen (text);
505 
506   GTK_EDITABLE_GET_IFACE (editable)->do_insert_text (editable, text, length, position);
507 }
508 
509 /**
510  * gtk_editable_delete_text: (virtual do_delete_text)
511  * @editable: a `GtkEditable`
512  * @start_pos: start position
513  * @end_pos: end position
514  *
515  * Deletes a sequence of characters.
516  *
517  * The characters that are deleted are those characters at positions
518  * from @start_pos up to, but not including @end_pos. If @end_pos is
519  * negative, then the characters deleted are those from @start_pos to
520  * the end of the text.
521  *
522  * Note that the positions are specified in characters, not bytes.
523  */
524 void
gtk_editable_delete_text(GtkEditable * editable,int start_pos,int end_pos)525 gtk_editable_delete_text (GtkEditable *editable,
526                           int          start_pos,
527                           int          end_pos)
528 {
529   g_return_if_fail (GTK_IS_EDITABLE (editable));
530 
531   GTK_EDITABLE_GET_IFACE (editable)->do_delete_text (editable, start_pos, end_pos);
532 }
533 
534 /**
535  * gtk_editable_get_chars:
536  * @editable: a `GtkEditable`
537  * @start_pos: start of text
538  * @end_pos: end of text
539  *
540  * Retrieves a sequence of characters.
541  *
542  * The characters that are retrieved are those characters at positions
543  * from @start_pos up to, but not including @end_pos. If @end_pos is negative,
544  * then the characters retrieved are those characters from @start_pos to
545  * the end of the text.
546  *
547  * Note that positions are specified in characters, not bytes.
548  *
549  * Returns: (transfer full): a pointer to the contents of the widget as a
550  *   string. This string is allocated by the `GtkEditable` implementation
551  *   and should be freed by the caller.
552  */
553 char *
gtk_editable_get_chars(GtkEditable * editable,int start_pos,int end_pos)554 gtk_editable_get_chars (GtkEditable *editable,
555                         int          start_pos,
556                         int          end_pos)
557 {
558   const char *text;
559   int length;
560   int start_index,end_index;
561 
562   g_return_val_if_fail (GTK_IS_EDITABLE (editable), NULL);
563 
564   text = GTK_EDITABLE_GET_IFACE (editable)->get_text (editable);
565   length = g_utf8_strlen (text, -1);
566 
567   if (end_pos < 0)
568     end_pos = length;
569 
570   start_pos = MIN (length, start_pos);
571   end_pos = MIN (length, end_pos);
572 
573   start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
574   end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
575 
576   return g_strndup (text + start_index, end_index - start_index);
577 }
578 
579 /**
580  * gtk_editable_get_text: (attributes org.gtk.Method.get_property=text)
581  * @editable: a `GtkEditable`
582  *
583  * Retrieves the contents of @editable.
584  *
585  * The returned string is owned by GTK and must not be modified or freed.
586  *
587  * Returns: (transfer none): a pointer to the contents of the editable
588  */
589 const char *
gtk_editable_get_text(GtkEditable * editable)590 gtk_editable_get_text (GtkEditable *editable)
591 {
592   g_return_val_if_fail (GTK_IS_EDITABLE (editable), NULL);
593 
594   return GTK_EDITABLE_GET_IFACE (editable)->get_text (editable);
595 }
596 
597 /**
598  * gtk_editable_set_text: (attributes org.gtk.Method.set_property=text)
599  * @editable: a `GtkEditable`
600  * @text: the text to set
601  *
602  * Sets the text in the editable to the given value.
603  *
604  * This is replacing the current contents.
605  */
606 void
gtk_editable_set_text(GtkEditable * editable,const char * text)607 gtk_editable_set_text (GtkEditable *editable,
608                        const char  *text)
609 {
610   int pos;
611 
612   g_return_if_fail (GTK_IS_EDITABLE (editable));
613 
614   g_object_freeze_notify (G_OBJECT (editable));
615   gtk_editable_delete_text (editable, 0, -1);
616   pos = 0;
617   gtk_editable_insert_text (editable, text, -1, &pos);
618   g_object_thaw_notify (G_OBJECT (editable));
619 }
620 
621 /**
622  * gtk_editable_set_position: (attributes org.gtk.Method.set_property=cursor-position)
623  * @editable: a `GtkEditable`
624  * @position: the position of the cursor
625  *
626  * Sets the cursor position in the editable to the given value.
627  *
628  * The cursor is displayed before the character with the given (base 0)
629  * index in the contents of the editable. The value must be less than
630  * or equal to the number of characters in the editable. A value of -1
631  * indicates that the position should be set after the last character
632  * of the editable. Note that @position is in characters, not in bytes.
633  */
634 void
gtk_editable_set_position(GtkEditable * editable,int position)635 gtk_editable_set_position (GtkEditable *editable,
636                            int          position)
637 {
638   g_return_if_fail (GTK_IS_EDITABLE (editable));
639 
640   GTK_EDITABLE_GET_IFACE (editable)->set_selection_bounds (editable, position, position);
641 }
642 
643 /**
644  * gtk_editable_get_position: (attributes org.gtk.Method.get_property=cursor-position)
645  * @editable: a `GtkEditable`
646  *
647  * Retrieves the current position of the cursor relative
648  * to the start of the content of the editable.
649  *
650  * Note that this position is in characters, not in bytes.
651  *
652  * Returns: the cursor position
653  */
654 int
gtk_editable_get_position(GtkEditable * editable)655 gtk_editable_get_position (GtkEditable *editable)
656 {
657   int start, end;
658 
659   g_return_val_if_fail (GTK_IS_EDITABLE (editable), 0);
660 
661   GTK_EDITABLE_GET_IFACE (editable)->get_selection_bounds (editable, &start, &end);
662 
663   return end;
664 }
665 
666 /**
667  * gtk_editable_get_selection_bounds:
668  * @editable: a `GtkEditable`
669  * @start_pos: (out) (optional): location to store the starting position
670  * @end_pos: (out) (optional): location to store the end position
671  *
672  * Retrieves the selection bound of the editable.
673  *
674  * @start_pos will be filled with the start of the selection and
675  * @end_pos with end. If no text was selected both will be identical
676  * and %FALSE will be returned.
677  *
678  * Note that positions are specified in characters, not bytes.
679  *
680  * Returns: %TRUE if there is a non-empty selection, %FALSE otherwise
681  */
682 gboolean
gtk_editable_get_selection_bounds(GtkEditable * editable,int * start_pos,int * end_pos)683 gtk_editable_get_selection_bounds (GtkEditable *editable,
684                                    int         *start_pos,
685                                    int         *end_pos)
686 {
687   int tmp_start, tmp_end;
688   gboolean result;
689 
690   g_return_val_if_fail (GTK_IS_EDITABLE (editable), FALSE);
691 
692   result = GTK_EDITABLE_GET_IFACE (editable)->get_selection_bounds (editable, &tmp_start, &tmp_end);
693 
694   if (start_pos)
695     *start_pos = MIN (tmp_start, tmp_end);
696   if (end_pos)
697     *end_pos = MAX (tmp_start, tmp_end);
698 
699   return result;
700 }
701 
702 /**
703  * gtk_editable_delete_selection:
704  * @editable: a `GtkEditable`
705  *
706  * Deletes the currently selected text of the editable.
707  *
708  * This call doesn’t do anything if there is no selected text.
709  */
710 void
gtk_editable_delete_selection(GtkEditable * editable)711 gtk_editable_delete_selection (GtkEditable *editable)
712 {
713   int start, end;
714 
715   g_return_if_fail (GTK_IS_EDITABLE (editable));
716 
717   if (gtk_editable_get_selection_bounds (editable, &start, &end))
718     gtk_editable_delete_text (editable, start, end);
719 }
720 
721 /**
722  * gtk_editable_select_region: (virtual set_selection_bounds)
723  * @editable: a `GtkEditable`
724  * @start_pos: start of region
725  * @end_pos: end of region
726  *
727  * Selects a region of text.
728  *
729  * The characters that are selected are those characters at positions
730  * from @start_pos up to, but not including @end_pos. If @end_pos is
731  * negative, then the characters selected are those characters from
732  * @start_pos to  the end of the text.
733  *
734  * Note that positions are specified in characters, not bytes.
735  */
736 void
gtk_editable_select_region(GtkEditable * editable,int start_pos,int end_pos)737 gtk_editable_select_region (GtkEditable *editable,
738                             int          start_pos,
739                             int          end_pos)
740 {
741   g_return_if_fail (GTK_IS_EDITABLE (editable));
742 
743   GTK_EDITABLE_GET_IFACE (editable)->set_selection_bounds (editable, start_pos, end_pos);
744 }
745 
746 /**
747  * gtk_editable_set_editable: (attributes org.gtk.Method.set_property=editable)
748  * @editable: a `GtkEditable`
749  * @is_editable: %TRUE if the user is allowed to edit the text
750  *   in the widget
751  *
752  * Determines if the user can edit the text in the editable widget.
753  */
754 void
gtk_editable_set_editable(GtkEditable * editable,gboolean is_editable)755 gtk_editable_set_editable (GtkEditable *editable,
756                            gboolean     is_editable)
757 {
758   g_return_if_fail (GTK_IS_EDITABLE (editable));
759 
760   g_object_set (editable, "editable", is_editable, NULL);
761 }
762 
763 /**
764  * gtk_editable_get_editable: (attributes org.gtk.Method.get_property=editable)
765  * @editable: a `GtkEditable`
766  *
767  * Retrieves whether @editable is editable.
768  *
769  * Returns: %TRUE if @editable is editable.
770  */
771 gboolean
gtk_editable_get_editable(GtkEditable * editable)772 gtk_editable_get_editable (GtkEditable *editable)
773 {
774   gboolean is_editable;
775 
776   g_return_val_if_fail (GTK_IS_EDITABLE (editable), FALSE);
777 
778   g_object_get (editable, "editable", &is_editable, NULL);
779 
780   return is_editable;
781 }
782 
783 
784 /**
785  * gtk_editable_get_alignment: (attributes org.gtk.Method.get_property=xalign)
786  * @editable: a `GtkEditable`
787  *
788  * Gets the alignment of the editable.
789  *
790  * Returns: the alignment
791  */
792 float
gtk_editable_get_alignment(GtkEditable * editable)793 gtk_editable_get_alignment (GtkEditable *editable)
794 {
795   float xalign;
796 
797   g_return_val_if_fail (GTK_IS_EDITABLE (editable), 0);
798 
799   g_object_get (editable, "xalign", &xalign, NULL);
800 
801   return xalign;
802 }
803 
804 /**
805  * gtk_editable_set_alignment: (attributes org.gtk.Method.set_property=xalign)
806  * @editable: a `GtkEditable`
807  * @xalign: The horizontal alignment, from 0 (left) to 1 (right).
808  *   Reversed for RTL layouts
809  *
810  * Sets the alignment for the contents of the editable.
811  *
812  * This controls the horizontal positioning of the contents when
813  * the displayed text is shorter than the width of the editable.
814  */
815 void
gtk_editable_set_alignment(GtkEditable * editable,float xalign)816 gtk_editable_set_alignment (GtkEditable *editable,
817                             float        xalign)
818 {
819   g_return_if_fail (GTK_IS_EDITABLE (editable));
820 
821   g_object_set (editable, "xalign", xalign, NULL);
822 }
823 
824 /**
825  * gtk_editable_get_width_chars: (attributes org.gtk.Method.get_property=width-chars)
826  * @editable: a `GtkEditable`
827  *
828  * Gets the number of characters of space reserved
829  * for the contents of the editable.
830  *
831  * Returns: number of chars to request space for, or negative if unset
832  */
833 int
gtk_editable_get_width_chars(GtkEditable * editable)834 gtk_editable_get_width_chars (GtkEditable *editable)
835 {
836   int width_chars;
837 
838   g_return_val_if_fail (GTK_IS_EDITABLE (editable), 0);
839 
840   g_object_get (editable, "width-chars", &width_chars, NULL);
841 
842   return width_chars;
843 }
844 
845 /**
846  * gtk_editable_set_width_chars: (attributes org.gtk.Method.set_property=width-chars)
847  * @editable: a `GtkEditable`
848  * @n_chars: width in chars
849  *
850  * Changes the size request of the editable to be about the
851  * right size for @n_chars characters.
852  *
853  * Note that it changes the size request, the size can still
854  * be affected by how you pack the widget into containers.
855  * If @n_chars is -1, the size reverts to the default size.
856  */
857 void
gtk_editable_set_width_chars(GtkEditable * editable,int n_chars)858 gtk_editable_set_width_chars (GtkEditable *editable,
859                               int          n_chars)
860 {
861   g_return_if_fail (GTK_IS_EDITABLE (editable));
862 
863   g_object_set (editable, "width-chars", n_chars, NULL);
864 }
865 
866 /**
867  * gtk_editable_get_max_width_chars: (attributes org.gtk.Method.get_property=max-width-chars)
868  * @editable: a `GtkEditable`
869  *
870  * Retrieves the desired maximum width of @editable, in characters.
871  *
872  * Returns: the maximum width of the entry, in characters
873  */
874 int
gtk_editable_get_max_width_chars(GtkEditable * editable)875 gtk_editable_get_max_width_chars (GtkEditable *editable)
876 {
877   int max_width_chars;
878 
879   g_return_val_if_fail (GTK_IS_EDITABLE (editable), 0);
880 
881   g_object_get (editable, "max-width-chars", &max_width_chars, NULL);
882 
883   return max_width_chars;
884 }
885 
886 /**
887  * gtk_editable_set_max_width_chars: (attributes org.gtk.Method.set_property=max-width-chars)
888  * @editable: a `GtkEditable`
889  * @n_chars: the new desired maximum width, in characters
890  *
891  * Sets the desired maximum width in characters of @editable.
892  */
893 void
gtk_editable_set_max_width_chars(GtkEditable * editable,int n_chars)894 gtk_editable_set_max_width_chars (GtkEditable *editable,
895                                   int          n_chars)
896 {
897   g_return_if_fail (GTK_IS_EDITABLE (editable));
898 
899   g_object_set (editable, "max-width-chars", n_chars, NULL);
900 }
901 
902 /**
903  * gtk_editable_get_enable_undo: (attributes org.gtk.Method.get_property=enable-undo)
904  * @editable: a `GtkEditable`
905  *
906  * Gets if undo/redo actions are enabled for @editable
907  *
908  * Returns: %TRUE if undo is enabled
909  */
910 gboolean
gtk_editable_get_enable_undo(GtkEditable * editable)911 gtk_editable_get_enable_undo (GtkEditable *editable)
912 {
913   gboolean enable_undo;
914 
915   g_return_val_if_fail (GTK_IS_EDITABLE (editable), 0);
916 
917   g_object_get (editable, "enable-undo", &enable_undo, NULL);
918 
919   return enable_undo;
920 }
921 
922 /**
923  * gtk_editable_set_enable_undo: (attributes org.gtk.Method.set_property=enable-undo)
924  * @editable: a `GtkEditable`
925  * @enable_undo: if undo/redo should be enabled
926  *
927  * If enabled, changes to @editable will be saved for undo/redo
928  * actions.
929  *
930  * This results in an additional copy of text changes and are not
931  * stored in secure memory. As such, undo is forcefully disabled
932  * when [property@Gtk.Text:visibility] is set to %FALSE.
933  */
934 void
gtk_editable_set_enable_undo(GtkEditable * editable,gboolean enable_undo)935 gtk_editable_set_enable_undo (GtkEditable *editable,
936                               gboolean     enable_undo)
937 {
938   g_return_if_fail (GTK_IS_EDITABLE (editable));
939 
940   g_object_set (editable, "enable-undo", enable_undo, NULL);
941 }
942 
943 /**
944  * gtk_editable_install_properties:
945  * @object_class: a `GObjectClass`
946  * @first_prop: property ID to use for the first property
947  *
948  * Overrides the `GtkEditable` properties for @class.
949  *
950  * This is a helper function that should be called in class_init,
951  * after installing your own properties.
952  *
953  * Note that your class must have "text", "cursor-position",
954  * "selection-bound", "editable", "width-chars", "max-width-chars",
955  * "xalign" and "enable-undo" properties for this function to work.
956  *
957  * To handle the properties in your set_property and get_property
958  * functions, you can either use [func@Gtk.Editable.delegate_set_property]
959  * and [func@Gtk.Editable.delegate_get_property] (if you are using
960  * a delegate), or remember the @first_prop offset and add it to the
961  * values in the [enum@Gtk.EditableProperties] enumeration to get the
962  * property IDs for these properties.
963  *
964  * Returns: the number of properties that were installed
965  */
966 guint
gtk_editable_install_properties(GObjectClass * object_class,guint first_prop)967 gtk_editable_install_properties (GObjectClass *object_class,
968                                  guint         first_prop)
969 {
970   g_type_set_qdata (G_TYPE_FROM_CLASS (object_class),
971                     quark_editable_data,
972                     GUINT_TO_POINTER (first_prop));
973 
974   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_TEXT, "text");
975   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_CURSOR_POSITION, "cursor-position");
976   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_SELECTION_BOUND, "selection-bound");
977   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_EDITABLE, "editable");
978   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_WIDTH_CHARS, "width-chars");
979   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_MAX_WIDTH_CHARS, "max-width-chars");
980   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_XALIGN, "xalign");
981   g_object_class_override_property (object_class, first_prop + GTK_EDITABLE_PROP_ENABLE_UNDO, "enable-undo");
982 
983   return GTK_EDITABLE_NUM_PROPERTIES;
984 }
985 
986 static void
delegate_changed(GtkEditable * delegate,gpointer editable)987 delegate_changed (GtkEditable *delegate,
988                   gpointer     editable)
989 {
990   g_signal_emit (editable, signals[CHANGED], 0);
991 }
992 
993 static void
delegate_notify(GObject * object,GParamSpec * pspec,gpointer data)994 delegate_notify (GObject    *object,
995                  GParamSpec *pspec,
996                  gpointer    data)
997 {
998   gpointer iface;
999 
1000   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (object)), gtk_editable_get_type ());
1001   if (g_object_interface_find_property (iface, pspec->name))
1002     g_object_notify (data, pspec->name);
1003 }
1004 
1005 /**
1006  * gtk_editable_get_delegate:
1007  * @editable: a `GtkEditable`
1008  *
1009  * Gets the `GtkEditable` that @editable is delegating its
1010  * implementation to.
1011  *
1012  * Typically, the delegate is a [class@Gtk.Text] widget.
1013  *
1014  * Returns: (nullable) (transfer none): the delegate `GtkEditable`
1015  */
1016 GtkEditable *
gtk_editable_get_delegate(GtkEditable * editable)1017 gtk_editable_get_delegate (GtkEditable *editable)
1018 {
1019   return get_delegate (editable);
1020 }
1021 
1022 /**
1023  * gtk_editable_init_delegate:
1024  * @editable: a `GtkEditable`
1025  *
1026  * Sets up a delegate for `GtkEditable`.
1027  *
1028  * This is assuming that the get_delegate vfunc in the `GtkEditable`
1029  * interface has been set up for the @editable's type.
1030  *
1031  * This is a helper function that should be called in instance init,
1032  * after creating the delegate object.
1033  */
1034 void
gtk_editable_init_delegate(GtkEditable * editable)1035 gtk_editable_init_delegate (GtkEditable *editable)
1036 {
1037   GtkEditable *delegate = get_delegate (editable);
1038   g_signal_connect (delegate, "notify", G_CALLBACK (delegate_notify), editable);
1039   g_signal_connect (delegate, "changed", G_CALLBACK (delegate_changed), editable);
1040 }
1041 
1042 /**
1043  * gtk_editable_finish_delegate:
1044  * @editable: a `GtkEditable`
1045  *
1046  * Undoes the setup done by [method@Gtk.Editable.init_delegate].
1047  *
1048  * This is a helper function that should be called from dispose,
1049  * before removing the delegate object.
1050  */
1051 void
gtk_editable_finish_delegate(GtkEditable * editable)1052 gtk_editable_finish_delegate (GtkEditable *editable)
1053 {
1054   GtkEditable *delegate = get_delegate (editable);
1055   g_signal_handlers_disconnect_by_func (delegate, delegate_notify, editable);
1056   g_signal_handlers_disconnect_by_func (delegate, delegate_changed, editable);
1057 }
1058 
1059 /**
1060  * gtk_editable_delegate_set_property:
1061  * @object: a `GObject`
1062  * @prop_id: a property ID
1063  * @value: value to set
1064  * @pspec: the `GParamSpec` for the property
1065  *
1066  * Sets a property on the `GtkEditable` delegate for @object.
1067  *
1068  * This is a helper function that should be called in the `set_property`
1069  * function of your `GtkEditable` implementation, before handling your
1070  * own properties.
1071  *
1072  * Returns: %TRUE if the property was found
1073  */
1074 gboolean
gtk_editable_delegate_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1075 gtk_editable_delegate_set_property (GObject      *object,
1076                                     guint         prop_id,
1077                                     const GValue *value,
1078                                     GParamSpec   *pspec)
1079 {
1080   GtkEditable *delegate = get_delegate (GTK_EDITABLE (object));
1081   GType type = G_TYPE_FROM_INSTANCE (object);
1082   guint first_prop;
1083 
1084   do {
1085     first_prop = GPOINTER_TO_UINT (g_type_get_qdata (type, quark_editable_data));
1086     type = g_type_parent (type);
1087   } while (first_prop == 0 && type != 0);
1088 
1089   if (prop_id < first_prop)
1090     return FALSE;
1091 
1092   switch (prop_id - first_prop)
1093     {
1094     case GTK_EDITABLE_PROP_TEXT:
1095       gtk_editable_set_text (delegate, g_value_get_string (value));
1096       break;
1097 
1098     case GTK_EDITABLE_PROP_EDITABLE:
1099       gtk_editable_set_editable (delegate, g_value_get_boolean (value));
1100       break;
1101 
1102     case GTK_EDITABLE_PROP_WIDTH_CHARS:
1103       gtk_editable_set_width_chars (delegate, g_value_get_int (value));
1104       break;
1105 
1106     case GTK_EDITABLE_PROP_MAX_WIDTH_CHARS:
1107       gtk_editable_set_max_width_chars (delegate, g_value_get_int (value));
1108       break;
1109 
1110     case GTK_EDITABLE_PROP_XALIGN:
1111       gtk_editable_set_alignment (delegate, g_value_get_float (value));
1112       break;
1113 
1114     case GTK_EDITABLE_PROP_ENABLE_UNDO:
1115       gtk_editable_set_enable_undo (delegate, g_value_get_boolean (value));
1116       break;
1117 
1118     default:
1119       return FALSE;
1120     }
1121 
1122   return TRUE;
1123 }
1124 
1125 /**
1126  * gtk_editable_delegate_get_property:
1127  * @object: a `GObject`
1128  * @prop_id: a property ID
1129  * @value: value to set
1130  * @pspec: the `GParamSpec` for the property
1131  *
1132  * Gets a property of the `GtkEditable` delegate for @object.
1133  *
1134  * This is helper function that should be called in the `get_property`
1135  * function of your `GtkEditable` implementation, before handling your
1136  * own properties.
1137  *
1138  * Returns: %TRUE if the property was found
1139  */
1140 gboolean
gtk_editable_delegate_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1141 gtk_editable_delegate_get_property (GObject    *object,
1142                                     guint       prop_id,
1143                                     GValue     *value,
1144                                     GParamSpec *pspec)
1145 {
1146   GtkEditable *delegate = get_delegate (GTK_EDITABLE (object));
1147   int cursor_position, selection_bound;
1148   GType type = G_TYPE_FROM_INSTANCE (object);
1149   guint first_prop;
1150 
1151   do {
1152     first_prop = GPOINTER_TO_UINT (g_type_get_qdata (type, quark_editable_data));
1153     type = g_type_parent (type);
1154   } while (first_prop == 0 && type != 0);
1155 
1156   if (prop_id < first_prop)
1157     return FALSE;
1158 
1159   switch (prop_id - first_prop)
1160     {
1161     case GTK_EDITABLE_PROP_TEXT:
1162       g_value_set_string (value, gtk_editable_get_text (delegate));
1163       break;
1164 
1165     case GTK_EDITABLE_PROP_CURSOR_POSITION:
1166       gtk_editable_get_selection_bounds (delegate, &cursor_position, &selection_bound);
1167       g_value_set_int (value, cursor_position);
1168       break;
1169 
1170     case GTK_EDITABLE_PROP_SELECTION_BOUND:
1171       gtk_editable_get_selection_bounds (delegate, &cursor_position, &selection_bound);
1172       g_value_set_int (value, selection_bound);
1173       break;
1174 
1175     case GTK_EDITABLE_PROP_EDITABLE:
1176       g_value_set_boolean (value, gtk_editable_get_editable (delegate));
1177       break;
1178 
1179     case GTK_EDITABLE_PROP_WIDTH_CHARS:
1180       g_value_set_int (value, gtk_editable_get_width_chars (delegate));
1181       break;
1182 
1183     case GTK_EDITABLE_PROP_MAX_WIDTH_CHARS:
1184       g_value_set_int (value, gtk_editable_get_max_width_chars (delegate));
1185       break;
1186 
1187     case GTK_EDITABLE_PROP_XALIGN:
1188       g_value_set_float (value, gtk_editable_get_alignment (delegate));
1189       break;
1190 
1191     case GTK_EDITABLE_PROP_ENABLE_UNDO:
1192       g_value_set_boolean (value, gtk_editable_get_enable_undo (delegate));
1193       break;
1194 
1195     default:
1196       return FALSE;
1197     }
1198 
1199   return TRUE;
1200 }
1201