]> Pileus Git - ~andy/gtk/blob - gtk/gtkcomboboxtext.c
Merge branch 'bgo593793-filechooser-recent-folders-master'
[~andy/gtk] / gtk / gtkcomboboxtext.c
1 /* GTK - The GIMP Toolkit
2  *
3  * Copyright (C) 2010 Christian Dywan
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include "gtkcomboboxtext.h"
22 #include "gtkcombobox.h"
23 #include "gtkcellrenderertext.h"
24 #include "gtkcelllayout.h"
25 #include "gtkbuildable.h"
26 #include "gtkbuilderprivate.h"
27
28 #include <string.h>
29
30 /**
31  * SECTION:gtkcomboboxtext
32  * @Short_description: A simple, text-only combo box
33  * @Title: GtkComboBoxText
34  * @See_also: #GtkComboBox
35  *
36  * A GtkComboBoxText is a simple variant of #GtkComboBox that hides
37  * the model-view complexity for simple text-only use cases.
38  *
39  * To create a GtkComboBoxText, use gtk_combo_box_text_new() or
40  * gtk_combo_box_text_new_with_entry().
41  *
42  * You can add items to a GtkComboBoxText with
43  * gtk_combo_box_text_append_text(), gtk_combo_box_text_insert_text()
44  * or gtk_combo_box_text_prepend_text() and remove options with
45  * gtk_combo_box_text_remove().
46  *
47  * If the GtkComboBoxText contains an entry (via the 'has-entry' property),
48  * its contents can be retrieved using gtk_combo_box_text_get_active_text().
49  * The entry itself can be accessed by calling gtk_bin_get_child() on the
50  * combo box.
51  *
52  * <refsect2 id="GtkComboBoxText-BUILDER-UI">
53  * <title>GtkComboBoxText as GtkBuildable</title>
54  * <para>
55  * The GtkComboBoxText implementation of the GtkBuildable interface
56  * supports adding items directly using the &lt;items&gt; element
57  * and specifying &lt;item&gt; elements for each item. Each &lt;item&gt;
58  * element supports the regular translation attributes "translatable",
59  * "context" and "comments".
60  *
61  * <example>
62  * <title>A UI definition fragment specifying GtkComboBoxText items</title>
63  * <programlisting><![CDATA[
64  * <object class="GtkComboBoxText">
65  *   <items>
66  *     <item translatable="yes">Factory</item>
67  *     <item translatable="yes">Home</item>
68  *     <item translatable="yes">Subway</item>
69  *   </items>
70  * </object>
71  * ]]></programlisting>
72  * </example>
73  * </para>
74  * </refsect2>
75  */
76
77 static void     gtk_combo_box_text_buildable_interface_init     (GtkBuildableIface *iface);
78 static gboolean gtk_combo_box_text_buildable_custom_tag_start   (GtkBuildable     *buildable,
79                                                                  GtkBuilder       *builder,
80                                                                  GObject          *child,
81                                                                  const gchar      *tagname,
82                                                                  GMarkupParser    *parser,
83                                                                  gpointer         *data);
84
85 static void     gtk_combo_box_text_buildable_custom_finished    (GtkBuildable     *buildable,
86                                                                  GtkBuilder       *builder,
87                                                                  GObject          *child,
88                                                                  const gchar      *tagname,
89                                                                  gpointer          user_data);
90
91 static GtkBuildableIface *buildable_parent_iface = NULL;
92
93 G_DEFINE_TYPE_WITH_CODE (GtkComboBoxText, gtk_combo_box_text, GTK_TYPE_COMBO_BOX,
94                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
95                                                 gtk_combo_box_text_buildable_interface_init));
96
97 static GObject *
98 gtk_combo_box_text_constructor (GType                  type,
99                                 guint                  n_construct_properties,
100                                 GObjectConstructParam *construct_properties)
101 {
102   GObject            *object;
103
104   object = G_OBJECT_CLASS (gtk_combo_box_text_parent_class)->constructor
105     (type, n_construct_properties, construct_properties);
106
107   if (!gtk_combo_box_get_has_entry (GTK_COMBO_BOX (object)))
108     {
109       GtkCellRenderer *cell;
110
111       cell = gtk_cell_renderer_text_new ();
112       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), cell, TRUE);
113       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), cell,
114                                       "text", 0,
115                                       NULL);
116     }
117
118   return object;
119 }
120
121 static void
122 gtk_combo_box_text_init (GtkComboBoxText *combo_box)
123 {
124   GtkListStore *store;
125
126   store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
127   gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
128   g_object_unref (store);
129 }
130
131 static void
132 gtk_combo_box_text_class_init (GtkComboBoxTextClass *klass)
133 {
134   GObjectClass *object_class;
135
136   object_class = (GObjectClass *)klass;
137   object_class->constructor = gtk_combo_box_text_constructor;
138 }
139
140 static void
141 gtk_combo_box_text_buildable_interface_init (GtkBuildableIface *iface)
142 {
143   buildable_parent_iface = g_type_interface_peek_parent (iface);
144
145   iface->custom_tag_start = gtk_combo_box_text_buildable_custom_tag_start;
146   iface->custom_finished = gtk_combo_box_text_buildable_custom_finished;
147 }
148
149 typedef struct {
150   GtkBuilder    *builder;
151   GObject       *object;
152   const gchar   *domain;
153
154   GString       *string;
155
156   gchar         *context;
157   guint          translatable : 1;
158
159   guint          is_text : 1;
160 } ItemParserData;
161
162 static void
163 item_start_element (GMarkupParseContext *context,
164                     const gchar         *element_name,
165                     const gchar        **names,
166                     const gchar        **values,
167                     gpointer             user_data,
168                     GError             **error)
169 {
170   ItemParserData *data = (ItemParserData*)user_data;
171   guint i;
172
173   if (strcmp (element_name, "item") == 0)
174     {
175       data->is_text = TRUE;
176
177       for (i = 0; names[i]; i++)
178         {
179           if (strcmp (names[i], "translatable") == 0)
180             {
181               gboolean bval;
182
183               if (!_gtk_builder_boolean_from_string (values[i], &bval,
184                                                      error))
185                 return;
186
187               data->translatable = bval;
188             }
189           else if (strcmp (names[i], "comments") == 0)
190             {
191               /* do nothing, comments are for translators */
192             }
193           else if (strcmp (names[i], "context") == 0) 
194             data->context = g_strdup (values[i]);
195           else
196             g_warning ("Unknown custom combo box item attribute: %s", names[i]);
197         }
198     }
199 }
200
201 static void
202 item_text (GMarkupParseContext *context,
203            const gchar         *text,
204            gsize                text_len,
205            gpointer             user_data,
206            GError             **error)
207 {
208   ItemParserData *data = (ItemParserData*)user_data;
209
210   if (data->is_text)
211     g_string_append_len (data->string, text, text_len);
212 }
213
214 static void
215 item_end_element (GMarkupParseContext *context,
216                   const gchar         *element_name,
217                   gpointer             user_data,
218                   GError             **error)
219 {
220   ItemParserData *data = (ItemParserData*)user_data;
221
222   /* Append the translated strings */
223   if (data->string->len)
224     {
225       if (data->translatable)
226         {
227           gchar *translated;
228
229           /* FIXME: This will not use the domain set in the .ui file,
230            * since the parser is not telling the builder about the domain.
231            * However, it will work for gtk_builder_set_translation_domain() calls.
232            */
233           translated = _gtk_builder_parser_translate (data->domain,
234                                                       data->context,
235                                                       data->string->str);
236           g_string_set_size (data->string, 0);
237           g_string_append (data->string, translated);
238         }
239
240       gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (data->object), data->string->str);
241     }
242
243   data->translatable = FALSE;
244   g_string_set_size (data->string, 0);
245   g_free (data->context);
246   data->context = NULL;
247   data->is_text = FALSE;
248 }
249
250 static const GMarkupParser item_parser =
251   {
252     item_start_element,
253     item_end_element,
254     item_text
255   };
256
257 static gboolean
258 gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable     *buildable,
259                                                GtkBuilder       *builder,
260                                                GObject          *child,
261                                                const gchar      *tagname,
262                                                GMarkupParser    *parser,
263                                                gpointer         *data)
264 {
265   if (buildable_parent_iface->custom_tag_start (buildable, builder, child, 
266                                                 tagname, parser, data))
267     return TRUE;
268
269   if (strcmp (tagname, "items") == 0)
270     {
271       ItemParserData *parser_data;
272
273       parser_data = g_slice_new0 (ItemParserData);
274       parser_data->builder = g_object_ref (builder);
275       parser_data->object = g_object_ref (buildable);
276       parser_data->domain = gtk_builder_get_translation_domain (builder);
277       parser_data->string = g_string_new ("");
278       *parser = item_parser;
279       *data = parser_data;
280       return TRUE;
281     }
282   return FALSE;
283 }
284
285 static void
286 gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable,
287                                               GtkBuilder   *builder,
288                                               GObject      *child,
289                                               const gchar  *tagname,
290                                               gpointer      user_data)
291 {
292   ItemParserData *data;
293
294   buildable_parent_iface->custom_finished (buildable, builder, child, 
295                                            tagname, user_data);
296
297   if (strcmp (tagname, "items") == 0)
298     {
299       data = (ItemParserData*)user_data;
300
301       g_object_unref (data->object);
302       g_object_unref (data->builder);
303       g_string_free (data->string, TRUE);
304       g_slice_free (ItemParserData, data);
305     }
306 }
307
308 /**
309  * gtk_combo_box_text_new:
310  *
311  * Creates a new #GtkComboBoxText, which is a #GtkComboBox just displaying
312  * strings. See gtk_combo_box_entry_new_with_text().
313  *
314  * Return value: A new #GtkComboBoxText
315  *
316  * Since: 2.24
317  */
318 GtkWidget *
319 gtk_combo_box_text_new (void)
320 {
321   return g_object_new (GTK_TYPE_COMBO_BOX_TEXT,
322                        "entry-text-column", 0,
323                        "id-column", 1,
324                        NULL);
325 }
326
327 /**
328  * gtk_combo_box_text_new_with_entry:
329  *
330  * Creates a new #GtkComboBoxText, which is a #GtkComboBox just displaying
331  * strings. The combo box created by this function has an entry.
332  *
333  * Return value: a new #GtkComboBoxText
334  *
335  * Since: 2.24
336  */
337 GtkWidget *
338 gtk_combo_box_text_new_with_entry (void)
339 {
340   return g_object_new (GTK_TYPE_COMBO_BOX_TEXT,
341                        "has-entry", TRUE,
342                        "entry-text-column", 0,
343                        "id-column", 1,
344                        NULL);
345 }
346
347 /**
348  * gtk_combo_box_text_append_text:
349  * @combo_box: A #GtkComboBoxText
350  * @text: A string
351  *
352  * Appends @text to the list of strings stored in @combo_box.
353  *
354  * This is the same as calling gtk_combo_box_text_insert_text() with a
355  * position of -1.
356  *
357  * Since: 2.24
358  */
359 void
360 gtk_combo_box_text_append_text (GtkComboBoxText *combo_box,
361                                 const gchar     *text)
362 {
363   gtk_combo_box_text_insert (combo_box, -1, NULL, text);
364 }
365
366 /**
367  * gtk_combo_box_text_prepend_text:
368  * @combo_box: A #GtkComboBox
369  * @text: A string
370  *
371  * Prepends @text to the list of strings stored in @combo_box.
372  *
373  * This is the same as calling gtk_combo_box_text_insert_text() with a
374  * position of 0.
375  *
376  * Since: 2.24
377  */
378 void
379 gtk_combo_box_text_prepend_text (GtkComboBoxText *combo_box,
380                                  const gchar     *text)
381 {
382   gtk_combo_box_text_insert (combo_box, 0, NULL, text);
383 }
384
385 /**
386  * gtk_combo_box_text_insert_text:
387  * @combo_box: A #GtkComboBoxText
388  * @position: An index to insert @text
389  * @text: A string
390  *
391  * Inserts @text at @position in the list of strings stored in @combo_box.
392  *
393  * If @position is negative then @text is appended.
394  *
395  * This is the same as calling gtk_combo_box_text_insert() with a %NULL
396  * ID string.
397  *
398  * Since: 2.24
399  */
400 void
401 gtk_combo_box_text_insert_text (GtkComboBoxText *combo_box,
402                                 gint             position,
403                                 const gchar     *text)
404 {
405   gtk_combo_box_text_insert (combo_box, position, NULL, text);
406 }
407
408 /**
409  * gtk_combo_box_text_append:
410  * @combo_box: A #GtkComboBoxText
411  * @id: (allow-none): a string ID for this value, or %NULL
412  * @text: A string
413  *
414  * Appends @text to the list of strings stored in @combo_box.
415  * If @id is non-%NULL then it is used as the ID of the row.
416  *
417  * This is the same as calling gtk_combo_box_text_insert() with a
418  * position of -1.
419  *
420  * Since: 2.24
421  */
422 void
423 gtk_combo_box_text_append (GtkComboBoxText *combo_box,
424                            const gchar     *id,
425                            const gchar     *text)
426 {
427   gtk_combo_box_text_insert (combo_box, -1, id, text);
428 }
429
430 /**
431  * gtk_combo_box_text_prepend:
432  * @combo_box: A #GtkComboBox
433  * @id: (allow-none): a string ID for this value, or %NULL
434  * @text: a string
435  *
436  * Prepends @text to the list of strings stored in @combo_box.
437  * If @id is non-%NULL then it is used as the ID of the row.
438  *
439  * This is the same as calling gtk_combo_box_text_insert() with a
440  * position of 0.
441  *
442  * Since: 2.24
443  */
444 void
445 gtk_combo_box_text_prepend (GtkComboBoxText *combo_box,
446                             const gchar     *id,
447                             const gchar     *text)
448 {
449   gtk_combo_box_text_insert (combo_box, 0, id, text);
450 }
451
452
453 /**
454  * gtk_combo_box_text_insert:
455  * @combo_box: A #GtkComboBoxText
456  * @position: An index to insert @text
457  * @id: (allow-none): a string ID for this value, or %NULL
458  * @text: A string to display
459  *
460  * Inserts @text at @position in the list of strings stored in @combo_box.
461  * If @id is non-%NULL then it is used as the ID of the row.  See
462  * #GtkComboBox::id-column.
463  *
464  * If @position is negative then @text is appended.
465  *
466  * Since: 3.0
467  */
468 void
469 gtk_combo_box_text_insert (GtkComboBoxText *combo_box,
470                            gint             position,
471                            const gchar     *id,
472                            const gchar     *text)
473 {
474   GtkListStore *store;
475   GtkTreeIter iter;
476   gint text_column;
477   gint column_type;
478
479   g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
480   g_return_if_fail (text != NULL);
481
482   store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
483   g_return_if_fail (GTK_IS_LIST_STORE (store));
484
485   text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box));
486
487   if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box)))
488     g_return_if_fail (text_column >= 0);
489   else if (text_column < 0)
490     text_column = 0;
491
492   column_type = gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), text_column);
493   g_return_if_fail (column_type == G_TYPE_STRING);
494
495   if (position < 0)
496     gtk_list_store_append (store, &iter);
497   else
498     gtk_list_store_insert (store, &iter, position);
499
500   gtk_list_store_set (store, &iter, text_column, text, -1);
501
502   if (id != NULL)
503     {
504       gint id_column;
505
506       id_column = gtk_combo_box_get_id_column (GTK_COMBO_BOX (combo_box));
507       g_return_if_fail (id_column >= 0);
508       column_type = gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), id_column);
509       g_return_if_fail (column_type == G_TYPE_STRING);
510
511       gtk_list_store_set (store, &iter, id_column, id, -1);
512     }
513 }
514
515 /**
516  * gtk_combo_box_text_remove:
517  * @combo_box: A #GtkComboBox
518  * @position: Index of the item to remove
519  *
520  * Removes the string at @position from @combo_box.
521  *
522  * Since: 2.24
523  */
524 void
525 gtk_combo_box_text_remove (GtkComboBoxText *combo_box,
526                            gint             position)
527 {
528   GtkTreeModel *model;
529   GtkListStore *store;
530   GtkTreeIter iter;
531
532   g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
533   g_return_if_fail (position >= 0);
534
535   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
536   store = GTK_LIST_STORE (model);
537   g_return_if_fail (GTK_IS_LIST_STORE (store));
538
539   if (gtk_tree_model_iter_nth_child (model, &iter, NULL, position))
540     gtk_list_store_remove (store, &iter);
541 }
542
543 /**
544  * gtk_combo_box_text_remove_all:
545  * @combo_box: A #GtkComboBoxText
546  *
547  * Removes all the text entries from the combo box.
548  *
549  * Since: 3.0
550  */
551 void
552 gtk_combo_box_text_remove_all (GtkComboBoxText *combo_box)
553 {
554   GtkListStore *store;
555
556   g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
557
558   store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
559   gtk_list_store_clear (store);
560 }
561
562 /**
563  * gtk_combo_box_text_get_active_text:
564  * @combo_box: A #GtkComboBoxText
565  *
566  * Returns the currently active string in @combo_box, or %NULL
567  * if none is selected. If @combo_box contains an entry, this
568  * function will return its contents (which will not necessarily
569  * be an item from the list).
570  *
571  * Returns: a newly allocated string containing the currently
572  *     active text. Must be freed with g_free().
573  *
574  * Since: 2.24
575  */
576 gchar *
577 gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box)
578 {
579   GtkTreeIter iter;
580   gchar *text = NULL;
581
582   g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box), NULL);
583
584  if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box)))
585    {
586      GtkWidget *entry;
587
588      entry = gtk_bin_get_child (GTK_BIN (combo_box));
589      text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
590    }
591   else if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
592     {
593       GtkTreeModel *model;
594       gint text_column;
595       gint column_type;
596
597       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
598       g_return_val_if_fail (GTK_IS_LIST_STORE (model), NULL);
599       text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box));
600       g_return_val_if_fail (text_column >= 0, NULL);
601       column_type = gtk_tree_model_get_column_type (model, text_column);
602       g_return_val_if_fail (column_type == G_TYPE_STRING, NULL);
603       gtk_tree_model_get (model, &iter, text_column, &text, -1);
604     }
605
606   return text;
607 }