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