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