evolution-3.6.4/widgets/misc/e-popup-action.c

No issues found

  1 /*
  2  * e-popup-action.c
  3  *
  4  * This program 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) version 3.
  8  *
  9  * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 19  *
 20  */
 21 
 22 #ifdef HAVE_CONFIG_H
 23 #include <config.h>
 24 #endif
 25 
 26 #include "e-popup-action.h"
 27 
 28 #include <glib/gi18n.h>
 29 
 30 #define E_POPUP_ACTION_GET_PRIVATE(obj) \
 31 	(G_TYPE_INSTANCE_GET_PRIVATE \
 32 	((obj), E_TYPE_POPUP_ACTION, EPopupActionPrivate))
 33 
 34 enum {
 35 	PROP_0,
 36 	PROP_RELATED_ACTION,
 37 	PROP_USE_ACTION_APPEARANCE
 38 };
 39 
 40 struct _EPopupActionPrivate {
 41 	GtkAction *related_action;
 42 	gboolean use_action_appearance;
 43 	gulong activate_handler_id;
 44 	gulong notify_handler_id;
 45 };
 46 
 47 /* Forward Declarations */
 48 static void e_popup_action_activatable_init (GtkActivatableIface *interface);
 49 
 50 G_DEFINE_TYPE_WITH_CODE (
 51 	EPopupAction,
 52 	e_popup_action,
 53 	GTK_TYPE_ACTION,
 54 	G_IMPLEMENT_INTERFACE (
 55 		GTK_TYPE_ACTIVATABLE,
 56 		e_popup_action_activatable_init))
 57 
 58 static void
 59 popup_action_notify_cb (GtkAction *action,
 60                         GParamSpec *pspec,
 61                         GtkActivatable *activatable)
 62 {
 63 	GtkActivatableIface *iface;
 64 
 65 	iface = GTK_ACTIVATABLE_GET_IFACE (activatable);
 66 	g_return_if_fail (iface->update != NULL);
 67 
 68 	iface->update (activatable, action, pspec->name);
 69 }
 70 
 71 static GtkAction *
 72 popup_action_get_related_action (EPopupAction *popup_action)
 73 {
 74 	return popup_action->priv->related_action;
 75 }
 76 
 77 static void
 78 popup_action_set_related_action (EPopupAction *popup_action,
 79                                  GtkAction *related_action)
 80 {
 81 	GtkActivatable *activatable;
 82 
 83 	/* Do not call gtk_activatable_do_set_related_action() because
 84 	 * it assumes the activatable object is a widget and tries to add
 85 	 * it to the related actions's proxy list.  Instead we'll just do
 86 	 * the relevant steps manually. */
 87 
 88 	activatable = GTK_ACTIVATABLE (popup_action);
 89 
 90 	if (related_action == popup_action->priv->related_action)
 91 		return;
 92 
 93 	if (related_action != NULL)
 94 		g_object_ref (related_action);
 95 
 96 	if (popup_action->priv->related_action != NULL) {
 97 		g_signal_handler_disconnect (
 98 			popup_action,
 99 			popup_action->priv->activate_handler_id);
100 		g_signal_handler_disconnect (
101 			popup_action->priv->related_action,
102 			popup_action->priv->notify_handler_id);
103 		popup_action->priv->activate_handler_id = 0;
104 		popup_action->priv->notify_handler_id = 0;
105 		g_object_unref (popup_action->priv->related_action);
106 	}
107 
108 	popup_action->priv->related_action = related_action;
109 
110 	if (related_action != NULL) {
111 		popup_action->priv->activate_handler_id =
112 			g_signal_connect_swapped (
113 				popup_action, "activate",
114 				G_CALLBACK (gtk_action_activate),
115 				related_action);
116 		popup_action->priv->notify_handler_id =
117 			g_signal_connect (
118 				related_action, "notify",
119 				G_CALLBACK (popup_action_notify_cb),
120 				popup_action);
121 		gtk_activatable_sync_action_properties (
122 			activatable, related_action);
123 	} else
124 		gtk_action_set_visible (GTK_ACTION (popup_action), FALSE);
125 
126 	g_object_notify (G_OBJECT (popup_action), "related-action");
127 }
128 
129 static gboolean
130 popup_action_get_use_action_appearance (EPopupAction *popup_action)
131 {
132 	return popup_action->priv->use_action_appearance;
133 }
134 
135 static void
136 popup_action_set_use_action_appearance (EPopupAction *popup_action,
137                                         gboolean use_action_appearance)
138 {
139 	GtkActivatable *activatable;
140 	GtkAction *related_action;
141 
142 	if (popup_action->priv->use_action_appearance == use_action_appearance)
143 		return;
144 
145 	popup_action->priv->use_action_appearance = use_action_appearance;
146 
147 	g_object_notify (G_OBJECT (popup_action), "use-action-appearance");
148 
149 	activatable = GTK_ACTIVATABLE (popup_action);
150 	related_action = popup_action_get_related_action (popup_action);
151 	gtk_activatable_sync_action_properties (activatable, related_action);
152 }
153 
154 static void
155 popup_action_set_property (GObject *object,
156                            guint property_id,
157                            const GValue *value,
158                            GParamSpec *pspec)
159 {
160 	switch (property_id) {
161 		case PROP_RELATED_ACTION:
162 			popup_action_set_related_action (
163 				E_POPUP_ACTION (object),
164 				g_value_get_object (value));
165 			return;
166 
167 		case PROP_USE_ACTION_APPEARANCE:
168 			popup_action_set_use_action_appearance (
169 				E_POPUP_ACTION (object),
170 				g_value_get_boolean (value));
171 			return;
172 	}
173 
174 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
175 }
176 
177 static void
178 popup_action_get_property (GObject *object,
179                            guint property_id,
180                            GValue *value,
181                            GParamSpec *pspec)
182 {
183 	switch (property_id) {
184 		case PROP_RELATED_ACTION:
185 			g_value_set_object (
186 				value,
187 				popup_action_get_related_action (
188 				E_POPUP_ACTION (object)));
189 			return;
190 
191 		case PROP_USE_ACTION_APPEARANCE:
192 			g_value_set_boolean (
193 				value,
194 				popup_action_get_use_action_appearance (
195 				E_POPUP_ACTION (object)));
196 			return;
197 	}
198 
199 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
200 }
201 
202 static void
203 popup_action_dispose (GObject *object)
204 {
205 	EPopupActionPrivate *priv;
206 
207 	priv = E_POPUP_ACTION_GET_PRIVATE (object);
208 
209 	if (priv->related_action != NULL) {
210 		g_signal_handler_disconnect (
211 			object,
212 			priv->activate_handler_id);
213 		g_signal_handler_disconnect (
214 			priv->related_action,
215 			priv->notify_handler_id);
216 		g_object_unref (priv->related_action);
217 		priv->related_action = NULL;
218 	}
219 
220 	/* Chain up to parent's dispose() method. */
221 	G_OBJECT_CLASS (e_popup_action_parent_class)->dispose (object);
222 }
223 
224 static void
225 popup_action_update (GtkActivatable *activatable,
226                      GtkAction *action,
227                      const gchar *property_name)
228 {
229 	GObjectClass *class;
230 	GParamSpec *pspec;
231 	GValue *value;
232 
233 	/* Ignore "action-group" changes" */
234 	if (strcmp (property_name, "action-group") == 0)
235 		return;
236 
237 	/* Ignore "visible" changes. */
238 	if (strcmp (property_name, "visible") == 0)
239 		return;
240 
241 	value = g_slice_new0 (GValue);
242 	class = G_OBJECT_GET_CLASS (action);
243 	pspec = g_object_class_find_property (class, property_name);
244 	g_value_init (value, pspec->value_type);
245 
246 	g_object_get_property (G_OBJECT (action), property_name, value);
247 
248 	if (strcmp (property_name, "sensitive") == 0)
249 		property_name = "visible";
250 	else if (!gtk_activatable_get_use_action_appearance (activatable))
251 		goto exit;
252 
253 	g_object_set_property (G_OBJECT (activatable), property_name, value);
254 
255 exit:
256 	g_value_unset (value);
257 	g_slice_free (GValue, value);
258 }
259 
260 static void
261 popup_action_sync_action_properties (GtkActivatable *activatable,
262                                      GtkAction *action)
263 {
264 	if (action == NULL)
265 		return;
266 
267 	/* XXX GTK+ 2.18 is still missing accessor functions for
268 	 *     "hide-if-empty" and "visible-overflown" properties.
269 	 *     These are rarely used so we'll skip them for now. */
270 
271 	/* A popup action is never shown as insensitive. */
272 	gtk_action_set_sensitive (GTK_ACTION (activatable), TRUE);
273 
274 	gtk_action_set_visible (
275 		GTK_ACTION (activatable),
276 		gtk_action_get_sensitive (action));
277 
278 	gtk_action_set_visible_horizontal (
279 		GTK_ACTION (activatable),
280 		gtk_action_get_visible_horizontal (action));
281 
282 	gtk_action_set_visible_vertical (
283 		GTK_ACTION (activatable),
284 		gtk_action_get_visible_vertical (action));
285 
286 	gtk_action_set_is_important (
287 		GTK_ACTION (activatable),
288 		gtk_action_get_is_important (action));
289 
290 	if (!gtk_activatable_get_use_action_appearance (activatable))
291 		return;
292 
293 	gtk_action_set_label (
294 		GTK_ACTION (activatable),
295 		gtk_action_get_label (action));
296 
297 	gtk_action_set_short_label (
298 		GTK_ACTION (activatable),
299 		gtk_action_get_short_label (action));
300 
301 	gtk_action_set_tooltip (
302 		GTK_ACTION (activatable),
303 		gtk_action_get_tooltip (action));
304 
305 	gtk_action_set_stock_id (
306 		GTK_ACTION (activatable),
307 		gtk_action_get_stock_id (action));
308 
309 	gtk_action_set_gicon (
310 		GTK_ACTION (activatable),
311 		gtk_action_get_gicon (action));
312 
313 	gtk_action_set_icon_name (
314 		GTK_ACTION (activatable),
315 		gtk_action_get_icon_name (action));
316 }
317 
318 static void
319 e_popup_action_class_init (EPopupActionClass *class)
320 {
321 	GObjectClass *object_class;
322 
323 	g_type_class_add_private (class, sizeof (EPopupActionPrivate));
324 
325 	object_class = G_OBJECT_CLASS (class);
326 	object_class->set_property = popup_action_set_property;
327 	object_class->get_property = popup_action_get_property;
328 	object_class->dispose = popup_action_dispose;
329 
330 	g_object_class_override_property (
331 		object_class,
332 		PROP_RELATED_ACTION,
333 		"related-action");
334 
335 	g_object_class_override_property (
336 		object_class,
337 		PROP_USE_ACTION_APPEARANCE,
338 		"use-action-appearance");
339 }
340 
341 static void
342 e_popup_action_init (EPopupAction *popup_action)
343 {
344 	popup_action->priv = E_POPUP_ACTION_GET_PRIVATE (popup_action);
345 	popup_action->priv->use_action_appearance = TRUE;
346 
347 	/* Remain invisible until we have a related action. */
348 	gtk_action_set_visible (GTK_ACTION (popup_action), FALSE);
349 }
350 
351 static void
352 e_popup_action_activatable_init (GtkActivatableIface *interface)
353 {
354 	interface->update = popup_action_update;
355 	interface->sync_action_properties = popup_action_sync_action_properties;
356 }
357 
358 EPopupAction *
359 e_popup_action_new (const gchar *name)
360 {
361 	g_return_val_if_fail (name != NULL, NULL);
362 
363 	return g_object_new (E_TYPE_POPUP_ACTION, "name", name, NULL);
364 }
365 
366 void
367 e_action_group_add_popup_actions (GtkActionGroup *action_group,
368                                   const EPopupActionEntry *entries,
369                                   guint n_entries)
370 {
371 	guint ii;
372 
373 	g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
374 
375 	for (ii = 0; ii < n_entries; ii++) {
376 		EPopupAction *popup_action;
377 		GtkAction *related_action;
378 		const gchar *label;
379 
380 		label = gtk_action_group_translate_string (
381 			action_group, entries[ii].label);
382 
383 		related_action = gtk_action_group_get_action (
384 			action_group, entries[ii].related);
385 
386 		if (related_action == NULL) {
387 			g_warning (
388 				"Related action '%s' not found in "
389 				"action group '%s'", entries[ii].related,
390 				gtk_action_group_get_name (action_group));
391 			continue;
392 		}
393 
394 		popup_action = e_popup_action_new (entries[ii].name);
395 
396 		gtk_activatable_set_related_action (
397 			GTK_ACTIVATABLE (popup_action), related_action);
398 
399 		if (label != NULL && *label != '\0')
400 			gtk_action_set_label (
401 				GTK_ACTION (popup_action), label);
402 
403 		gtk_action_group_add_action (
404 			action_group, GTK_ACTION (popup_action));
405 
406 		g_object_unref (popup_action);
407 	}
408 }