]> Pileus Git - ~andy/gtk/blob - gtk/gtkcellrenderercombo.c
6411ef2618188f01265adc7405dcbb825a1d853e
[~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 #include "gtkmarshalers.h"
32 #include "gtkprivate.h"
33 #include "gtkalias.h"
34
35 static void gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass);
36 static void gtk_cell_renderer_combo_init       (GtkCellRendererCombo      *self);
37 static void gtk_cell_renderer_combo_finalize     (GObject      *object);
38 static void gtk_cell_renderer_combo_get_property (GObject      *object,
39                                                   guint         prop_id,
40                                                   GValue       *value,
41                                                   GParamSpec   *pspec);
42
43 static void gtk_cell_renderer_combo_set_property (GObject      *object,
44                                                   guint         prop_id,
45                                                   const GValue *value,
46                                                   GParamSpec   *pspec);
47
48 static GtkCellEditable *gtk_cell_renderer_combo_start_editing (GtkCellRenderer     *cell,
49                                                                GdkEvent            *event,
50                                                                GtkWidget           *widget,
51                                                                const gchar         *path,
52                                                                GdkRectangle        *background_area,
53                                                                GdkRectangle        *cell_area,
54                                                                GtkCellRendererState flags);
55
56 enum {
57   PROP_0,
58   PROP_MODEL,
59   PROP_TEXT_COLUMN,
60   PROP_HAS_ENTRY
61 };
62
63 enum {
64   CHANGED,
65   LAST_SIGNAL
66 };
67
68 static guint cell_renderer_combo_signals[LAST_SIGNAL] = { 0, };
69
70 #define GTK_CELL_RENDERER_COMBO_PATH "gtk-cell-renderer-combo-path"
71
72 G_DEFINE_TYPE (GtkCellRendererCombo, gtk_cell_renderer_combo, GTK_TYPE_CELL_RENDERER_TEXT)
73
74 static void
75 gtk_cell_renderer_combo_class_init (GtkCellRendererComboClass *klass)
76 {
77   GObjectClass *object_class = G_OBJECT_CLASS (klass);
78   GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
79
80   object_class->finalize = gtk_cell_renderer_combo_finalize;
81   object_class->get_property = gtk_cell_renderer_combo_get_property;
82   object_class->set_property = gtk_cell_renderer_combo_set_property;
83
84   cell_class->start_editing = gtk_cell_renderer_combo_start_editing;
85
86   /**
87    * GtkCellRendererCombo:model:
88    *
89    * Holds a tree model containing the possible values for the combo box. 
90    * Use the text_column property to specify the column holding the values.
91    * 
92    * Since: 2.6
93    */
94   g_object_class_install_property (object_class,
95                                    PROP_MODEL,
96                                    g_param_spec_object ("model",
97                                                         P_("Model"),
98                                                         P_("The model containing the possible values for the combo box"),
99                                                         GTK_TYPE_TREE_MODEL,
100                                                         GTK_PARAM_READWRITE));
101
102   /**
103    * GtkCellRendererCombo:text-column:
104    *
105    * Specifies the model column which holds the possible values for the 
106    * combo box. 
107    *
108    * Note that this refers to the model specified in the model property, 
109    * <emphasis>not</emphasis> the model backing the tree view to which 
110    * this cell renderer is attached.
111    * 
112    * #GtkCellRendererCombo automatically adds a text cell renderer for 
113    * this column to its combo box.
114    *
115    * Since: 2.6
116    */
117   g_object_class_install_property (object_class,
118                                    PROP_TEXT_COLUMN,
119                                    g_param_spec_int ("text-column",
120                                                      P_("Text Column"),
121                                                      P_("A column in the data source model to get the strings from"),
122                                                      -1,
123                                                      G_MAXINT,
124                                                      -1,
125                                                      GTK_PARAM_READWRITE));
126
127   /** 
128    * GtkCellRendererCombo:has-entry:
129    *
130    * If %TRUE, the cell renderer will include an entry and allow to enter 
131    * values other than the ones in the popup list. 
132    *
133    * Since: 2.6
134    */
135   g_object_class_install_property (object_class,
136                                    PROP_HAS_ENTRY,
137                                    g_param_spec_boolean ("has-entry",
138                                                          P_("Has Entry"),
139                                                          P_("If FALSE, don't allow to enter strings other than the chosen ones"),
140                                                          TRUE,
141                                                          GTK_PARAM_READWRITE));
142
143
144   /**
145    * GtkCellRendererCombo::changed:
146    * @combo: the object on which the signal is emitted
147    * @path_string: a string of the path identifying the edited cell
148    *               (relative to the tree view model)
149    * @new_iter: the new iter selected in the combo box
150    *            (relative to the combo box model)
151    *
152    * This signal is emitted each time after the user selected an item in
153    * the combo box, either by using the mouse or the arrow keys.  Contrary
154    * to GtkComboBox, GtkCellRendererCombo::changed is not emitted for
155    * changes made to a selected item in the entry.  The argument @new_iter
156    * corresponds to the newly selected item in the combo box and it is relative
157    * to the GtkTreeModel set via the model property on GtkCellRendererCombo.
158    *
159    * Note that as soon as you change the model displayed in the tree view,
160    * the tree view will immediately cease the editing operating.  This
161    * means that you most probably want to refrain from changing the model
162    * until the combo cell renderer emits the edited or editing_canceled signal.
163    *
164    * Since: 2.14
165    */
166   cell_renderer_combo_signals[CHANGED] =
167     g_signal_new (I_("changed"),
168                   G_TYPE_FROM_CLASS (object_class),
169                   G_SIGNAL_RUN_LAST,
170                   0,
171                   NULL, NULL,
172                   _gtk_marshal_VOID__STRING_BOXED,
173                   G_TYPE_NONE, 2,
174                   G_TYPE_STRING,
175                   GTK_TYPE_TREE_ITER);
176 }
177
178 static void
179 gtk_cell_renderer_combo_init (GtkCellRendererCombo *self)
180 {
181   self->model = NULL;
182   self->text_column = -1;
183   self->has_entry = TRUE;
184   self->focus_out_id = 0;
185 }
186
187 /**
188  * gtk_cell_renderer_combo_new: 
189  * 
190  * Creates a new #GtkCellRendererCombo. 
191  * Adjust how text is drawn using object properties. 
192  * Object properties can be set globally (with g_object_set()). 
193  * Also, with #GtkTreeViewColumn, you can bind a property to a value 
194  * in a #GtkTreeModel. For example, you can bind the "text" property 
195  * on the cell renderer to a string value in the model, thus rendering 
196  * a different string in each row of the #GtkTreeView.
197  * 
198  * Returns: the new cell renderer
199  *
200  * Since: 2.6
201  */
202 GtkCellRenderer *
203 gtk_cell_renderer_combo_new (void)
204 {
205   return g_object_new (GTK_TYPE_CELL_RENDERER_COMBO, NULL); 
206 }
207
208 static void
209 gtk_cell_renderer_combo_finalize (GObject *object)
210 {
211   GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object);
212   
213   if (cell->model)
214     {
215       g_object_unref (cell->model);
216       cell->model = NULL;
217     }
218   
219   G_OBJECT_CLASS (gtk_cell_renderer_combo_parent_class)->finalize (object);
220 }
221
222 static void
223 gtk_cell_renderer_combo_get_property (GObject    *object,
224                                       guint       prop_id,
225                                       GValue     *value,
226                                       GParamSpec *pspec)
227 {
228   GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object);
229
230   switch (prop_id)
231     {
232     case PROP_MODEL:
233       g_value_set_object (value, cell->model);
234       break; 
235     case PROP_TEXT_COLUMN:
236       g_value_set_int (value, cell->text_column);
237       break;
238     case PROP_HAS_ENTRY:
239       g_value_set_boolean (value, cell->has_entry);
240       break;
241    default:
242       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243       break;
244     }
245 }
246
247 static void
248 gtk_cell_renderer_combo_set_property (GObject      *object,
249                                       guint         prop_id,
250                                       const GValue *value,
251                                       GParamSpec   *pspec)
252 {
253   GtkCellRendererCombo *cell = GTK_CELL_RENDERER_COMBO (object);
254
255   switch (prop_id)
256     {
257     case PROP_MODEL:
258       {
259         GObject *object;
260
261         object = g_value_get_object (value);
262         g_return_if_fail (GTK_IS_TREE_MODEL (object));
263         g_object_ref (object);
264
265         if (cell->model)
266           {
267             g_object_unref (cell->model);
268             cell->model = NULL;
269           }
270         cell->model = GTK_TREE_MODEL (object);
271         break;
272       }
273     case PROP_TEXT_COLUMN:
274       cell->text_column = g_value_get_int (value);
275       break;
276     case PROP_HAS_ENTRY:
277       cell->has_entry = g_value_get_boolean (value);
278       break;
279     default:
280       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
281       break;
282     }
283 }
284
285 static void
286 gtk_cell_renderer_combo_changed (GtkComboBox *combo,
287                                  gpointer     data)
288 {
289   GtkTreeIter iter;
290   GtkCellRendererCombo *cell;
291
292   cell = GTK_CELL_RENDERER_COMBO (data);
293
294   if (gtk_combo_box_get_active_iter (combo, &iter))
295     {
296       const char *path;
297
298       path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH);
299       g_signal_emit (cell, cell_renderer_combo_signals[CHANGED], 0,
300                      path, &iter);
301     }
302 }
303
304 static void
305 gtk_cell_renderer_combo_editing_done (GtkCellEditable *combo,
306                                       gpointer         data)
307 {
308   const gchar *path;
309   gchar *new_text = NULL;
310   GtkTreeModel *model;
311   GtkTreeIter iter;
312   GtkCellRendererCombo *cell;
313   GtkEntry *entry;
314   gboolean canceled;
315
316   cell = GTK_CELL_RENDERER_COMBO (data);
317
318   if (cell->focus_out_id > 0)
319     {
320       g_signal_handler_disconnect (combo, cell->focus_out_id);
321       cell->focus_out_id = 0;
322     }
323   
324   canceled = _gtk_combo_box_editing_canceled (GTK_COMBO_BOX (combo));
325   gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), canceled);
326   if (canceled)
327     return;
328
329   if (GTK_IS_COMBO_BOX_ENTRY (combo))
330     {
331       entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo)));
332       new_text = g_strdup (gtk_entry_get_text (entry));
333     }
334   else 
335     {
336       model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
337       if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
338         gtk_tree_model_get (model, &iter, cell->text_column, &new_text, -1);
339     }
340
341   path = g_object_get_data (G_OBJECT (combo), GTK_CELL_RENDERER_COMBO_PATH);
342   g_signal_emit_by_name (cell, "edited", path, new_text);
343
344   g_free (new_text);
345 }
346
347 static gboolean
348 gtk_cell_renderer_combo_focus_out_event (GtkWidget *widget,
349                                          GdkEvent  *event,
350                                          gpointer   data)
351 {
352   
353   gtk_cell_renderer_combo_editing_done (GTK_CELL_EDITABLE (widget), data);
354
355   return FALSE;
356 }
357
358 typedef struct 
359 {
360   GtkCellRendererCombo *cell;
361   gboolean found;
362   GtkTreeIter iter;
363 } SearchData;
364
365 static gboolean 
366 find_text (GtkTreeModel *model, 
367            GtkTreePath  *path, 
368            GtkTreeIter  *iter, 
369            gpointer      data)
370 {
371   SearchData *search_data = (SearchData *)data;
372   gchar *text;
373   
374   gtk_tree_model_get (model, iter, search_data->cell->text_column, &text, -1);
375   if (text && GTK_CELL_RENDERER_TEXT (search_data->cell)->text &&
376       strcmp (text, GTK_CELL_RENDERER_TEXT (search_data->cell)->text) == 0)
377     {
378       search_data->iter = *iter;
379       search_data->found = TRUE;
380     }
381
382   g_free (text);
383   
384   return search_data->found;
385 }
386
387 static GtkCellEditable *
388 gtk_cell_renderer_combo_start_editing (GtkCellRenderer     *cell,
389                                        GdkEvent            *event,
390                                        GtkWidget           *widget,
391                                        const gchar         *path,
392                                        GdkRectangle        *background_area,
393                                        GdkRectangle        *cell_area,
394                                        GtkCellRendererState flags)
395 {
396   GtkCellRendererCombo *cell_combo;
397   GtkCellRendererText *cell_text;
398   GtkWidget *combo;
399   SearchData search_data;
400
401   cell_text = GTK_CELL_RENDERER_TEXT (cell);
402   if (cell_text->editable == FALSE)
403     return NULL;
404
405   cell_combo = GTK_CELL_RENDERER_COMBO (cell);
406   if (cell_combo->model == NULL || cell_combo->text_column < 0)
407     return NULL;
408
409   if (cell_combo->has_entry) 
410     {
411       combo = gtk_combo_box_entry_new_with_model (cell_combo->model, cell_combo->text_column);
412
413       if (cell_text->text)
414         gtk_entry_set_text (GTK_ENTRY (GTK_BIN (combo)->child), 
415                             cell_text->text);
416     }
417   else
418     {
419       cell = gtk_cell_renderer_text_new ();
420       combo = gtk_combo_box_new_with_model (cell_combo->model);
421       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
422       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), 
423                                       cell, "text", cell_combo->text_column, 
424                                       NULL);
425
426       /* determine the current value */
427       search_data.cell = cell_combo;
428       search_data.found = FALSE;
429       gtk_tree_model_foreach (cell_combo->model, find_text, &search_data);
430       if (search_data.found)
431         gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo),
432                                        &(search_data.iter));
433     }
434
435   g_object_set (combo, "has-frame", FALSE, NULL);
436   g_object_set_data_full (G_OBJECT (combo),
437                           I_(GTK_CELL_RENDERER_COMBO_PATH),
438                           g_strdup (path), g_free);
439
440   gtk_widget_show (combo);
441
442   g_signal_connect (GTK_CELL_EDITABLE (combo), "editing_done",
443                     G_CALLBACK (gtk_cell_renderer_combo_editing_done),
444                     cell_combo);
445   g_signal_connect (GTK_CELL_EDITABLE (combo), "changed",
446                     G_CALLBACK (gtk_cell_renderer_combo_changed),
447                     cell_combo);
448   cell_combo->focus_out_id = 
449     g_signal_connect (combo, "focus_out_event",
450                       G_CALLBACK (gtk_cell_renderer_combo_focus_out_event),
451                       cell_combo);
452
453   return GTK_CELL_EDITABLE (combo);
454 }
455
456 #define __GTK_CELL_RENDERER_COMBO_C__
457 #include "gtkaliasdef.c"