evolution-3.6.4/widgets/misc/e-focus-tracker.c

No issues found

  1 /*
  2  * e-focus-tracker.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-focus-tracker.h"
 27 
 28 #include <glib/gi18n-lib.h>
 29 
 30 #include <misc/e-selectable.h>
 31 
 32 #define E_FOCUS_TRACKER_GET_PRIVATE(obj) \
 33 	(G_TYPE_INSTANCE_GET_PRIVATE \
 34 	((obj), E_TYPE_FOCUS_TRACKER, EFocusTrackerPrivate))
 35 
 36 struct _EFocusTrackerPrivate {
 37 	GtkWidget *focus;  /* not referenced */
 38 	GtkWindow *window;
 39 
 40 	GtkAction *cut_clipboard;
 41 	GtkAction *copy_clipboard;
 42 	GtkAction *paste_clipboard;
 43 	GtkAction *delete_selection;
 44 	GtkAction *select_all;
 45 };
 46 
 47 enum {
 48 	PROP_0,
 49 	PROP_FOCUS,
 50 	PROP_WINDOW,
 51 	PROP_CUT_CLIPBOARD_ACTION,
 52 	PROP_COPY_CLIPBOARD_ACTION,
 53 	PROP_PASTE_CLIPBOARD_ACTION,
 54 	PROP_DELETE_SELECTION_ACTION,
 55 	PROP_SELECT_ALL_ACTION
 56 };
 57 
 58 G_DEFINE_TYPE (
 59 	EFocusTracker,
 60 	e_focus_tracker,
 61 	G_TYPE_OBJECT)
 62 
 63 static void
 64 focus_tracker_disable_actions (EFocusTracker *focus_tracker)
 65 {
 66 	GtkAction *action;
 67 
 68 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
 69 	if (action != NULL)
 70 		gtk_action_set_sensitive (action, FALSE);
 71 
 72 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
 73 	if (action != NULL)
 74 		gtk_action_set_sensitive (action, FALSE);
 75 
 76 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
 77 	if (action != NULL)
 78 		gtk_action_set_sensitive (action, FALSE);
 79 
 80 	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
 81 	if (action != NULL)
 82 		gtk_action_set_sensitive (action, FALSE);
 83 
 84 	action = e_focus_tracker_get_select_all_action (focus_tracker);
 85 	if (action != NULL)
 86 		gtk_action_set_sensitive (action, FALSE);
 87 }
 88 
 89 static void
 90 focus_tracker_editable_update_actions (EFocusTracker *focus_tracker,
 91                                        GtkEditable *editable,
 92                                        GdkAtom *targets,
 93                                        gint n_targets)
 94 {
 95 	GtkAction *action;
 96 	gboolean can_edit_text;
 97 	gboolean clipboard_has_text;
 98 	gboolean text_is_selected;
 99 	gboolean sensitive;
100 
101 	can_edit_text =
102 		gtk_editable_get_editable (editable);
103 
104 	clipboard_has_text = (targets != NULL) &&
105 		gtk_targets_include_text (targets, n_targets);
106 
107 	text_is_selected =
108 		gtk_editable_get_selection_bounds (editable, NULL, NULL);
109 
110 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
111 	if (action != NULL) {
112 		sensitive = can_edit_text && text_is_selected;
113 		gtk_action_set_sensitive (action, sensitive);
114 		gtk_action_set_tooltip (action, _("Cut the selection"));
115 	}
116 
117 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
118 	if (action != NULL) {
119 		sensitive = text_is_selected;
120 		gtk_action_set_sensitive (action, sensitive);
121 		gtk_action_set_tooltip (action, _("Copy the selection"));
122 	}
123 
124 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
125 	if (action != NULL) {
126 		sensitive = can_edit_text && clipboard_has_text;
127 		gtk_action_set_sensitive (action, sensitive);
128 		gtk_action_set_tooltip (action, _("Paste the clipboard"));
129 	}
130 
131 	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
132 	if (action != NULL) {
133 		sensitive = can_edit_text && text_is_selected;
134 		gtk_action_set_sensitive (action, sensitive);
135 		gtk_action_set_tooltip (action, _("Delete the selection"));
136 	}
137 
138 	action = e_focus_tracker_get_select_all_action (focus_tracker);
139 	if (action != NULL) {
140 		sensitive = TRUE;  /* always enabled */
141 		gtk_action_set_sensitive (action, sensitive);
142 		gtk_action_set_tooltip (action, _("Select all text"));
143 	}
144 }
145 
146 static void
147 focus_tracker_selectable_update_actions (EFocusTracker *focus_tracker,
148                                          ESelectable *selectable,
149                                          GdkAtom *targets,
150                                          gint n_targets)
151 {
152 	ESelectableInterface *interface;
153 	GtkAction *action;
154 
155 	interface = E_SELECTABLE_GET_INTERFACE (selectable);
156 
157 	e_selectable_update_actions (
158 		selectable, focus_tracker, targets, n_targets);
159 
160 	/* Disable actions for which the corresponding method is not
161 	 * implemented.  This allows update_actions() implementations
162 	 * to simply skip the actions they don't support, which in turn
163 	 * allows us to add new actions without disturbing the existing
164 	 * ESelectable implementations. */
165 
166 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
167 	if (action != NULL && interface->cut_clipboard == NULL)
168 		gtk_action_set_sensitive (action, FALSE);
169 
170 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
171 	if (action != NULL && interface->copy_clipboard == NULL)
172 		gtk_action_set_sensitive (action, FALSE);
173 
174 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
175 	if (action != NULL && interface->paste_clipboard == NULL)
176 		gtk_action_set_sensitive (action, FALSE);
177 
178 	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
179 	if (action != NULL && interface->delete_selection == NULL)
180 		gtk_action_set_sensitive (action, FALSE);
181 
182 	action = e_focus_tracker_get_select_all_action (focus_tracker);
183 	if (action != NULL && interface->select_all == NULL)
184 		gtk_action_set_sensitive (action, FALSE);
185 }
186 
187 static void
188 focus_tracker_targets_received_cb (GtkClipboard *clipboard,
189                                    GdkAtom *targets,
190                                    gint n_targets,
191                                    EFocusTracker *focus_tracker)
192 {
193 	GtkWidget *focus;
194 
195 	focus = e_focus_tracker_get_focus (focus_tracker);
196 
197 	if (focus == NULL)
198 		focus_tracker_disable_actions (focus_tracker);
199 
200 	else if (GTK_IS_EDITABLE (focus))
201 		focus_tracker_editable_update_actions (
202 			focus_tracker, GTK_EDITABLE (focus),
203 			targets, n_targets);
204 
205 	else if (E_IS_SELECTABLE (focus))
206 		focus_tracker_selectable_update_actions (
207 			focus_tracker, E_SELECTABLE (focus),
208 			targets, n_targets);
209 
210 	g_object_unref (focus_tracker);
211 }
212 
213 static void
214 focus_tracker_set_focus_cb (GtkWindow *window,
215                             GtkWidget *focus,
216                             EFocusTracker *focus_tracker)
217 {
218 	while (focus != NULL) {
219 		if (GTK_IS_EDITABLE (focus))
220 			break;
221 
222 		if (E_IS_SELECTABLE (focus))
223 			break;
224 
225 		focus = gtk_widget_get_parent (focus);
226 	}
227 
228 	if (focus == focus_tracker->priv->focus)
229 		return;
230 
231 	focus_tracker->priv->focus = focus;
232 	g_object_notify (G_OBJECT (focus_tracker), "focus");
233 
234 	e_focus_tracker_update_actions (focus_tracker);
235 }
236 
237 static void
238 focus_tracker_set_window (EFocusTracker *focus_tracker,
239                           GtkWindow *window)
240 {
241 	g_return_if_fail (GTK_IS_WINDOW (window));
242 	g_return_if_fail (focus_tracker->priv->window == NULL);
243 
244 	focus_tracker->priv->window = g_object_ref (window);
245 
246 	g_signal_connect (
247 		window, "set-focus",
248 		G_CALLBACK (focus_tracker_set_focus_cb), focus_tracker);
249 }
250 
251 static void
252 focus_tracker_set_property (GObject *object,
253                             guint property_id,
254                             const GValue *value,
255                             GParamSpec *pspec)
256 {
257 	switch (property_id) {
258 		case PROP_WINDOW:
259 			focus_tracker_set_window (
260 				E_FOCUS_TRACKER (object),
261 				g_value_get_object (value));
262 			return;
263 
264 		case PROP_CUT_CLIPBOARD_ACTION:
265 			e_focus_tracker_set_cut_clipboard_action (
266 				E_FOCUS_TRACKER (object),
267 				g_value_get_object (value));
268 			return;
269 
270 		case PROP_COPY_CLIPBOARD_ACTION:
271 			e_focus_tracker_set_copy_clipboard_action (
272 				E_FOCUS_TRACKER (object),
273 				g_value_get_object (value));
274 			return;
275 
276 		case PROP_PASTE_CLIPBOARD_ACTION:
277 			e_focus_tracker_set_paste_clipboard_action (
278 				E_FOCUS_TRACKER (object),
279 				g_value_get_object (value));
280 			return;
281 
282 		case PROP_DELETE_SELECTION_ACTION:
283 			e_focus_tracker_set_delete_selection_action (
284 				E_FOCUS_TRACKER (object),
285 				g_value_get_object (value));
286 			return;
287 
288 		case PROP_SELECT_ALL_ACTION:
289 			e_focus_tracker_set_select_all_action (
290 				E_FOCUS_TRACKER (object),
291 				g_value_get_object (value));
292 			return;
293 	}
294 
295 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
296 }
297 
298 static void
299 focus_tracker_get_property (GObject *object,
300                             guint property_id,
301                             GValue *value,
302                             GParamSpec *pspec)
303 {
304 	switch (property_id) {
305 		case PROP_FOCUS:
306 			g_value_set_object (
307 				value,
308 				e_focus_tracker_get_focus (
309 				E_FOCUS_TRACKER (object)));
310 			return;
311 
312 		case PROP_WINDOW:
313 			g_value_set_object (
314 				value,
315 				e_focus_tracker_get_window (
316 				E_FOCUS_TRACKER (object)));
317 			return;
318 
319 		case PROP_CUT_CLIPBOARD_ACTION:
320 			g_value_set_object (
321 				value,
322 				e_focus_tracker_get_cut_clipboard_action (
323 				E_FOCUS_TRACKER (object)));
324 			return;
325 
326 		case PROP_COPY_CLIPBOARD_ACTION:
327 			g_value_set_object (
328 				value,
329 				e_focus_tracker_get_copy_clipboard_action (
330 				E_FOCUS_TRACKER (object)));
331 			return;
332 
333 		case PROP_PASTE_CLIPBOARD_ACTION:
334 			g_value_set_object (
335 				value,
336 				e_focus_tracker_get_paste_clipboard_action (
337 				E_FOCUS_TRACKER (object)));
338 			return;
339 
340 		case PROP_DELETE_SELECTION_ACTION:
341 			g_value_set_object (
342 				value,
343 				e_focus_tracker_get_delete_selection_action (
344 				E_FOCUS_TRACKER (object)));
345 			return;
346 
347 		case PROP_SELECT_ALL_ACTION:
348 			g_value_set_object (
349 				value,
350 				e_focus_tracker_get_select_all_action (
351 				E_FOCUS_TRACKER (object)));
352 			return;
353 	}
354 
355 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
356 }
357 
358 static void
359 focus_tracker_dispose (GObject *object)
360 {
361 	EFocusTrackerPrivate *priv;
362 
363 	priv = E_FOCUS_TRACKER_GET_PRIVATE (object);
364 
365 	g_signal_handlers_disconnect_matched (
366 		gtk_clipboard_get (GDK_SELECTION_PRIMARY),
367 		G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
368 
369 	g_signal_handlers_disconnect_matched (
370 		gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
371 		G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
372 
373 	if (priv->window != NULL) {
374 		g_signal_handlers_disconnect_matched (
375 			priv->window, G_SIGNAL_MATCH_DATA,
376 			0, 0, NULL, NULL, object);
377 		g_object_unref (priv->window);
378 		priv->window = NULL;
379 	}
380 
381 	if (priv->cut_clipboard != NULL) {
382 		g_signal_handlers_disconnect_matched (
383 			priv->cut_clipboard, G_SIGNAL_MATCH_DATA,
384 			0, 0, NULL, NULL, object);
385 		g_object_unref (priv->cut_clipboard);
386 		priv->cut_clipboard = NULL;
387 	}
388 
389 	if (priv->copy_clipboard != NULL) {
390 		g_signal_handlers_disconnect_matched (
391 			priv->copy_clipboard, G_SIGNAL_MATCH_DATA,
392 			0, 0, NULL, NULL, object);
393 		g_object_unref (priv->copy_clipboard);
394 		priv->copy_clipboard = NULL;
395 	}
396 
397 	if (priv->paste_clipboard != NULL) {
398 		g_signal_handlers_disconnect_matched (
399 			priv->paste_clipboard, G_SIGNAL_MATCH_DATA,
400 			0, 0, NULL, NULL, object);
401 		g_object_unref (priv->paste_clipboard);
402 		priv->paste_clipboard = NULL;
403 	}
404 
405 	if (priv->delete_selection != NULL) {
406 		g_signal_handlers_disconnect_matched (
407 			priv->delete_selection, G_SIGNAL_MATCH_DATA,
408 			0, 0, NULL, NULL, object);
409 		g_object_unref (priv->delete_selection);
410 		priv->delete_selection = NULL;
411 	}
412 
413 	if (priv->select_all != NULL) {
414 		g_signal_handlers_disconnect_matched (
415 			priv->select_all, G_SIGNAL_MATCH_DATA,
416 			0, 0, NULL, NULL, object);
417 		g_object_unref (priv->select_all);
418 		priv->select_all = NULL;
419 	}
420 
421 	/* Chain up to parent's dispose() method. */
422 	G_OBJECT_CLASS (e_focus_tracker_parent_class)->dispose (object);
423 }
424 
425 static void
426 focus_tracker_constructed (GObject *object)
427 {
428 	GtkClipboard *clipboard;
429 
430 	/* Listen for "owner-change" signals from the primary selection
431 	 * clipboard to learn when text selections change in GtkEditable
432 	 * widgets.  It's a bit of an overkill, but I don't know of any
433 	 * other notification mechanism. */
434 
435 	clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
436 
437 	g_signal_connect_swapped (
438 		clipboard, "owner-change",
439 		G_CALLBACK (e_focus_tracker_update_actions), object);
440 
441 	/* Listen for "owner-change" signals from the default clipboard
442 	 * so we can update the paste action when the user cuts or copies
443 	 * something.  This is how GEdit does it. */
444 
445 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
446 
447 	g_signal_connect_swapped (
448 		clipboard, "owner-change",
449 		G_CALLBACK (e_focus_tracker_update_actions), object);
450 
451 	/* Chain up to parent's constructed() method. */
452 	G_OBJECT_CLASS (e_focus_tracker_parent_class)->constructed (object);
453 }
454 
455 static void
456 e_focus_tracker_class_init (EFocusTrackerClass *class)
457 {
458 	GObjectClass *object_class;
459 
460 	g_type_class_add_private (class, sizeof (EFocusTrackerPrivate));
461 
462 	object_class = G_OBJECT_CLASS (class);
463 	object_class->set_property = focus_tracker_set_property;
464 	object_class->get_property = focus_tracker_get_property;
465 	object_class->dispose = focus_tracker_dispose;
466 	object_class->constructed = focus_tracker_constructed;
467 
468 	g_object_class_install_property (
469 		object_class,
470 		PROP_FOCUS,
471 		g_param_spec_object (
472 			"focus",
473 			"Focus",
474 			NULL,
475 			GTK_TYPE_WIDGET,
476 			G_PARAM_READABLE));
477 
478 	g_object_class_install_property (
479 		object_class,
480 		PROP_WINDOW,
481 		g_param_spec_object (
482 			"window",
483 			"Window",
484 			NULL,
485 			GTK_TYPE_WINDOW,
486 			G_PARAM_READWRITE |
487 			G_PARAM_CONSTRUCT_ONLY));
488 
489 	g_object_class_install_property (
490 		object_class,
491 		PROP_CUT_CLIPBOARD_ACTION,
492 		g_param_spec_object (
493 			"cut-clipboard-action",
494 			"Cut Clipboard Action",
495 			NULL,
496 			GTK_TYPE_ACTION,
497 			G_PARAM_READWRITE));
498 
499 	g_object_class_install_property (
500 		object_class,
501 		PROP_COPY_CLIPBOARD_ACTION,
502 		g_param_spec_object (
503 			"copy-clipboard-action",
504 			"Copy Clipboard Action",
505 			NULL,
506 			GTK_TYPE_ACTION,
507 			G_PARAM_READWRITE));
508 
509 	g_object_class_install_property (
510 		object_class,
511 		PROP_PASTE_CLIPBOARD_ACTION,
512 		g_param_spec_object (
513 			"paste-clipboard-action",
514 			"Paste Clipboard Action",
515 			NULL,
516 			GTK_TYPE_ACTION,
517 			G_PARAM_READWRITE));
518 
519 	g_object_class_install_property (
520 		object_class,
521 		PROP_DELETE_SELECTION_ACTION,
522 		g_param_spec_object (
523 			"delete-selection-action",
524 			"Delete Selection Action",
525 			NULL,
526 			GTK_TYPE_ACTION,
527 			G_PARAM_READWRITE));
528 
529 	g_object_class_install_property (
530 		object_class,
531 		PROP_SELECT_ALL_ACTION,
532 		g_param_spec_object (
533 			"select-all-action",
534 			"Select All Action",
535 			NULL,
536 			GTK_TYPE_ACTION,
537 			G_PARAM_READWRITE));
538 }
539 
540 static void
541 e_focus_tracker_init (EFocusTracker *focus_tracker)
542 {
543 	GtkAction *action;
544 
545 	focus_tracker->priv = E_FOCUS_TRACKER_GET_PRIVATE (focus_tracker);
546 
547 	/* Define dummy actions.  These will most likely be overridden,
548 	 * but for cases where they're not it ensures ESelectable objects
549 	 * will always get a valid GtkAction when they ask us for one. */
550 
551 	action = gtk_action_new (
552 		"cut-clipboard", NULL,
553 		_("Cut the selection"), GTK_STOCK_CUT);
554 	focus_tracker->priv->cut_clipboard = action;
555 
556 	action = gtk_action_new (
557 		"copy-clipboard", NULL,
558 		_("Copy the selection"), GTK_STOCK_COPY);
559 	focus_tracker->priv->copy_clipboard = action;
560 
561 	action = gtk_action_new (
562 		"paste-clipboard", NULL,
563 		_("Paste the clipboard"), GTK_STOCK_PASTE);
564 	focus_tracker->priv->paste_clipboard = action;
565 
566 	action = gtk_action_new (
567 		"delete-selection", NULL,
568 		_("Delete the selection"), GTK_STOCK_DELETE);
569 	focus_tracker->priv->delete_selection = action;
570 
571 	action = gtk_action_new (
572 		"select-all", NULL,
573 		_("Select all text"), GTK_STOCK_SELECT_ALL);
574 	focus_tracker->priv->select_all = action;
575 }
576 
577 EFocusTracker *
578 e_focus_tracker_new (GtkWindow *window)
579 {
580 	g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
581 
582 	return g_object_new (E_TYPE_FOCUS_TRACKER, "window", window, NULL);
583 }
584 
585 GtkWidget *
586 e_focus_tracker_get_focus (EFocusTracker *focus_tracker)
587 {
588 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
589 
590 	return focus_tracker->priv->focus;
591 }
592 
593 GtkWindow *
594 e_focus_tracker_get_window (EFocusTracker *focus_tracker)
595 {
596 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
597 
598 	return focus_tracker->priv->window;
599 }
600 
601 GtkAction *
602 e_focus_tracker_get_cut_clipboard_action (EFocusTracker *focus_tracker)
603 {
604 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
605 
606 	return focus_tracker->priv->cut_clipboard;
607 }
608 
609 void
610 e_focus_tracker_set_cut_clipboard_action (EFocusTracker *focus_tracker,
611                                           GtkAction *cut_clipboard)
612 {
613 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
614 
615 	if (cut_clipboard != NULL) {
616 		g_return_if_fail (GTK_IS_ACTION (cut_clipboard));
617 		g_object_ref (cut_clipboard);
618 	}
619 
620 	if (focus_tracker->priv->cut_clipboard != NULL) {
621 		g_signal_handlers_disconnect_matched (
622 			focus_tracker->priv->cut_clipboard,
623 			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
624 			focus_tracker);
625 		g_object_unref (focus_tracker->priv->cut_clipboard);
626 	}
627 
628 	focus_tracker->priv->cut_clipboard = cut_clipboard;
629 
630 	if (cut_clipboard != NULL)
631 		g_signal_connect_swapped (
632 			cut_clipboard, "activate",
633 			G_CALLBACK (e_focus_tracker_cut_clipboard),
634 			focus_tracker);
635 
636 	g_object_notify (G_OBJECT (focus_tracker), "cut-clipboard-action");
637 }
638 
639 GtkAction *
640 e_focus_tracker_get_copy_clipboard_action (EFocusTracker *focus_tracker)
641 {
642 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
643 
644 	return focus_tracker->priv->copy_clipboard;
645 }
646 
647 void
648 e_focus_tracker_set_copy_clipboard_action (EFocusTracker *focus_tracker,
649                                            GtkAction *copy_clipboard)
650 {
651 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
652 
653 	if (copy_clipboard != NULL) {
654 		g_return_if_fail (GTK_IS_ACTION (copy_clipboard));
655 		g_object_ref (copy_clipboard);
656 	}
657 
658 	if (focus_tracker->priv->copy_clipboard != NULL) {
659 		g_signal_handlers_disconnect_matched (
660 			focus_tracker->priv->copy_clipboard,
661 			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
662 			focus_tracker);
663 		g_object_unref (focus_tracker->priv->copy_clipboard);
664 	}
665 
666 	focus_tracker->priv->copy_clipboard = copy_clipboard;
667 
668 	if (copy_clipboard != NULL)
669 		g_signal_connect_swapped (
670 			copy_clipboard, "activate",
671 			G_CALLBACK (e_focus_tracker_copy_clipboard),
672 			focus_tracker);
673 
674 	g_object_notify (G_OBJECT (focus_tracker), "copy-clipboard-action");
675 }
676 
677 GtkAction *
678 e_focus_tracker_get_paste_clipboard_action (EFocusTracker *focus_tracker)
679 {
680 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
681 
682 	return focus_tracker->priv->paste_clipboard;
683 }
684 
685 void
686 e_focus_tracker_set_paste_clipboard_action (EFocusTracker *focus_tracker,
687                                             GtkAction *paste_clipboard)
688 {
689 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
690 
691 	if (paste_clipboard != NULL) {
692 		g_return_if_fail (GTK_IS_ACTION (paste_clipboard));
693 		g_object_ref (paste_clipboard);
694 	}
695 
696 	if (focus_tracker->priv->paste_clipboard != NULL) {
697 		g_signal_handlers_disconnect_matched (
698 			focus_tracker->priv->paste_clipboard,
699 			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
700 			focus_tracker);
701 		g_object_unref (focus_tracker->priv->paste_clipboard);
702 	}
703 
704 	focus_tracker->priv->paste_clipboard = paste_clipboard;
705 
706 	if (paste_clipboard != NULL)
707 		g_signal_connect_swapped (
708 			paste_clipboard, "activate",
709 			G_CALLBACK (e_focus_tracker_paste_clipboard),
710 			focus_tracker);
711 
712 	g_object_notify (G_OBJECT (focus_tracker), "paste-clipboard-action");
713 }
714 
715 GtkAction *
716 e_focus_tracker_get_delete_selection_action (EFocusTracker *focus_tracker)
717 {
718 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
719 
720 	return focus_tracker->priv->delete_selection;
721 }
722 
723 void
724 e_focus_tracker_set_delete_selection_action (EFocusTracker *focus_tracker,
725                                              GtkAction *delete_selection)
726 {
727 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
728 
729 	if (delete_selection != NULL) {
730 		g_return_if_fail (GTK_IS_ACTION (delete_selection));
731 		g_object_ref (delete_selection);
732 	}
733 
734 	if (focus_tracker->priv->delete_selection != NULL) {
735 		g_signal_handlers_disconnect_matched (
736 			focus_tracker->priv->delete_selection,
737 			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
738 			focus_tracker);
739 		g_object_unref (focus_tracker->priv->delete_selection);
740 	}
741 
742 	focus_tracker->priv->delete_selection = delete_selection;
743 
744 	if (delete_selection != NULL)
745 		g_signal_connect_swapped (
746 			delete_selection, "activate",
747 			G_CALLBACK (e_focus_tracker_delete_selection),
748 			focus_tracker);
749 
750 	g_object_notify (G_OBJECT (focus_tracker), "delete-selection-action");
751 }
752 
753 GtkAction *
754 e_focus_tracker_get_select_all_action (EFocusTracker *focus_tracker)
755 {
756 	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
757 
758 	return focus_tracker->priv->select_all;
759 }
760 
761 void
762 e_focus_tracker_set_select_all_action (EFocusTracker *focus_tracker,
763                                        GtkAction *select_all)
764 {
765 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
766 
767 	if (select_all != NULL) {
768 		g_return_if_fail (GTK_IS_ACTION (select_all));
769 		g_object_ref (select_all);
770 	}
771 
772 	if (focus_tracker->priv->select_all != NULL) {
773 		g_signal_handlers_disconnect_matched (
774 			focus_tracker->priv->select_all,
775 			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
776 			focus_tracker);
777 		g_object_unref (focus_tracker->priv->select_all);
778 	}
779 
780 	focus_tracker->priv->select_all = select_all;
781 
782 	if (select_all != NULL)
783 		g_signal_connect_swapped (
784 			select_all, "activate",
785 			G_CALLBACK (e_focus_tracker_select_all),
786 			focus_tracker);
787 
788 	g_object_notify (G_OBJECT (focus_tracker), "select-all-action");
789 }
790 
791 void
792 e_focus_tracker_update_actions (EFocusTracker *focus_tracker)
793 {
794 	GtkClipboard *clipboard;
795 
796 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
797 
798 	/* Request clipboard targets asynchronously. */
799 
800 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
801 
802 	gtk_clipboard_request_targets (
803 		clipboard, (GtkClipboardTargetsReceivedFunc)
804 		focus_tracker_targets_received_cb,
805 		g_object_ref (focus_tracker));
806 }
807 
808 void
809 e_focus_tracker_cut_clipboard (EFocusTracker *focus_tracker)
810 {
811 	GtkWidget *focus;
812 
813 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
814 
815 	focus = e_focus_tracker_get_focus (focus_tracker);
816 
817 	if (GTK_IS_EDITABLE (focus))
818 		gtk_editable_cut_clipboard (GTK_EDITABLE (focus));
819 
820 	else if (E_IS_SELECTABLE (focus))
821 		e_selectable_cut_clipboard (E_SELECTABLE (focus));
822 }
823 
824 void
825 e_focus_tracker_copy_clipboard (EFocusTracker *focus_tracker)
826 {
827 	GtkWidget *focus;
828 
829 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
830 
831 	focus = e_focus_tracker_get_focus (focus_tracker);
832 
833 	if (GTK_IS_EDITABLE (focus))
834 		gtk_editable_copy_clipboard (GTK_EDITABLE (focus));
835 
836 	else if (E_IS_SELECTABLE (focus))
837 		e_selectable_copy_clipboard (E_SELECTABLE (focus));
838 }
839 
840 void
841 e_focus_tracker_paste_clipboard (EFocusTracker *focus_tracker)
842 {
843 	GtkWidget *focus;
844 
845 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
846 
847 	focus = e_focus_tracker_get_focus (focus_tracker);
848 
849 	if (GTK_IS_EDITABLE (focus))
850 		gtk_editable_paste_clipboard (GTK_EDITABLE (focus));
851 
852 	else if (E_IS_SELECTABLE (focus))
853 		e_selectable_paste_clipboard (E_SELECTABLE (focus));
854 }
855 
856 void
857 e_focus_tracker_delete_selection (EFocusTracker *focus_tracker)
858 {
859 	GtkWidget *focus;
860 
861 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
862 
863 	focus = e_focus_tracker_get_focus (focus_tracker);
864 
865 	if (GTK_IS_EDITABLE (focus))
866 		gtk_editable_delete_selection (GTK_EDITABLE (focus));
867 
868 	else if (E_IS_SELECTABLE (focus))
869 		e_selectable_delete_selection (E_SELECTABLE (focus));
870 }
871 
872 void
873 e_focus_tracker_select_all (EFocusTracker *focus_tracker)
874 {
875 	GtkWidget *focus;
876 
877 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
878 
879 	focus = e_focus_tracker_get_focus (focus_tracker);
880 
881 	if (GTK_IS_EDITABLE (focus))
882 		gtk_editable_select_region (GTK_EDITABLE (focus), 0, -1);
883 
884 	else if (E_IS_SELECTABLE (focus))
885 		e_selectable_select_all (E_SELECTABLE (focus));
886 }