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