Updated Hungarian translation
[evolution.git] / e-util / e-action-combo-box.c
blob33d678ab1e26b688bcead04cd98f8f517dbf1b88
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* e-action-combo-box.c
4 * Copyright (C) 2008 Novell, Inc.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include "e-action-combo-box.h"
24 #include "e-misc-utils.h"
26 #include <glib/gi18n.h>
28 #define E_ACTION_COMBO_BOX_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE \
30 ((obj), E_TYPE_ACTION_COMBO_BOX, EActionComboBoxPrivate))
32 enum {
33 COLUMN_ACTION,
34 COLUMN_SORT
37 enum {
38 PROP_0,
39 PROP_ACTION
42 struct _EActionComboBoxPrivate {
43 GtkRadioAction *action;
44 GtkActionGroup *action_group;
45 GHashTable *index;
46 guint changed_handler_id; /* action::changed */
47 guint group_sensitive_handler_id; /* action-group::sensitive */
48 guint group_visible_handler_id; /* action-group::visible */
49 gboolean group_has_icons : 1;
52 G_DEFINE_TYPE (
53 EActionComboBox,
54 e_action_combo_box,
55 GTK_TYPE_COMBO_BOX)
57 static void
58 action_combo_box_action_changed_cb (GtkRadioAction *action,
59 GtkRadioAction *current,
60 EActionComboBox *combo_box)
62 GtkTreeRowReference *reference;
63 GtkTreeModel *model;
64 GtkTreePath *path;
65 GtkTreeIter iter;
66 gboolean valid;
68 reference = g_hash_table_lookup (
69 combo_box->priv->index, GINT_TO_POINTER (
70 gtk_radio_action_get_current_value (current)));
71 g_return_if_fail (reference != NULL);
73 model = gtk_tree_row_reference_get_model (reference);
74 path = gtk_tree_row_reference_get_path (reference);
75 valid = gtk_tree_model_get_iter (model, &iter, path);
76 gtk_tree_path_free (path);
77 g_return_if_fail (valid);
79 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
82 static void
83 action_combo_box_action_group_notify_cb (GtkActionGroup *action_group,
84 GParamSpec *pspec,
85 EActionComboBox *combo_box)
87 g_object_set (
88 combo_box, "sensitive",
89 gtk_action_group_get_sensitive (action_group), "visible",
90 gtk_action_group_get_visible (action_group), NULL);
93 static void
94 action_combo_box_render_pixbuf (GtkCellLayout *layout,
95 GtkCellRenderer *renderer,
96 GtkTreeModel *model,
97 GtkTreeIter *iter,
98 EActionComboBox *combo_box)
100 GtkRadioAction *action;
101 gchar *icon_name;
102 gchar *stock_id;
103 gboolean sensitive;
104 gboolean visible;
105 gint width;
107 gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
109 /* A NULL action means the row is a separator. */
110 if (action == NULL)
111 return;
113 g_object_get (
114 G_OBJECT (action),
115 "icon-name", &icon_name,
116 "sensitive", &sensitive,
117 "stock-id", &stock_id,
118 "visible", &visible,
119 NULL);
121 /* If some action has an icon */
122 if (combo_box->priv->group_has_icons)
123 /* Keep the pixbuf renderer a fixed size for proper alignment. */
124 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
125 else
126 width = 0;
128 /* We can't set both "icon-name" and "stock-id" because setting
129 * one unsets the other. So pick the one that has a non-NULL
130 * value. If both are non-NULL, "stock-id" wins. */
132 if (stock_id != NULL)
133 g_object_set (
134 G_OBJECT (renderer),
135 "sensitive", sensitive,
136 "icon-name", NULL,
137 "stock-id", stock_id,
138 "stock-size", GTK_ICON_SIZE_MENU,
139 "visible", visible,
140 "width", width,
141 NULL);
142 else
143 g_object_set (
144 G_OBJECT (renderer),
145 "sensitive", sensitive,
146 "icon-name", icon_name,
147 "stock-id", NULL,
148 "stock-size", GTK_ICON_SIZE_MENU,
149 "visible", visible,
150 "width", width,
151 NULL);
153 g_object_unref (action);
154 g_free (icon_name);
155 g_free (stock_id);
158 static void
159 action_combo_box_render_text (GtkCellLayout *layout,
160 GtkCellRenderer *renderer,
161 GtkTreeModel *model,
162 GtkTreeIter *iter,
163 EActionComboBox *combo_box)
165 GtkRadioAction *action;
166 gchar **strv;
167 gchar *label;
168 gboolean sensitive;
169 gboolean visible;
170 gint xpad;
172 gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
174 /* A NULL action means the row is a separator. */
175 if (action == NULL)
176 return;
178 g_object_get (
179 G_OBJECT (action),
180 "label", &label,
181 "sensitive", &sensitive,
182 "visible", &visible,
183 NULL);
185 /* Strip out underscores. */
186 strv = g_strsplit (label, "_", -1);
187 g_free (label);
188 label = g_strjoinv (NULL, strv);
189 g_strfreev (strv);
191 xpad = combo_box->priv->group_has_icons ? 3 : 0;
193 g_object_set (
194 G_OBJECT (renderer),
195 "sensitive", sensitive,
196 "text", label,
197 "visible", visible,
198 "xpad", xpad,
199 NULL);
201 g_object_unref (action);
202 g_free (label);
205 static gboolean
206 action_combo_box_is_row_separator (GtkTreeModel *model,
207 GtkTreeIter *iter)
209 GtkAction *action;
210 gboolean separator;
212 /* NULL actions are rendered as separators. */
213 gtk_tree_model_get (model, iter, COLUMN_ACTION, &action, -1);
214 separator = (action == NULL);
215 if (action != NULL)
216 g_object_unref (action);
218 return separator;
221 static void
222 action_combo_box_update_model (EActionComboBox *combo_box)
224 GtkListStore *list_store;
225 GSList *list;
227 g_hash_table_remove_all (combo_box->priv->index);
229 if (combo_box->priv->action == NULL) {
230 gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), NULL);
231 return;
234 /* We store values in the sort column as floats so that we can
235 * insert separators in between consecutive integer values and
236 * still maintain the proper ordering. */
237 list_store = gtk_list_store_new (
238 2, GTK_TYPE_RADIO_ACTION, G_TYPE_FLOAT);
240 list = gtk_radio_action_get_group (combo_box->priv->action);
241 combo_box->priv->group_has_icons = FALSE;
243 while (list != NULL) {
244 GtkTreeRowReference *reference;
245 GtkRadioAction *action = list->data;
246 GtkTreePath *path;
247 GtkTreeIter iter;
248 gchar *icon_name = NULL;
249 gchar *stock_id = NULL;
250 gboolean visible = FALSE;
251 gint value;
253 g_object_get (action,
254 "icon-name", &icon_name,
255 "stock-id", &stock_id,
256 "visible", &visible,
257 NULL);
259 if (!visible) {
260 g_free (icon_name);
261 g_free (stock_id);
263 list = g_slist_next (list);
264 continue;
267 combo_box->priv->group_has_icons |=
268 (icon_name != NULL || stock_id != NULL);
269 g_free (icon_name);
270 g_free (stock_id);
272 gtk_list_store_append (list_store, &iter);
273 g_object_get (action, "value", &value, NULL);
274 gtk_list_store_set (
275 list_store, &iter, COLUMN_ACTION,
276 list->data, COLUMN_SORT, (gfloat) value, -1);
278 path = gtk_tree_model_get_path (
279 GTK_TREE_MODEL (list_store), &iter);
280 reference = gtk_tree_row_reference_new (
281 GTK_TREE_MODEL (list_store), path);
282 g_hash_table_insert (
283 combo_box->priv->index,
284 GINT_TO_POINTER (value), reference);
285 gtk_tree_path_free (path);
287 list = g_slist_next (list);
290 gtk_tree_sortable_set_sort_column_id (
291 GTK_TREE_SORTABLE (list_store),
292 COLUMN_SORT, GTK_SORT_ASCENDING);
293 gtk_combo_box_set_model (
294 GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (list_store));
295 g_object_unref (list_store);
297 action_combo_box_action_changed_cb (
298 combo_box->priv->action,
299 combo_box->priv->action,
300 combo_box);
303 static void
304 action_combo_box_set_property (GObject *object,
305 guint property_id,
306 const GValue *value,
307 GParamSpec *pspec)
309 switch (property_id) {
310 case PROP_ACTION:
311 e_action_combo_box_set_action (
312 E_ACTION_COMBO_BOX (object),
313 g_value_get_object (value));
314 return;
317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
320 static void
321 action_combo_box_get_property (GObject *object,
322 guint property_id,
323 GValue *value,
324 GParamSpec *pspec)
326 switch (property_id) {
327 case PROP_ACTION:
328 g_value_set_object (
329 value, e_action_combo_box_get_action (
330 E_ACTION_COMBO_BOX (object)));
331 return;
334 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
337 static void
338 action_combo_box_dispose (GObject *object)
340 EActionComboBoxPrivate *priv = E_ACTION_COMBO_BOX_GET_PRIVATE (object);
342 if (priv->action != NULL) {
343 g_object_unref (priv->action);
344 priv->action = NULL;
347 if (priv->action_group != NULL) {
348 g_object_unref (priv->action_group);
349 priv->action_group = NULL;
352 g_hash_table_remove_all (priv->index);
354 /* Chain up to parent's dispose() method. */
355 G_OBJECT_CLASS (e_action_combo_box_parent_class)->dispose (object);
358 static void
359 action_combo_box_finalize (GObject *object)
361 EActionComboBoxPrivate *priv = E_ACTION_COMBO_BOX_GET_PRIVATE (object);
363 g_hash_table_destroy (priv->index);
365 /* Chain up to parent's finalize() method. */
366 G_OBJECT_CLASS (e_action_combo_box_parent_class)->finalize (object);
369 static void
370 action_combo_box_constructed (GObject *object)
372 GtkComboBox *combo_box;
373 GtkCellRenderer *renderer;
375 combo_box = GTK_COMBO_BOX (object);
377 /* Chain up to parent's constructed() method. */
378 G_OBJECT_CLASS (e_action_combo_box_parent_class)->constructed (object);
380 /* This needs to happen after constructor properties are set
381 * so that GtkCellLayout.get_area() returns something valid. */
383 renderer = gtk_cell_renderer_pixbuf_new ();
384 gtk_cell_layout_pack_start (
385 GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
386 gtk_cell_layout_set_cell_data_func (
387 GTK_CELL_LAYOUT (combo_box), renderer,
388 (GtkCellLayoutDataFunc) action_combo_box_render_pixbuf,
389 combo_box, NULL);
391 renderer = gtk_cell_renderer_text_new ();
392 gtk_cell_layout_pack_start (
393 GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
394 gtk_cell_layout_set_cell_data_func (
395 GTK_CELL_LAYOUT (combo_box), renderer,
396 (GtkCellLayoutDataFunc) action_combo_box_render_text,
397 combo_box, NULL);
399 gtk_combo_box_set_row_separator_func (
400 combo_box, (GtkTreeViewRowSeparatorFunc)
401 action_combo_box_is_row_separator, NULL, NULL);
404 static void
405 action_combo_box_changed (GtkComboBox *combo_box)
407 GtkRadioAction *action;
408 GtkTreeModel *model;
409 GtkTreeIter iter;
410 gint value;
412 /* This method is virtual, so no need to chain up. */
414 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
415 return;
417 model = gtk_combo_box_get_model (combo_box);
418 gtk_tree_model_get (model, &iter, COLUMN_ACTION, &action, -1);
419 g_object_get (action, "value", &value, NULL);
420 gtk_radio_action_set_current_value (action, value);
421 g_object_unref (action);
424 static void
425 e_action_combo_box_class_init (EActionComboBoxClass *class)
427 GObjectClass *object_class;
428 GtkComboBoxClass *combo_box_class;
430 g_type_class_add_private (class, sizeof (EActionComboBoxPrivate));
432 object_class = G_OBJECT_CLASS (class);
433 object_class->set_property = action_combo_box_set_property;
434 object_class->get_property = action_combo_box_get_property;
435 object_class->dispose = action_combo_box_dispose;
436 object_class->finalize = action_combo_box_finalize;
437 object_class->constructed = action_combo_box_constructed;
439 combo_box_class = GTK_COMBO_BOX_CLASS (class);
440 combo_box_class->changed = action_combo_box_changed;
442 g_object_class_install_property (
443 object_class,
444 PROP_ACTION,
445 g_param_spec_object (
446 "action",
447 "Action",
448 "A GtkRadioAction",
449 GTK_TYPE_RADIO_ACTION,
450 G_PARAM_READWRITE));
453 static void
454 e_action_combo_box_init (EActionComboBox *combo_box)
456 combo_box->priv = E_ACTION_COMBO_BOX_GET_PRIVATE (combo_box);
458 combo_box->priv->index = g_hash_table_new_full (
459 g_direct_hash, g_direct_equal,
460 (GDestroyNotify) NULL,
461 (GDestroyNotify) gtk_tree_row_reference_free);
464 GtkWidget *
465 e_action_combo_box_new (void)
467 return e_action_combo_box_new_with_action (NULL);
470 GtkWidget *
471 e_action_combo_box_new_with_action (GtkRadioAction *action)
473 return g_object_new (E_TYPE_ACTION_COMBO_BOX, "action", action, NULL);
476 GtkRadioAction *
477 e_action_combo_box_get_action (EActionComboBox *combo_box)
479 g_return_val_if_fail (E_IS_ACTION_COMBO_BOX (combo_box), NULL);
481 return combo_box->priv->action;
484 void
485 e_action_combo_box_set_action (EActionComboBox *combo_box,
486 GtkRadioAction *action)
488 g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
490 if (action != NULL)
491 g_return_if_fail (GTK_IS_RADIO_ACTION (action));
493 if (combo_box->priv->action != NULL) {
494 g_signal_handler_disconnect (
495 combo_box->priv->action,
496 combo_box->priv->changed_handler_id);
497 g_object_unref (combo_box->priv->action);
500 if (combo_box->priv->action_group != NULL) {
501 g_signal_handler_disconnect (
502 combo_box->priv->action_group,
503 combo_box->priv->group_sensitive_handler_id);
504 g_signal_handler_disconnect (
505 combo_box->priv->action_group,
506 combo_box->priv->group_visible_handler_id);
507 g_object_unref (combo_box->priv->action_group);
508 combo_box->priv->action_group = NULL;
511 if (action != NULL) {
512 /* This also adds a reference to the combo_box->priv->action_group */
513 g_object_get (
514 g_object_ref (action), "action-group",
515 &combo_box->priv->action_group, NULL);
518 combo_box->priv->action = action;
519 action_combo_box_update_model (combo_box);
521 if (combo_box->priv->action != NULL)
522 combo_box->priv->changed_handler_id = g_signal_connect (
523 combo_box->priv->action, "changed",
524 G_CALLBACK (action_combo_box_action_changed_cb),
525 combo_box);
527 if (combo_box->priv->action_group != NULL) {
528 combo_box->priv->group_sensitive_handler_id =
529 e_signal_connect_notify (
530 combo_box->priv->action_group,
531 "notify::sensitive", G_CALLBACK (
532 action_combo_box_action_group_notify_cb),
533 combo_box);
534 combo_box->priv->group_visible_handler_id =
535 e_signal_connect_notify (
536 combo_box->priv->action_group,
537 "notify::visible", G_CALLBACK (
538 action_combo_box_action_group_notify_cb),
539 combo_box);
542 g_object_notify (G_OBJECT (combo_box), "action");
545 gint
546 e_action_combo_box_get_current_value (EActionComboBox *combo_box)
548 g_return_val_if_fail (E_IS_ACTION_COMBO_BOX (combo_box), 0);
549 g_return_val_if_fail (combo_box->priv->action != NULL, 0);
551 return gtk_radio_action_get_current_value (combo_box->priv->action);
554 void
555 e_action_combo_box_set_current_value (EActionComboBox *combo_box,
556 gint current_value)
558 g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
559 g_return_if_fail (combo_box->priv->action != NULL);
561 gtk_radio_action_set_current_value (
562 combo_box->priv->action, current_value);
565 void
566 e_action_combo_box_add_separator_before (EActionComboBox *combo_box,
567 gint action_value)
569 GtkTreeModel *model;
570 GtkTreeIter iter;
572 g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
574 /* NULL actions are rendered as separators. */
575 model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
576 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
577 gtk_list_store_set (
578 GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
579 NULL, COLUMN_SORT, (gfloat) action_value - 0.5, -1);
582 void
583 e_action_combo_box_add_separator_after (EActionComboBox *combo_box,
584 gint action_value)
586 GtkTreeModel *model;
587 GtkTreeIter iter;
589 g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
591 /* NULL actions are rendered as separators. */
592 model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
593 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
594 gtk_list_store_set (
595 GTK_LIST_STORE (model), &iter, COLUMN_ACTION,
596 NULL, COLUMN_SORT, (gfloat) action_value + 0.5, -1);
599 void
600 e_action_combo_box_update_model (EActionComboBox *combo_box)
602 g_return_if_fail (E_IS_ACTION_COMBO_BOX (combo_box));
604 action_combo_box_update_model (combo_box);