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