]> Pileus Git - ~andy/gtk/blob - gtk/gtkcomboboxtext.c
Merge branch 'master' into broadway2
[~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   gchar         *context;
155   gchar         *string;
156   guint          translatable : 1;
157
158   guint          is_text : 1;
159 } ItemParserData;
160
161 static void
162 item_start_element (GMarkupParseContext *context,
163                     const gchar         *element_name,
164                     const gchar        **names,
165                     const gchar        **values,
166                     gpointer             user_data,
167                     GError             **error)
168 {
169   ItemParserData *data = (ItemParserData*)user_data;
170   guint i;
171
172   if (strcmp (element_name, "item") == 0)
173     {
174       data->is_text = TRUE;
175
176       for (i = 0; names[i]; i++)
177         {
178           if (strcmp (names[i], "translatable") == 0)
179             {
180               gboolean bval;
181
182               if (!_gtk_builder_boolean_from_string (values[i], &bval,
183                                                      error))
184                 return;
185
186               data->translatable = bval;
187             }
188           else if (strcmp (names[i], "comments") == 0)
189             {
190               /* do nothing, comments are for translators */
191             }
192           else if (strcmp (names[i], "context") == 0) 
193             data->context = g_strdup (values[i]);
194           else
195             g_warning ("Unknown custom combo box item attribute: %s", names[i]);
196         }
197     }
198 }
199
200 static void
201 item_text (GMarkupParseContext *context,
202            const gchar         *text,
203            gsize                text_len,
204            gpointer             user_data,
205            GError             **error)
206 {
207   ItemParserData *data = (ItemParserData*)user_data;
208   gchar *string;
209
210   if (!data->is_text)
211     return;
212
213   string = g_strndup (text, text_len);
214
215   if (data->translatable && text_len)
216     {
217       gchar *translated;
218
219       /* FIXME: This will not use the domain set in the .ui file,
220        * since the parser is not telling the builder about the domain.
221        * However, it will work for gtk_builder_set_translation_domain() calls.
222        */
223       translated = _gtk_builder_parser_translate (data->domain,
224                                                   data->context,
225                                                   string);
226       g_free (string);
227       string = translated;
228     }
229
230   data->string = string;
231 }
232
233 static void
234 item_end_element (GMarkupParseContext *context,
235                   const gchar         *element_name,
236                   gpointer             user_data,
237                   GError             **error)
238 {
239   ItemParserData *data = (ItemParserData*)user_data;
240
241   /* Append the translated strings */
242   if (data->string)
243     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (data->object), data->string);
244
245   data->translatable = FALSE;
246   g_free (data->context);
247   g_free (data->string);
248   data->context = NULL;
249   data->string = NULL;
250   data->is_text = FALSE;
251 }
252
253 static const GMarkupParser item_parser =
254   {
255     item_start_element,
256     item_end_element,
257     item_text
258   };
259
260 static gboolean
261 gtk_combo_box_text_buildable_custom_tag_start (GtkBuildable     *buildable,
262                                                GtkBuilder       *builder,
263                                                GObject          *child,
264                                                const gchar      *tagname,
265                                                GMarkupParser    *parser,
266                                                gpointer         *data)
267 {
268   if (buildable_parent_iface->custom_tag_start (buildable, builder, child, 
269                                                 tagname, parser, data))
270     return TRUE;
271
272   if (strcmp (tagname, "items") == 0)
273     {
274       ItemParserData *parser_data;
275
276       parser_data = g_slice_new0 (ItemParserData);
277       parser_data->builder = g_object_ref (builder);
278       parser_data->object = g_object_ref (buildable);
279       parser_data->domain = gtk_builder_get_translation_domain (builder);
280       *parser = item_parser;
281       *data = parser_data;
282       return TRUE;
283     }
284   return FALSE;
285 }
286
287 static void
288 gtk_combo_box_text_buildable_custom_finished (GtkBuildable *buildable,
289                                               GtkBuilder   *builder,
290                                               GObject      *child,
291                                               const gchar  *tagname,
292                                               gpointer      user_data)
293 {
294   ItemParserData *data;
295
296   buildable_parent_iface->custom_finished (buildable, builder, child, 
297                                            tagname, user_data);
298
299   if (strcmp (tagname, "items") == 0)
300     {
301       data = (ItemParserData*)user_data;
302
303       g_object_unref (data->object);
304       g_object_unref (data->builder);
305       g_slice_free (ItemParserData, data);
306     }
307 }
308
309 /**
310  * gtk_combo_box_text_new:
311  *
312  * Creates a new #GtkComboBoxText, which is a #GtkComboBox just displaying
313  * strings. See gtk_combo_box_entry_new_with_text().
314  *
315  * Return value: A new #GtkComboBoxText
316  *
317  * Since: 2.24
318  */
319 GtkWidget *
320 gtk_combo_box_text_new (void)
321 {
322   return g_object_new (GTK_TYPE_COMBO_BOX_TEXT,
323                        "entry-text-column", 0,
324                        "id-column", 1,
325                        NULL);
326 }
327
328 /**
329  * gtk_combo_box_text_new_with_entry:
330  *
331  * Creates a new #GtkComboBoxText, which is a #GtkComboBox just displaying
332  * strings. The combo box created by this function has an entry.
333  *
334  * Return value: a new #GtkComboBoxText
335  *
336  * Since: 2.24
337  */
338 GtkWidget *
339 gtk_combo_box_text_new_with_entry (void)
340 {
341   return g_object_new (GTK_TYPE_COMBO_BOX_TEXT,
342                        "has-entry", TRUE,
343                        "entry-text-column", 0,
344                        "id-column", 1,
345                        NULL);
346 }
347
348 /**
349  * gtk_combo_box_text_append_text:
350  * @combo_box: A #GtkComboBoxText
351  * @text: A string
352  *
353  * Appends @text to the list of strings stored in @combo_box.
354  *
355  * This is the same as calling gtk_combo_box_text_insert_text() with a
356  * position of -1.
357  *
358  * Since: 2.24
359  */
360 void
361 gtk_combo_box_text_append_text (GtkComboBoxText *combo_box,
362                                 const gchar     *text)
363 {
364   gtk_combo_box_text_insert (combo_box, -1, NULL, text);
365 }
366
367 /**
368  * gtk_combo_box_text_prepend_text:
369  * @combo_box: A #GtkComboBox
370  * @text: A string
371  *
372  * Prepends @text to the list of strings stored in @combo_box.
373  *
374  * This is the same as calling gtk_combo_box_text_insert_text() with a
375  * position of 0.
376  *
377  * Since: 2.24
378  */
379 void
380 gtk_combo_box_text_prepend_text (GtkComboBoxText *combo_box,
381                                  const gchar     *text)
382 {
383   gtk_combo_box_text_insert (combo_box, 0, NULL, text);
384 }
385
386 /**
387  * gtk_combo_box_text_insert_text:
388  * @combo_box: A #GtkComboBoxText
389  * @position: An index to insert @text
390  * @text: A string
391  *
392  * Inserts @text at @position in the list of strings stored in @combo_box.
393  *
394  * If @position is negative then @text is appended.
395  *
396  * This is the same as calling gtk_combo_box_text_insert() with a %NULL
397  * ID string.
398  *
399  * Since: 2.24
400  */
401 void
402 gtk_combo_box_text_insert_text (GtkComboBoxText *combo_box,
403                                 gint             position,
404                                 const gchar     *text)
405 {
406   gtk_combo_box_text_insert (combo_box, position, NULL, text);
407 }
408
409 /**
410  * gtk_combo_box_text_append:
411  * @combo_box: A #GtkComboBoxText
412  * @text: A string
413  *
414  * Appends @text to the list of strings stored in @combo_box.  If @id is
415  * 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.  If @id
437  * 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 if none
567  * is selected. If @combo_box contains an entry, this function will return
568  * its contents (which will not necessarily be an item from the list).
569  *
570  * Returns: a newly allocated string containing the currently active text.
571  *     Must be freed with g_free().
572  *
573  * Since: 2.24
574  */
575 gchar *
576 gtk_combo_box_text_get_active_text (GtkComboBoxText *combo_box)
577 {
578   GtkTreeIter iter;
579   gchar *text = NULL;
580
581   g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box), NULL);
582
583   if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
584     {
585       GtkTreeModel *model;
586       gint text_column;
587       gint column_type;
588
589       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
590       g_return_val_if_fail (GTK_IS_LIST_STORE (model), NULL);
591       text_column = gtk_combo_box_get_entry_text_column (GTK_COMBO_BOX (combo_box));
592       g_return_val_if_fail (text_column >= 0, NULL);
593       column_type = gtk_tree_model_get_column_type (model, text_column);
594       g_return_val_if_fail (column_type == G_TYPE_STRING, NULL);
595       gtk_tree_model_get (model, &iter, text_column, &text, -1);
596     }
597
598   return text;
599 }