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