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