]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrenderercombo.c
e03a4873cc92c666687daf4ff2b25cc4878531b6
[~andy/gtk] / gtk / gtkcellrenderercombo.c
1 /* GtkCellRendererCombo
2  * Copyright (C) 2004 Lorenzo Gil Sanchez
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <config.h>
21 #include <string.h>
22
23 #include "gtkintl.h"
24 #include "gtkbin.h"
25 #include "gtkentry.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrenderercombo.h"
28 #include "gtkcellrenderertext.h"
29 #include "gtkcombobox.h"
30 #include "gtkcomboboxentry.h"
31
32 static void gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass);
33 static void gtk_cell_renderer_combo_init       (GtkCellRendererCombo      *self);
34 static void gtk_cell_renderer_combo_get_property (GObject      *object,
35                                                   guint         prop_id,
36                                                   GValue       *value,
37                                                   GParamSpec   *pspec);
38
39 static void gtk_cell_renderer_combo_set_property (GObject      *object,
40                                                   guint         prop_id,
41                                                   const GValue *value,
42                                                   GParamSpec   *pspec);
43
44 static GtkCellEditable *gtk_cell_renderer_combo_start_editing (GtkCellRenderer     *cell,
45                                                                GdkEvent            *event,
46                                                                GtkWidget           *widget,
47                                                                const gchar         *path,
48                                                                GdkRectangle        *background_area,
49                                                                GdkRectangle        *cell_area,
50                                                                GtkCellRendererState flags);
51
52 enum {
53   PROP_0,
54   PROP_MODEL,
55   PROP_TEXT_COLUMN,
56   PROP_HAS_ENTRY
57 };
58
59 static GObjectClass *parent_class = NULL;
60
61 #define GTK_CELL_RENDERER_COMBO_PATH "gtk-cell-renderer-combo-path"
62
63 GType
64 gtk_cell_renderer_combo_get_type (void)
65 {
66   static GType gtk_cell_renderer_combo_type = 0;
67
68   if (!gtk_cell_renderer_combo_type)
69     {
70       static const GTypeInfo gtk_cell_renderer_combo_info = 
71         {
72           sizeof (GtkCellRendererComboClass),
73           (GBaseInitFunc)     NULL,
74           (GBaseFinalizeFunc) NULL,
75           (GClassInitFunc)    gtk_cell_renderer_combo_class_init,
76           NULL,
77           NULL,
78           sizeof (GtkCellRendererCombo),
79           0,
80           (GInstanceInitFunc) gtk_cell_renderer_combo_init
81         };
82       gtk_cell_renderer_combo_type =
83         g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT,
84                                 "GtkCellRendererCombo",
85                                 &gtk_cell_renderer_combo_info,
86                                 0);
87     }
88   return gtk_cell_renderer_combo_type;
89 }
90
91 static void
92 gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass)
93 {
94   GObjectClass *object_class = G_OBJECT_CLASS (klass);
95   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
96
97   parent_class = g_type_class_peek_parent (klass);
98
99   object_class->get_property = gtk_cell_renderer_combo_get_property;
100   object_class->set_property = gtk_cell_renderer_combo_set_property;
101
102   cell_class->start_editing = gtk_cell_renderer_combo_start_editing;
103
104   /**
105    * GtkCellRendererCombo:model:
106    *
107    * The :model property holds a tree model containing the possible
108    * values for the combo box. Use the :text_column property to specify
109    * the column holding the values.
110    * 
111    * Since: 2.6
112    */
113   g_object_class_install_property (object_class,
114                                    PROP_MODEL,
115                                    g_param_spec_object ("model",
116                                                         P_("Model"),
117                                                         P_("The model containing the possible values for the combo box"),
118                                                         GTK_TYPE_TREE_MODEL,
119                                                         G_PARAM_READWRITE));
120
121   /**
122    * GtkCellRendererCombo:text_column:
123    *
124    * The :text_column property specifies the model column which
125    * holds the possible values for the combo box. Note that this
126    * refers to the model specified in the :model property, 
127    * <emphasis>not</emphasis> the model backing the tree view to 
128    * which this cell renderer is attached.
129    * 
130    * Since: 2.6
131    */
132   g_object_class_install_property (object_class,
133                                    PROP_TEXT_COLUMN,
134                                    g_param_spec_int ("text_column",
135                                                      P_("Text Column"),
136                                                      P_("A column in the data source model to get the strings from"),
137                                                      -1,
138                                                      G_MAXINT,
139                                                      -1,
140                                                      G_PARAM_READWRITE));
141
142   /** 
143    * GtkCellRendererCombo:has_entry:
144    *
145    * If the :has_entry property is %TRUe, the cell renderer will 
146    * include an entry and allow to enter values other than the ones 
147    * in the popup list. 
148    *
149    * Since: 2.6
150    */
151   g_object_class_install_property (object_class,
152                                    PROP_HAS_ENTRY,
153                                    g_param_spec_boolean ("has_entry",
154                                                          P_("Has Entry"),
155                                                          P_("If %FALSE, don't allow to enter strings other than the chosen ones"),
156                                                          TRUE,
157                                                          G_PARAM_READWRITE));
158
159 }
160
161 static void
162 gtk_cell_renderer_combo_init (GtkCellRendererCombo *self)
163 {
164   self->model = NULL;
165   self->text_column = -1;
166   self->focus_out_id = 0;
167 }
168
169 /**
170  * gtk_cell_renderer_combo_new: 
171  * 
172  * Creates a new #GtkCellRendererCombo 
173  * Adjust how text is drawn using object properties. 
174  * Object properties can be set globally (with g_object_set()). 
175  * Also, with #GtkTreeViewColumn, you can bind a property to a value 
176  * in a #GtkTreeModel. For example, you can bind the "text" property 
177  * on the cell renderer to a string value in the model, thus rendering 
178  * a different string in each row of the #GtkTreeView.
179  * 
180  * Returns: the new cell renderer
181  *
182  * Since: 2.6
183  */
184 GtkCellRenderer *
185 gtk_cell_renderer_combo_new (void)
186 {
187   return g_object_new (GTK_TYPE_CELL_RENDERER_COMBO, NULL); 
188 }
189
190 static void
191 gtk_cell_renderer_combo_get_property (GObject    *object,
192                                       guint       prop_id,
193                                       GValue     *value,
194                                       GParamSpec *pspec)
195 {
196   GtkCellRendererCombo *cell;
197  
198   g_return_if_fail (GTK_IS_CELL_RENDERER_COMBO (object));
199
200   cell = GTK_CELL_RENDERER_COMBO (object);
201
202   switch (prop_id)
203     {
204     case PROP_MODEL:
205       g_value_set_object (value, cell->model);
206       break; 
207     case PROP_TEXT_COLUMN:
208       g_value_set_int (value, cell->text_column);
209       break;
210     case PROP_HAS_ENTRY:
211       g_value_set_boolean (value, cell->has_entry);
212       break;
213    default:
214       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215     }
216 }
217
218 static void
219 gtk_cell_renderer_combo_set_property (GObject      *object,
220                                       guint         prop_id,
221                                       const GValue *value,
222                                       GParamSpec   *pspec)
223 {
224   GtkCellRendererCombo *cell;
225   
226   g_return_if_fail (GTK_IS_CELL_RENDERER_COMBO (object));
227
228   cell = GTK_CELL_RENDERER_COMBO (object);
229
230   switch (prop_id)
231     {
232     case PROP_MODEL:
233       cell->model = g_value_get_object (value);
234       break;
235     case PROP_TEXT_COLUMN:
236       cell->text_column = g_value_get_int (value);
237       break;
238     case PROP_HAS_ENTRY:
239       cell->has_entry = g_value_get_boolean (value);
240       break;
241     default:
242       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243     }
244 }
245
246 static void
247 gtk_cell_renderer_combo_editing_done (GtkCellEditable *combo,
248                                       gpointer         data)
249 {
250   const gchar *path;
251   gchar *new_text = NULL;
252   GtkTreeModel *model;
253   GtkTreeIter iter;
254   GtkCellRendererCombo *cell;
255   GtkEntry *entry;
256
257   cell = GTK_CELL_RENDERER_COMBO (data);
258
259   if (cell->focus_out_id > 0)
260     {
261       g_signal_handler_disconnect (combo, cell->focus_out_id);
262       cell->focus_out_id = 0;
263     }
264
265   if (_gtk_combo_box_editing_canceled (GTK_COMBO_BOX (combo)))
266     {
267       gtk_cell_renderer_editing_canceled (GTK_CELL_RENDERER (data));
268       return;
269     }
270
271   if (GTK_IS_COMBO_BOX_ENTRY (combo))
272     {
273       entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo)));
274       new_text = g_strdup (gtk_entry_get_text (entry));
275     }
276   else 
277     {
278       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
279       if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
280         gtk_tree_model_get (model, &iter, cell->text_column, &new_text, -1);
281     }
282
283   path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH);
284   g_signal_emit_by_name (cell, "edited", path, new_text);
285
286   g_free (new_text);
287 }
288
289 static gboolean
290 gtk_cell_renderer_combo_focus_out_event (GtkWidget *widget,
291                                          GdkEvent  *event,
292                                          gpointer   data)
293 {
294   
295   gtk_cell_renderer_combo_editing_done (GTK_CELL_EDITABLE (widget), data);
296
297   return FALSE;
298 }
299
300 typedef struct 
301 {
302   GtkCellRendererCombo *cell;
303   gboolean found;
304   GtkTreeIter iter;
305 } SearchData;
306
307 static gboolean 
308 find_text (GtkTreeModel *model, 
309            GtkTreePath  *path, 
310            GtkTreeIter  *iter, 
311            gpointer      data)
312 {
313   SearchData *search_data = (SearchData *)data;
314   gchar *text;
315   
316   gtk_tree_model_get (model, iter, search_data->cell->text_column, &text, -1);
317   if (text && GTK_CELL_RENDERER_TEXT (search_data->cell)->text &&
318       strcmp (text, GTK_CELL_RENDERER_TEXT (search_data->cell)->text) == 0)
319     {
320       search_data->iter = *iter;
321       search_data->found = TRUE;
322     }
323   
324   return search_data->found;
325 }
326
327 static GtkCellEditable *
328 gtk_cell_renderer_combo_start_editing (GtkCellRenderer     *cell,
329                                        GdkEvent            *event,
330                                        GtkWidget           *widget,
331                                        const gchar         *path,
332                                        GdkRectangle        *background_area,
333                                        GdkRectangle        *cell_area,
334                                        GtkCellRendererState flags)
335 {
336   GtkCellRendererCombo *cell_combo;
337   GtkCellRendererText *cell_text;
338   GtkWidget *combo;
339   SearchData search_data;
340
341   cell_text = GTK_CELL_RENDERER_TEXT (cell);
342   if (cell_text->editable == FALSE)
343     return NULL;
344
345   cell_combo = GTK_CELL_RENDERER_COMBO (cell);
346   if (cell_combo->model == NULL || cell_combo->text_column < 0)
347     return NULL;
348
349   if (cell_combo->has_entry) 
350     {
351       combo = gtk_combo_box_entry_new_with_model (cell_combo->model, cell_combo->text_column);
352
353       if (cell_text->text)
354         gtk_entry_set_text (GTK_ENTRY (GTK_BIN (combo)->child), 
355                             cell_text->text);
356     }
357   else
358     {
359       cell = gtk_cell_renderer_text_new ();
360       combo = gtk_combo_box_new_with_model (cell_combo->model);
361       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
362       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), 
363                                       cell, "text", cell_combo->text_column, 
364                                       NULL);
365
366       /* determine the current value */
367       search_data.cell = cell_combo;
368       search_data.found = FALSE;
369       gtk_tree_model_foreach (cell_combo->model, find_text, &search_data);
370       if (search_data.found)
371         gtk_combo_box_set_active_iter (combo, &(search_data.iter));
372     }
373
374   g_object_set (combo, "has_frame", FALSE, NULL);
375   g_object_set_data_full (G_OBJECT (combo),
376                           GTK_CELL_RENDERER_COMBO_PATH,
377                           g_strdup (path), g_free);
378
379   gtk_widget_show (combo);
380
381   g_signal_connect (GTK_CELL_EDITABLE (combo), "editing_done",
382                     G_CALLBACK (gtk_cell_renderer_combo_editing_done),
383                     cell_combo);
384   cell_combo->focus_out_id = 
385     g_signal_connect (combo, "focus_out_event",
386                       G_CALLBACK (gtk_cell_renderer_combo_focus_out_event),
387                       cell_combo);
388
389   return GTK_CELL_EDITABLE (combo);
390 }