]> Pileus Git - ~andy/gtk/blob - gtk/gtkcomboboxtext.c
Added GtkBuildable support for specifying <items> in GtkComboBoxText.
[~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  *
74  */
75
76 static void     gtk_combo_box_text_buildable_interface_init     (GtkBuildableIface *iface);
77 static gboolean gtk_combo_box_text_buildable_custom_tag_start   (GtkBuildable     *buildable,
78                                                                  GtkBuilder       *builder,
79                                                                  GObject          *child,
80                                                                  const gchar      *tagname,
81                                                                  GMarkupParser    *parser,
82                                                                  gpointer         *data);
83
84 static void     gtk_combo_box_text_buildable_custom_finished    (GtkBuildable     *buildable,
85                                                                  GtkBuilder       *builder,
86                                                                  GObject          *child,
87                                                                  const gchar      *tagname,
88                                                                  gpointer          user_data);
89
90 static GtkBuildableIface *buildable_parent_iface = NULL;
91
92 G_DEFINE_TYPE_WITH_CODE (GtkComboBoxText, gtk_combo_box_text, GTK_TYPE_COMBO_BOX,
93                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
94                                                 gtk_combo_box_text_buildable_interface_init));
95
96 static GObject *
97 gtk_combo_box_text_constructor (GType                  type,
98                                 guint                  n_construct_properties,
99                                 GObjectConstructParam *construct_properties)
100 {
101   GObject            *object;
102
103   object = G_OBJECT_CLASS (gtk_combo_box_text_parent_class)->constructor
104     (type, n_construct_properties, construct_properties);
105
106   if (!gtk_combo_box_get_has_entry (GTK_COMBO_BOX (object)))
107     {
108       GtkCellRenderer *cell;
109
110       cell = gtk_cell_renderer_text_new ();
111       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), cell, TRUE);
112       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), cell,
113                                       "text", 0,
114                                       NULL);
115     }
116
117   return object;
118 }
119
120 static void
121 gtk_combo_box_text_init (GtkComboBoxText *combo_box)
122 {
123   GtkListStore *store;
124
125   store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
126   gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
127   g_object_unref (store);
128 }
129
130 static void
131 gtk_combo_box_text_class_init (GtkComboBoxTextClass *klass)
132 {
133   GObjectClass *object_class;
134
135   object_class = (GObjectClass *)klass;
136   object_class->constructor = gtk_combo_box_text_constructor;
137 }
138
139 static void
140 gtk_combo_box_text_buildable_interface_init (GtkBuildableIface *iface)
141 {
142   buildable_parent_iface = g_type_interface_peek_parent (iface);
143
144   iface->custom_tag_start = gtk_combo_box_text_buildable_custom_tag_start;
145   iface->custom_finished = gtk_combo_box_text_buildable_custom_finished;
146 }
147
148 typedef struct {
149   GtkBuilder    *builder;
150   GObject       *object;
151   const gchar   *domain;
152
153   gchar         *context;
154   gchar         *string;
155   guint          translatable : 1;
156
157   guint          is_text : 1;
158 } ItemParserData;
159
160 static void
161 item_start_element (GMarkupParseContext *context,
162                     const gchar         *element_name,
163                     const gchar        **names,
164                     const gchar        **values,
165                     gpointer             user_data,
166                     GError             **error)
167 {
168   ItemParserData *data = (ItemParserData*)user_data;
169   guint i;
170
171   if (strcmp (element_name, "item") == 0)
172     {
173       data->is_text = TRUE;
174
175       for (i = 0; names[i]; i++)
176         {
177           if (strcmp (names[i], "translatable") == 0)
178             {
179               gboolean bval;
180
181               if (!_gtk_builder_boolean_from_string (values[i], &bval,
182                                                      error))
183                 return;
184
185               data->translatable = bval;
186             }
187           else if (strcmp (names[i], "comments") == 0)
188             {
189               /* do nothing, comments are for translators */
190             }
191           else if (strcmp (names[i], "context") == 0) 
192             data->context = g_strdup (values[i]);
193           else
194             g_warning ("Unknown custom combo box item attribute: %s", names[i]);
195         }
196     }
197 }
198
199 static void
200 item_text (GMarkupParseContext *context,
201            const gchar         *text,
202            gsize                text_len,
203            gpointer             user_data,
204            GError             **error)
205 {
206   ItemParserData *data = (ItemParserData*)user_data;
207   gchar *string;
208
209   if (!data->is_text)
210     return;
211
212   string = g_strndup (text, text_len);
213
214   if (data->translatable && text_len)
215     {
216       gchar *translated;
217
218       /* FIXME: This will not use the domain set in the .ui file,
219        * since the parser is not telling the builder about the domain.
220        * However, it will work for gtk_builder_set_translation_domain() calls.
221        */
222       translated = _gtk_builder_parser_translate (data->domain,
223                                                   data->context,
224                                                   string);
225       g_free (string);
226       string = translated;
227     }
228
229   data->string = string;
230 }
231
232 static void
233 item_end_element (GMarkupParseContext *context,
234                   const gchar         *element_name,
235                   gpointer             user_data,
236                   GError             **error)
237 {
238   ItemParserData *data = (ItemParserData*)user_data;
239
240   /* Append the translated strings */
241   if (data->string)
242     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (data->object), data->string);
243
244   data->translatable = FALSE;
245   g_free (data->context);
246   g_free (data->string);
247   data->context = NULL;
248   data->string = NULL;
249   data->is_text = FALSE;
250 }
251
252 static const GMarkupParser item_parser =
253   {
254     item_start_element,
255     item_end_element,
256     item_text
257   };
258
259 static gboolean
260 gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable     *buildable,
261                                                GtkBuilder       *builder,
262                                                GObject          *child,
263                                                const gchar      *tagname,
264                                                GMarkupParser    *parser,
265                                                gpointer         *data)
266 {
267   if (buildable_parent_iface->custom_tag_start (buildable, builder, child, 
268                                                 tagname, parser, data))
269     return TRUE;
270
271   if (strcmp (tagname, "items") == 0)
272     {
273       ItemParserData *parser_data;
274
275       parser_data = g_slice_new0 (ItemParserData);
276       parser_data->builder = g_object_ref (builder);
277       parser_data->object = g_object_ref (buildable);
278       parser_data->domain = gtk_builder_get_translation_domain (builder);
279       *parser = item_parser;
280       *data = parser_data;
281       return TRUE;
282     }
283   return FALSE;
284 }
285
286 static void
287 gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable,
288                                               GtkBuilder   *builder,
289                                               GObject      *child,
290                                               const gchar  *tagname,
291                                               gpointer      user_data)
292 {
293   ItemParserData *data;
294
295   buildable_parent_iface->custom_finished (buildable, builder, child, 
296                                            tagname, user_data);
297
298   if (strcmp (tagname, "items") == 0)
299     {
300       data = (ItemParserData*)user_data;
301
302       g_object_unref (data->object);
303       g_object_unref (data->builder);
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  * @text: A string
412  *
413  * Appends @text to the list of strings stored in @combo_box.  If @id is
414  * non-%NULL then it is used as the ID of the row.
415  *
416  * This is the same as calling gtk_combo_box_text_insert() with a
417  * position of -1.
418  *
419  * Since: 2.24
420  */
421 void
422 gtk_combo_box_text_append (GtkComboBoxText *combo_box,
423                            const gchar     *id,
424                            const gchar     *text)
425 {
426   gtk_combo_box_text_insert (combo_box, -1, id, text);
427 }
428
429 /**
430  * gtk_combo_box_text_prepend:
431  * @combo_box: A #GtkComboBox
432  * @id: (allow-none): a string ID for this value, or %NULL
433  * @text: a string
434  *
435  * Prepends @text to the list of strings stored in @combo_box.  If @id
436  * is non-%NULL then it is used as the ID of the row.
437  *
438  * This is the same as calling gtk_combo_box_text_insert() with a
439  * position of 0.
440  *
441  * Since: 2.24
442  */
443 void
444 gtk_combo_box_text_prepend (GtkComboBoxText *combo_box,
445                             const gchar     *id,
446                             const gchar     *text)
447 {
448   gtk_combo_box_text_insert (combo_box, 0, id, text);
449 }
450
451
452 /**
453  * gtk_combo_box_text_insert:
454  * @combo_box: A #GtkComboBoxText
455  * @position: An index to insert @text
456  * @id: (allow-none): a string ID for this value, or %NULL
457  * @text: A string to display
458  *
459  * Inserts @text at @position in the list of strings stored in @combo_box.
460  * If @id is non-%NULL then it is used as the ID of the row.  See
461  * #GtkComboBox::id-column.
462  *
463  * If @position is negative then @text is appended.
464  *
465  * Since: 3.0
466  */
467 void
468 gtk_combo_box_text_insert (GtkComboBoxText *combo_box,
469                            gint             position,
470                            const gchar     *id,
471                            const gchar     *text)
472 {
473   GtkListStore *store;
474   GtkTreeIter iter;
475   gint text_column;
476   gint column_type;
477
478   g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
479   g_return_if_fail (text != NULL);
480
481   store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
482   g_return_if_fail (GTK_IS_LIST_STORE (store));
483
484   text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box));
485
486   if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo_box)))
487     g_return_if_fail (text_column >= 0);
488   else if (text_column < 0)
489     text_column = 0;
490
491   column_type = gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), text_column);
492   g_return_if_fail (column_type == G_TYPE_STRING);
493
494   if (position < 0)
495     gtk_list_store_append (store, &iter);
496   else
497     gtk_list_store_insert (store, &iter, position);
498
499   gtk_list_store_set (store, &iter, text_column, text, -1);
500
501   if (id != NULL)
502     {
503       gint id_column;
504
505       id_column = gtk_combo_box_get_id_column (GTK_COMBO_BOX (combo_box));
506       g_return_if_fail (id_column >= 0);
507       column_type = gtk_tree_model_get_column_type (GTK_TREE_MODEL (store), id_column);
508       g_return_if_fail (column_type == G_TYPE_STRING);
509
510       gtk_list_store_set (store, &iter, id_column, id, -1);
511     }
512 }
513
514 /**
515  * gtk_combo_box_text_remove:
516  * @combo_box: A #GtkComboBox
517  * @position: Index of the item to remove
518  *
519  * Removes the string at @position from @combo_box.
520  *
521  * Since: 2.24
522  */
523 void
524 gtk_combo_box_text_remove (GtkComboBoxText *combo_box,
525                            gint             position)
526 {
527   GtkTreeModel *model;
528   GtkListStore *store;
529   GtkTreeIter iter;
530
531   g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
532   g_return_if_fail (position >= 0);
533
534   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
535   store = GTK_LIST_STORE (model);
536   g_return_if_fail (GTK_IS_LIST_STORE (store));
537
538   if (gtk_tree_model_iter_nth_child (model, &iter, NULL, position))
539     gtk_list_store_remove (store, &iter);
540 }
541
542 /**
543  * gtk_combo_box_text_remove_all:
544  * @combo_box: A #GtkComboBoxText
545  *
546  * Removes all the text entries from the combo box.
547  *
548  * Since: 3.0
549  */
550 void
551 gtk_combo_box_text_remove_all (GtkComboBoxText *combo_box)
552 {
553   GtkListStore *store;
554
555   g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box));
556
557   store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
558   gtk_list_store_clear (store);
559 }
560
561 /**
562  * gtk_combo_box_text_get_active_text:
563  * @combo_box: A #GtkComboBoxText
564  *
565  * Returns the currently active string in @combo_box, or %NULL if none
566  * is selected. If @combo_box contains an entry, this function will return
567  * its contents (which will not necessarily be an item from the list).
568  *
569  * Returns: a newly allocated string containing the currently active text.
570  *     Must be freed with g_free().
571  *
572  * Since: 2.24
573  */
574 gchar *
575 gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box)
576 {
577   GtkTreeIter iter;
578   gchar *text = NULL;
579
580   g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box), NULL);
581
582   if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
583     {
584       GtkTreeModel *model;
585       gint text_column;
586       gint column_type;
587
588       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
589       g_return_val_if_fail (GTK_IS_LIST_STORE (model), NULL);
590       text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box));
591       g_return_val_if_fail (text_column >= 0, NULL);
592       column_type = gtk_tree_model_get_column_type (model, text_column);
593       g_return_val_if_fail (column_type == G_TYPE_STRING, NULL);
594       gtk_tree_model_get (model, &iter, text_column, &text, -1);
595     }
596
597   return text;
598 }