[evolution] Introduce ESelectable and EFocusTracker.



commit abc0e4c694fb3d9624e890384880def730769fa0
Author: Matthew Barnes <mbarnes redhat com>
Date:   Mon Dec 7 11:31:17 2009 -0500

    Introduce ESelectable and EFocusTracker.
    
    EFocusTracker tracks the input focus within a window and helps keep
    the sensitivity of "selectable" actions in the main menu up-to-date.
    Selectable actions include Cut, Copy, Paste, Select All and Delete.
    
    EFocusTracker has built-in support for widgets that implement the
    GtkEditable interface such as GtkEntry and GtkTextView.  It also
    supports custom widgets that implement the ESelectable interface,
    which is a subset of GtkEditable and can apply to anything that
    displays selectable content (esp. tree views and ETables).
    
    This commit integrates EFocusTracker with EShellWindow, CompEditor,
    EMsgComposer, and ESignatureManager.
    
    It also bumps the GtkHTML requirement to 2.29.5 to utilize the new
    GtkhtmlEditor:html constructor property.

 addressbook/gui/widgets/e-addressbook-view.c    |  230 ++++--
 addressbook/gui/widgets/e-addressbook-view.h    |    4 -
 calendar/gui/dialogs/comp-editor.c              |  153 ++--
 calendar/gui/dialogs/comp-editor.h              |    2 +
 calendar/gui/e-cal-list-view.h                  |    3 +
 calendar/gui/e-calendar-table.c                 |  540 ++++++++------
 calendar/gui/e-calendar-table.h                 |    5 -
 calendar/gui/e-calendar-view.c                  |  682 +++++++++++--------
 calendar/gui/e-calendar-view.h                  |    3 -
 calendar/gui/e-day-view-main-item.c             |    1 +
 calendar/gui/e-memo-table.c                     |  544 ++++++++------
 calendar/gui/e-memo-table.h                     |    5 -
 calendar/gui/gnome-cal.h                        |    2 +-
 calendar/gui/goto.c                             |    1 +
 calendar/gui/print.h                            |    1 +
 composer/e-composer-private.c                   |   28 +-
 composer/e-composer-private.h                   |    3 +-
 composer/e-msg-composer.c                       |  295 +++++----
 composer/e-msg-composer.h                       |    2 +
 configure.ac                                    |    2 +-
 doc/reference/shell/tmpl/e-shell-window.sgml    |    5 +
 mail/e-mail-reader.c                            |   83 ---
 mail/message-list.c                             |  108 +++-
 mail/message-list.h                             |  111 ++--
 modules/addressbook/e-book-shell-content.c      |   31 -
 modules/addressbook/e-book-shell-content.h      |    6 +-
 modules/addressbook/e-book-shell-view-actions.c |   92 ---
 modules/addressbook/e-book-shell-view-actions.h |    8 -
 modules/addressbook/e-book-shell-view.c         |   25 +-
 modules/calendar/e-cal-shell-content.c          |  108 ---
 modules/calendar/e-cal-shell-content.h          |    7 +-
 modules/calendar/e-cal-shell-view-actions.c     |   63 --
 modules/calendar/e-cal-shell-view-actions.h     |   18 -
 modules/calendar/e-cal-shell-view-memopad.c     |   72 --
 modules/calendar/e-cal-shell-view-taskpad.c     |   72 --
 modules/calendar/e-cal-shell-view.c             |   20 +-
 modules/calendar/e-memo-shell-content.c         |    5 -
 modules/calendar/e-memo-shell-content.h         |    3 +-
 modules/calendar/e-memo-shell-view-actions.c    |   72 --
 modules/calendar/e-memo-shell-view-actions.h    |    6 -
 modules/calendar/e-memo-shell-view.c            |   18 +-
 modules/calendar/e-task-shell-content.c         |    5 -
 modules/calendar/e-task-shell-content.h         |    3 +-
 modules/calendar/e-task-shell-view-actions.c    |   72 --
 modules/calendar/e-task-shell-view-actions.h    |    6 -
 modules/calendar/e-task-shell-view.c            |   18 +-
 modules/mail/e-mail-shell-view-actions.c        |   49 --
 modules/mail/e-mail-shell-view-private.c        |    1 +
 modules/mail/e-mail-shell-view.c                |    7 +-
 shell/e-shell-view.c                            |   45 +-
 shell/e-shell-window-actions.c                  |   73 ++
 shell/e-shell-window-actions.h                  |   10 +
 shell/e-shell-window-private.c                  |    1 +
 shell/e-shell-window-private.h                  |    2 +
 shell/e-shell-window.c                          |   40 ++
 shell/e-shell-window.h                          |    2 +
 ui/evolution-calendars.ui                       |   24 +-
 ui/evolution-contacts.ui                        |   12 +-
 ui/evolution-mail-reader.ui                     |    6 -
 ui/evolution-mail.ui                            |    1 -
 ui/evolution-memos.ui                           |   16 +-
 ui/evolution-shell.ui                           |    6 +
 ui/evolution-tasks.ui                           |   16 +-
 widgets/misc/Makefile.am                        |    4 +
 widgets/misc/e-focus-tracker.c                  |  859 +++++++++++++++++++++++
 widgets/misc/e-focus-tracker.h                  |   99 +++
 widgets/misc/e-selectable.c                     |  133 ++++
 widgets/misc/e-selectable.h                     |   76 ++
 widgets/misc/e-signature-editor.c               |   64 ++-
 widgets/misc/e-signature-editor.h               |    3 +
 widgets/misc/e-web-view.c                       |   78 ++-
 widgets/misc/e-web-view.h                       |    2 +-
 widgets/table/e-table.c                         |   10 +-
 widgets/table/e-tree.c                          |    9 +-
 74 files changed, 3141 insertions(+), 2050 deletions(-)
---
diff --git a/addressbook/gui/widgets/e-addressbook-view.c b/addressbook/gui/widgets/e-addressbook-view.c
index a48ea8c..91fdb16 100644
--- a/addressbook/gui/widgets/e-addressbook-view.c
+++ b/addressbook/gui/widgets/e-addressbook-view.c
@@ -28,6 +28,7 @@
 #include <table/e-table-model.h>
 #include <table/e-cell-date.h>
 #include <misc/e-gui-utils.h>
+#include <misc/e-selectable.h>
 #include <widgets/menus/gal-view-factory-etable.h>
 #include <filter/e-rule-editor.h>
 #include <widgets/menus/gal-view-etable.h>
@@ -539,6 +540,140 @@ addressbook_view_constructed (GObject *object)
 }
 
 static void
+addressbook_view_update_actions (ESelectable *selectable,
+                                 EFocusTracker *focus_tracker,
+                                 GdkAtom *clipboard_targets,
+                                 gint n_clipboard_targets)
+{
+	EAddressbookView *view;
+	EAddressbookModel *model;
+	ESelectionModel *selection_model;
+	GtkAction *action;
+	gboolean clipboard_has_directory;
+	gboolean source_is_editable;
+	gboolean sensitive;
+	const gchar *tooltip;
+	gint n_contacts;
+	gint n_selected;
+
+	view = E_ADDRESSBOOK_VIEW (selectable);
+	model = e_addressbook_view_get_model (view);
+	selection_model = e_addressbook_view_get_selection_model (view);
+
+	source_is_editable = e_addressbook_model_get_editable (model);
+	n_contacts = (selection_model != NULL) ?
+		e_selection_model_row_count (selection_model) : 0;
+	n_selected = (selection_model != NULL) ?
+		e_selection_model_selected_count (selection_model) : 0;
+
+	clipboard_has_directory = (clipboard_targets != NULL) &&
+		e_targets_include_directory (
+		clipboard_targets, n_clipboard_targets);
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	sensitive = source_is_editable && (n_selected > 0);
+	tooltip = _("Cut selected contacts to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0);
+	tooltip = _("Copy selected contacts to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	sensitive = source_is_editable && clipboard_has_directory;
+	tooltip = _("Paste contacts from the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	sensitive = (n_contacts > 0);
+	tooltip = _("Select all visible contacts");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+}
+
+static void
+addressbook_view_cut_clipboard (ESelectable *selectable)
+{
+	EAddressbookView *view;
+
+	view = E_ADDRESSBOOK_VIEW (selectable);
+
+	e_selectable_copy_clipboard (selectable);
+	e_addressbook_view_delete_selection (view, FALSE);
+}
+
+static void
+addressbook_view_copy_clipboard (ESelectable *selectable)
+{
+	EAddressbookView *view;
+	GtkClipboard *clipboard;
+	GList *contact_list;
+	gchar *string;
+
+	view = E_ADDRESSBOOK_VIEW (selectable);
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+	contact_list = e_addressbook_view_get_selected (view);
+
+	string = eab_contact_list_to_string (contact_list);
+	e_clipboard_set_directory (clipboard, string, -1);
+	g_free (string);
+
+	g_list_foreach (contact_list, (GFunc) g_object_unref, NULL);
+	g_list_free (contact_list);
+}
+
+static void
+addressbook_view_paste_clipboard (ESelectable *selectable)
+{
+	EBook *book;
+	EAddressbookView *view;
+	EAddressbookModel *model;
+	GtkClipboard *clipboard;
+	GList *contact_list, *iter;
+	gchar *string;
+
+	view = E_ADDRESSBOOK_VIEW (selectable);
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+	if (!e_clipboard_wait_is_directory_available (clipboard))
+		return;
+
+	model = e_addressbook_view_get_model (view);
+	book = e_addressbook_model_get_book (model);
+
+	string = e_clipboard_wait_for_directory (clipboard);
+	contact_list = eab_contact_list_from_string (string);
+	g_free (string);
+
+	for (iter = contact_list; iter != NULL; iter = iter->next) {
+		EContact *contact = iter->data;
+
+		eab_merging_book_add_contact (book, contact, NULL, NULL);
+	}
+
+	g_list_foreach (contact_list, (GFunc) g_object_unref, NULL);
+	g_list_free (contact_list);
+}
+
+static void
+addressbook_view_select_all (ESelectable *selectable)
+{
+	EAddressbookView *view;
+	ESelectionModel *selection_model;
+
+	view = E_ADDRESSBOOK_VIEW (selectable);
+	selection_model = e_addressbook_view_get_selection_model (view);
+
+	if (selection_model != NULL)
+		e_selection_model_select_all (selection_model);
+}
+
+static void
 addressbook_view_class_init (EAddressbookViewClass *class)
 {
 	GObjectClass *object_class;
@@ -641,6 +776,16 @@ addressbook_view_init (EAddressbookView *view)
 		GTK_SCROLLED_WINDOW (view), GTK_SHADOW_IN);
 }
 
+static void
+addressbook_view_selectable_init (ESelectableInterface *interface)
+{
+	interface->update_actions = addressbook_view_update_actions;
+	interface->cut_clipboard = addressbook_view_cut_clipboard;
+	interface->copy_clipboard = addressbook_view_copy_clipboard;
+	interface->paste_clipboard = addressbook_view_paste_clipboard;
+	interface->select_all = addressbook_view_select_all;
+}
+
 GType
 e_addressbook_view_get_type (void)
 {
@@ -660,9 +805,18 @@ e_addressbook_view_get_type (void)
 			NULL   /* value_table */
 		};
 
+		static const GInterfaceInfo selectable_info = {
+			(GInterfaceInitFunc) addressbook_view_selectable_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
 		type = g_type_register_static (
 			GTK_TYPE_SCROLLED_WINDOW, "EAddressbookView",
 			&type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_SELECTABLE, &selectable_info);
 	}
 
 	return type;
@@ -1238,82 +1392,6 @@ e_addressbook_view_view (EAddressbookView *view)
 }
 
 void
-e_addressbook_view_cut (EAddressbookView *view)
-{
-	g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (view));
-
-	e_addressbook_view_copy (view);
-	e_addressbook_view_delete_selection (view, FALSE);
-}
-
-void
-e_addressbook_view_copy (EAddressbookView *view)
-{
-	GtkClipboard *clipboard;
-	GList *contact_list;
-	gchar *string;
-
-	g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (view));
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-	contact_list = e_addressbook_view_get_selected (view);
-
-	string = eab_contact_list_to_string (contact_list);
-	e_clipboard_set_directory (clipboard, string, -1);
-	g_free (string);
-
-	g_list_foreach (contact_list, (GFunc) g_object_unref, NULL);
-	g_list_free (contact_list);
-}
-
-void
-e_addressbook_view_paste (EAddressbookView *view)
-{
-	EBook *book;
-	EAddressbookModel *model;
-	GtkClipboard *clipboard;
-	GList *contact_list, *iter;
-	gchar *string;
-
-	g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (view));
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-	if (!e_clipboard_wait_is_directory_available (clipboard))
-		return;
-
-	model = e_addressbook_view_get_model (view);
-	book = e_addressbook_model_get_book (model);
-
-	string = e_clipboard_wait_for_directory (clipboard);
-	contact_list = eab_contact_list_from_string (string);
-	g_free (string);
-
-	for (iter = contact_list; iter != NULL; iter = iter->next) {
-		EContact *contact = iter->data;
-
-		eab_merging_book_add_contact (book, contact, NULL, NULL);
-	}
-
-	g_list_foreach (contact_list, (GFunc) g_object_unref, NULL);
-	g_list_free (contact_list);
-}
-
-void
-e_addressbook_view_select_all (EAddressbookView *view)
-{
-	ESelectionModel *model;
-
-	g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (view));
-
-	model = e_addressbook_view_get_selection_model (view);
-	g_return_if_fail (model);
-
-	e_selection_model_select_all (model);
-}
-
-void
 e_addressbook_view_show_all (EAddressbookView *view)
 {
 	g_return_if_fail (E_IS_ADDRESSBOOK_VIEW (view));
diff --git a/addressbook/gui/widgets/e-addressbook-view.h b/addressbook/gui/widgets/e-addressbook-view.h
index 319d060..55f114a 100644
--- a/addressbook/gui/widgets/e-addressbook-view.h
+++ b/addressbook/gui/widgets/e-addressbook-view.h
@@ -103,10 +103,6 @@ void		e_addressbook_view_print	(EAddressbookView *view,
 void		e_addressbook_view_delete_selection
 						(EAddressbookView *view,
 						 gboolean is_delete);
-void		e_addressbook_view_cut		(EAddressbookView *view);
-void		e_addressbook_view_copy		(EAddressbookView *view);
-void		e_addressbook_view_paste	(EAddressbookView *view);
-void		e_addressbook_view_select_all	(EAddressbookView *view);
 void		e_addressbook_view_show_all	(EAddressbookView *view);
 void		e_addressbook_view_stop		(EAddressbookView *view);
 void		e_addressbook_view_copy_to_folder
diff --git a/calendar/gui/dialogs/comp-editor.c b/calendar/gui/dialogs/comp-editor.c
index 18bd574..2138403 100644
--- a/calendar/gui/dialogs/comp-editor.c
+++ b/calendar/gui/dialogs/comp-editor.c
@@ -80,6 +80,9 @@ struct _CompEditorPrivate {
 
 	gpointer shell;  /* weak pointer */
 
+	/* EFocusTracker keeps selection actions up-to-date. */
+	EFocusTracker *focus_tracker;
+
 	/* Each CompEditor window gets its own GtkWindowGroup, so it
 	 * doesn't block the main window or other CompEditor windows. */
 	GtkWindowGroup *window_group;
@@ -133,6 +136,7 @@ enum {
 	PROP_CHANGED,
 	PROP_CLIENT,
 	PROP_FLAGS,
+	PROP_FOCUS_TRACKER,
 	PROP_SHELL,
 	PROP_SUMMARY
 };
@@ -149,9 +153,10 @@ static const gchar *ui =
 "      <menuitem action='close'/>"
 "    </menu>"
 "    <menu action='edit-menu'>"
-"      <menuitem action='cut'/>"
-"      <menuitem action='copy'/>"
-"      <menuitem action='paste'/>"
+"      <menuitem action='cut-clipboard'/>"
+"      <menuitem action='copy-clipboard'/>"
+"      <menuitem action='paste-clipboard'/>"
+"      <menuitem action='delete-selection'/>"
 "      <separator/>"
 "      <menuitem action='select-all'/>"
 "    </menu>"
@@ -677,36 +682,6 @@ action_close_cb (GtkAction *action,
 }
 
 static void
-action_copy_cb (GtkAction *action,
-                CompEditor *editor)
-{
-	GtkWidget *focus;
-
-	focus = gtk_window_get_focus (GTK_WINDOW (editor));
-
-	if (GTK_IS_ENTRY (focus))
-		gtk_editable_copy_clipboard (GTK_EDITABLE (focus));
-
-	if (GTK_IS_TEXT_VIEW (focus))
-		g_signal_emit_by_name (focus, "copy-clipboard");
-}
-
-static void
-action_cut_cb (GtkAction *action,
-               CompEditor *editor)
-{
-	GtkWidget *focus;
-
-	focus = gtk_window_get_focus (GTK_WINDOW (editor));
-
-	if (GTK_IS_ENTRY (focus))
-		gtk_editable_cut_clipboard (GTK_EDITABLE (focus));
-
-	if (GTK_IS_TEXT_VIEW (focus))
-		g_signal_emit_by_name (focus, "cut-clipboard");
-}
-
-static void
 action_help_cb (GtkAction *action,
                 CompEditor *editor)
 {
@@ -714,21 +689,6 @@ action_help_cb (GtkAction *action,
 }
 
 static void
-action_paste_cb (GtkAction *action,
-                 CompEditor *editor)
-{
-	GtkWidget *focus;
-
-	focus = gtk_window_get_focus (GTK_WINDOW (editor));
-
-	if (GTK_IS_ENTRY (focus))
-		gtk_editable_paste_clipboard (GTK_EDITABLE (focus));
-
-	if (GTK_IS_TEXT_VIEW (focus))
-		g_signal_emit_by_name (focus, "paste-clipboard");
-}
-
-static void
 action_print_cb (GtkAction *action,
                  CompEditor *editor)
 {
@@ -886,23 +846,6 @@ action_save_cb (GtkAction *action,
 }
 
 static void
-action_select_all_cb (GtkAction *action,
-                      CompEditor *editor)
-{
-	GtkWidget *focus;
-
-	focus = gtk_window_get_focus (GTK_WINDOW (editor));
-
-	if (GTK_IS_ENTRY (focus)) {
-		gtk_editable_set_position (GTK_EDITABLE (focus), -1);
-		gtk_editable_select_region (GTK_EDITABLE (focus), 0, -1);
-	}
-
-	if (GTK_IS_TEXT_VIEW (focus))
-		g_signal_emit_by_name (focus, "select-all", TRUE);
-}
-
-static void
 action_view_categories_cb (GtkToggleAction *action,
                            CompEditor *editor)
 {
@@ -995,19 +938,26 @@ static GtkActionEntry core_entries[] = {
 	  N_("Click here to close the current window"),
 	  G_CALLBACK (action_close_cb) },
 
-	{ "copy",
+	{ "copy-clipboard",
 	  GTK_STOCK_COPY,
 	  NULL,
 	  NULL,
-	  N_("Copy selected text to the clipboard"),
-	  G_CALLBACK (action_copy_cb) },
+	  N_("Copy the selection"),
+	  NULL },  /* Handled by EFocusTracker */
 
-	{ "cut",
+	{ "cut-clipboard",
 	  GTK_STOCK_CUT,
 	  NULL,
 	  NULL,
-	  N_("Cut selected text to the clipboard"),
-	  G_CALLBACK (action_cut_cb) },
+	  N_("Cut the selection"),
+	  NULL },  /* Handled by EFocusTracker */
+
+	{ "delete-selection",
+	  GTK_STOCK_DELETE,
+	  NULL,
+	  NULL,
+	  N_("Delete the selection"),
+	  NULL },  /* Handled by EFocusTracker */
 
 	{ "help",
 	  GTK_STOCK_HELP,
@@ -1016,12 +966,12 @@ static GtkActionEntry core_entries[] = {
 	  N_("Click here to view help available"),
 	  G_CALLBACK (action_help_cb) },
 
-	{ "paste",
+	{ "paste-clipboard",
 	  GTK_STOCK_PASTE,
 	  NULL,
 	  NULL,
-	  N_("Paste text from the clipboard"),
-	  G_CALLBACK (action_paste_cb) },
+	  N_("Paste the clipboard"),
+	  NULL },  /* Handled by EFocusTracker */
 
 	{ "print",
 	  GTK_STOCK_PRINT,
@@ -1047,9 +997,9 @@ static GtkActionEntry core_entries[] = {
 	{ "select-all",
 	  GTK_STOCK_SELECT_ALL,
 	  NULL,
-	  NULL,
+	  "<Control>a",
 	  N_("Select all text"),
-	  G_CALLBACK (action_select_all_cb) },
+	  NULL },  /* Handled by EFocusTracker */
 
 	/* Menus */
 
@@ -1302,6 +1252,12 @@ comp_editor_get_property (GObject *object,
 				COMP_EDITOR (object)));
 			return;
 
+		case PROP_FOCUS_TRACKER:
+			g_value_set_object (
+				value, comp_editor_get_focus_tracker (
+				COMP_EDITOR (object)));
+			return;
+
 		case PROP_SHELL:
 			g_value_set_object (
 				value, comp_editor_get_shell (
@@ -1331,6 +1287,11 @@ comp_editor_dispose (GObject *object)
 		priv->shell = NULL;
 	}
 
+	if (priv->focus_tracker != NULL) {
+		g_object_unref (priv->focus_tracker);
+		priv->focus_tracker = NULL;
+	}
+
 	if (priv->window_group != NULL) {
 		g_object_unref (priv->window_group);
 		priv->window_group = NULL;
@@ -1566,6 +1527,16 @@ comp_editor_class_init (CompEditorClass *class)
 
 	g_object_class_install_property (
 		object_class,
+		PROP_FOCUS_TRACKER,
+		g_param_spec_object (
+			"focus-tracker",
+			NULL,
+			NULL,
+			E_TYPE_FOCUS_TRACKER,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
 		PROP_SHELL,
 		g_param_spec_object (
 			"shell",
@@ -1601,6 +1572,7 @@ comp_editor_init (CompEditor *editor)
 	CompEditorPrivate *priv;
 	EAttachmentView *view;
 	EAttachmentStore *store;
+	EFocusTracker *focus_tracker;
 	GdkDragAction drag_actions;
 	GtkTargetList *target_list;
 	GtkTargetEntry *targets;
@@ -1684,6 +1656,27 @@ comp_editor_init (CompEditor *editor)
 		priv->ui_manager, action_group, 0);
 	g_object_unref (action_group);
 
+	/* Configure an EFocusTracker to manage selection actions. */
+
+	focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor));
+
+	action = comp_editor_get_action (editor, "cut-clipboard");
+	e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
+
+	action = comp_editor_get_action (editor, "copy-clipboard");
+	e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
+
+	action = comp_editor_get_action (editor, "paste-clipboard");
+	e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
+
+	action = comp_editor_get_action (editor, "delete-selection");
+	e_focus_tracker_set_delete_selection_action (focus_tracker, action);
+
+	action = comp_editor_get_action (editor, "select-all");
+	e_focus_tracker_set_select_all_action (focus_tracker, action);
+
+	priv->focus_tracker = focus_tracker;
+
 	/* Fine Tuning */
 
 	action = comp_editor_get_action (editor, "attach");
@@ -2093,6 +2086,14 @@ comp_editor_get_changed (CompEditor *editor)
 	return editor->priv->changed;
 }
 
+EFocusTracker *
+comp_editor_get_focus_tracker (CompEditor *editor)
+{
+	g_return_val_if_fail (IS_COMP_EDITOR (editor), NULL);
+
+	return editor->priv->focus_tracker;
+}
+
 void
 comp_editor_set_flags (CompEditor *editor,
                        CompEditorFlags flags)
diff --git a/calendar/gui/dialogs/comp-editor.h b/calendar/gui/dialogs/comp-editor.h
index 0b3ea04..54672ec 100644
--- a/calendar/gui/dialogs/comp-editor.h
+++ b/calendar/gui/dialogs/comp-editor.h
@@ -30,6 +30,7 @@
 #include "../itip-utils.h"
 #include "comp-editor-page.h"
 #include <shell/e-shell.h>
+#include <misc/e-focus-tracker.h>
 
 /* Standard GObject macros */
 #define TYPE_COMP_EDITOR \
@@ -92,6 +93,7 @@ GType		comp_editor_get_type		(void);
 void		comp_editor_set_changed		(CompEditor *editor,
 						 gboolean changed);
 gboolean	comp_editor_get_changed		(CompEditor *editor);
+EFocusTracker *	comp_editor_get_focus_tracker	(CompEditor *editor);
 void		comp_editor_set_needs_send	(CompEditor *editor,
 						 gboolean needs_send);
 gboolean	comp_editor_get_needs_send	(CompEditor *editor);
diff --git a/calendar/gui/e-cal-list-view.h b/calendar/gui/e-cal-list-view.h
index 0874b73..faf37bb 100644
--- a/calendar/gui/e-cal-list-view.h
+++ b/calendar/gui/e-cal-list-view.h
@@ -27,6 +27,9 @@
 #include <time.h>
 #include <gtk/gtk.h>
 
+#include <table/e-table.h>
+#include <table/e-cell-date-edit.h>
+
 #include "e-calendar-view.h"
 #include "gnome-cal.h"
 
diff --git a/calendar/gui/e-calendar-table.c b/calendar/gui/e-calendar-table.c
index a1495fb..9ac534e 100644
--- a/calendar/gui/e-calendar-table.c
+++ b/calendar/gui/e-calendar-table.c
@@ -37,6 +37,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 #include <misc/e-gui-utils.h>
+#include <misc/e-selectable.h>
 #include <table/e-cell-checkbox.h>
 #include <table/e-cell-toggle.h>
 #include <table/e-cell-text.h>
@@ -248,6 +249,32 @@ calendar_table_model_cal_view_done_cb (ECalendarTable *cal_table,
 	calendar_table_emit_status_message (cal_table, NULL, -1.0);
 }
 
+/* Deletes all of the selected components in the table */
+static void
+delete_selected_components (ECalendarTable *cal_table)
+{
+	GSList *objs, *l;
+	const gchar *status_message;
+
+	objs = e_calendar_table_get_selected (cal_table);
+
+	status_message = _("Deleting selected objects");
+	calendar_table_emit_status_message (cal_table, status_message, -1.0);
+
+	for (l = objs; l; l = l->next) {
+		ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
+		GError *error = NULL;
+
+		e_cal_remove_object (comp_data->client,
+				     icalcomponent_get_uid (comp_data->icalcomp), &error);
+		delete_error_dialog (error, E_CAL_COMPONENT_TODO);
+		g_clear_error (&error);
+	}
+
+	calendar_table_emit_status_message (cal_table, NULL, -1.0);
+
+	g_slist_free (objs);
+}
 static void
 calendar_table_set_model (ECalendarTable *cal_table,
                           ECalModel *model)
@@ -846,6 +873,270 @@ calendar_table_right_click (ETable *table,
 }
 
 static void
+calendar_table_update_actions (ESelectable *selectable,
+                               EFocusTracker *focus_tracker,
+                               GdkAtom *clipboard_targets,
+                               gint n_clipboard_targets)
+{
+	ECalendarTable *cal_table;
+	GtkAction *action;
+	GSList *list, *iter;
+	gboolean sources_are_editable = TRUE;
+	gboolean clipboard_has_calendar;
+	gboolean sensitive;
+	const gchar *tooltip;
+	gint n_selected;
+
+	cal_table = E_CALENDAR_TABLE (selectable);
+	n_selected = e_table_selected_count (E_TABLE (cal_table));
+
+	list = e_calendar_table_get_selected (cal_table);
+	for (iter = list; iter != NULL; iter = iter->next) {
+		ECalModelComponent *comp_data = iter->data;
+		gboolean read_only;
+
+		e_cal_is_read_only (comp_data->client, &read_only, NULL);
+		sources_are_editable &= !read_only;
+	}
+	g_slist_free (list);
+
+	clipboard_has_calendar = (clipboard_targets != NULL) &&
+		e_targets_include_calendar (
+		clipboard_targets, n_clipboard_targets);
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0) && sources_are_editable;
+	tooltip = _("Cut selected tasks to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0);
+	tooltip = _("Copy selected tasks to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	sensitive = sources_are_editable && clipboard_has_calendar;
+	tooltip = _("Paste tasks from the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	sensitive = TRUE;
+	tooltip = _("Select all visible tasks");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+}
+
+static void
+calendar_table_cut_clipboard (ESelectable *selectable)
+{
+	ECalendarTable *cal_table;
+
+	cal_table = E_CALENDAR_TABLE (selectable);
+
+	e_selectable_copy_clipboard (selectable);
+	delete_selected_components (cal_table);
+}
+
+/* Helper for calendar_table_copy_clipboard() */
+static void
+copy_row_cb (gint model_row, gpointer data)
+{
+	ECalendarTable *cal_table;
+	ECalModelComponent *comp_data;
+	ECalModel *model;
+	gchar *comp_str;
+	icalcomponent *child;
+
+	cal_table = E_CALENDAR_TABLE (data);
+
+	g_return_if_fail (cal_table->tmp_vcal != NULL);
+
+	model = e_calendar_table_get_model (cal_table);
+	comp_data = e_cal_model_get_component_at (model, model_row);
+	if (!comp_data)
+		return;
+
+	/* Add timezones to the VCALENDAR component. */
+	e_cal_util_add_timezones_from_component (
+		cal_table->tmp_vcal, comp_data->icalcomp);
+
+	/* Add the new component to the VCALENDAR component. */
+	comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
+	child = icalparser_parse_string (comp_str);
+	if (child) {
+		icalcomponent_add_component (
+			cal_table->tmp_vcal,
+			icalcomponent_new_clone (child));
+		icalcomponent_free (child);
+	}
+	g_free (comp_str);
+}
+
+static void
+calendar_table_copy_clipboard (ESelectable *selectable)
+{
+	ECalendarTable *cal_table;
+	GtkClipboard *clipboard;
+	gchar *comp_str;
+
+	cal_table = E_CALENDAR_TABLE (selectable);
+
+	/* Create a temporary VCALENDAR object. */
+	cal_table->tmp_vcal = e_cal_util_new_top_level ();
+
+	e_table_selected_row_foreach (
+		E_TABLE (cal_table), copy_row_cb, cal_table);
+	comp_str = icalcomponent_as_ical_string_r (cal_table->tmp_vcal);
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	e_clipboard_set_calendar (clipboard, comp_str, -1);
+	gtk_clipboard_store (clipboard);
+
+	g_free (comp_str);
+
+	icalcomponent_free (cal_table->tmp_vcal);
+	cal_table->tmp_vcal = NULL;
+}
+
+/* Helper for calenable_table_paste_clipboard() */
+static void
+clipboard_get_calendar_data (ECalendarTable *cal_table,
+                             const gchar *text)
+{
+	icalcomponent *icalcomp;
+	gchar *uid;
+	ECalComponent *comp;
+	ECalModel *model;
+	ECal *client;
+	icalcomponent_kind kind;
+	const gchar *status_message;
+
+	g_return_if_fail (E_IS_CALENDAR_TABLE (cal_table));
+
+	if (!text || !*text)
+		return;
+
+	icalcomp = icalparser_parse_string (text);
+	if (!icalcomp)
+		return;
+
+	/* check the type of the component */
+	kind = icalcomponent_isa (icalcomp);
+	if (kind != ICAL_VCALENDAR_COMPONENT &&
+	    kind != ICAL_VEVENT_COMPONENT &&
+	    kind != ICAL_VTODO_COMPONENT &&
+	    kind != ICAL_VJOURNAL_COMPONENT) {
+		return;
+	}
+
+	model = e_calendar_table_get_model (cal_table);
+	client = e_cal_model_get_default_client (model);
+
+	status_message = _("Updating objects");
+	calendar_table_emit_status_message (cal_table, status_message, -1.0);
+
+	if (kind == ICAL_VCALENDAR_COMPONENT) {
+		icalcomponent_kind child_kind;
+		icalcomponent *subcomp;
+		icalcomponent *vcal_comp;
+
+		vcal_comp = icalcomp;
+		subcomp = icalcomponent_get_first_component (
+			vcal_comp, ICAL_ANY_COMPONENT);
+		while (subcomp) {
+			child_kind = icalcomponent_isa (subcomp);
+			if (child_kind == ICAL_VEVENT_COMPONENT ||
+			    child_kind == ICAL_VTODO_COMPONENT ||
+			    child_kind == ICAL_VJOURNAL_COMPONENT) {
+				ECalComponent *tmp_comp;
+
+				uid = e_cal_component_gen_uid ();
+				tmp_comp = e_cal_component_new ();
+				e_cal_component_set_icalcomponent (
+					tmp_comp,
+					icalcomponent_new_clone (subcomp));
+				e_cal_component_set_uid (tmp_comp, uid);
+				free (uid);
+
+				/* FIXME should we convert start/due/complete
+				 * times?  Also, need error handling. */
+				e_cal_create_object (
+					client, e_cal_component_get_icalcomponent (tmp_comp),
+					NULL, NULL);
+
+				g_object_unref (tmp_comp);
+			}
+			subcomp = icalcomponent_get_next_component (
+				vcal_comp, ICAL_ANY_COMPONENT);
+		}
+	} else {
+		comp = e_cal_component_new ();
+		e_cal_component_set_icalcomponent (comp, icalcomp);
+		uid = e_cal_component_gen_uid ();
+		e_cal_component_set_uid (comp, (const gchar *) uid);
+		free (uid);
+
+		e_cal_create_object (
+			client, e_cal_component_get_icalcomponent (comp),
+			NULL, NULL);
+
+		g_object_unref (comp);
+	}
+
+	calendar_table_emit_status_message (cal_table, NULL, -1.0);
+}
+
+static void
+calendar_table_paste_clipboard (ESelectable *selectable)
+{
+	ECalendarTable *cal_table;
+	GtkClipboard *clipboard;
+	GnomeCanvasItem *item;
+	GnomeCanvas *table_canvas;
+
+	cal_table = E_CALENDAR_TABLE (selectable);
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+	table_canvas = E_TABLE (cal_table)->table_canvas;
+	item = table_canvas->focused_item;
+
+	/* XXX Should ECellText implement GtkEditable? */
+
+	/* Paste text into a cell being edited. */
+	if (gtk_clipboard_wait_is_text_available (clipboard) &&
+		GTK_WIDGET_HAS_FOCUS (table_canvas) &&
+		E_IS_TABLE_ITEM (item) &&
+		E_TABLE_ITEM (item)->editing_col >= 0 &&
+		E_TABLE_ITEM (item)->editing_row >= 0) {
+
+		ETableItem *etable_item = E_TABLE_ITEM (item);
+
+		e_cell_text_paste_clipboard (
+			etable_item->cell_views[etable_item->editing_col],
+			etable_item->editing_col,
+			etable_item->editing_row);
+
+	/* Paste iCalendar data into the table. */
+	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
+		gchar *calendar_source;
+
+		calendar_source = e_clipboard_wait_for_calendar (clipboard);
+		clipboard_get_calendar_data (cal_table, calendar_source);
+		g_free (calendar_source);
+	}
+}
+
+static void
+calendar_table_select_all (ESelectable *selectable)
+{
+	e_table_select_all (E_TABLE (selectable));
+}
+
+static void
 calendar_table_class_init (ECalendarTableClass *class)
 {
 	GObjectClass *object_class;
@@ -937,6 +1228,16 @@ calendar_table_init (ECalendarTable *cal_table)
 	cal_table->priv = E_CALENDAR_TABLE_GET_PRIVATE (cal_table);
 }
 
+static void
+calendar_table_selectable_init (ESelectableInterface *interface)
+{
+	interface->update_actions = calendar_table_update_actions;
+	interface->cut_clipboard = calendar_table_cut_clipboard;
+	interface->copy_clipboard = calendar_table_copy_clipboard;
+	interface->paste_clipboard = calendar_table_paste_clipboard;
+	interface->select_all = calendar_table_select_all;
+}
+
 GType
 e_calendar_table_get_type (void)
 {
@@ -956,8 +1257,17 @@ e_calendar_table_get_type (void)
 			NULL   /* value_table */
 		};
 
+		static const GInterfaceInfo selectable_info = {
+			(GInterfaceInitFunc) calendar_table_selectable_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
 		type = g_type_register_static (
 			E_TABLE_TYPE, "ECalendarTable", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_SELECTABLE, &selectable_info);
 	}
 
 	return type;
@@ -1061,32 +1371,6 @@ add_uid_cb (gint model_row, gpointer data)
 	closure->objects = g_slist_prepend (closure->objects, comp_data);
 }
 
-/* Deletes all of the selected components in the table */
-static void
-delete_selected_components (ECalendarTable *cal_table)
-{
-	GSList *objs, *l;
-	const gchar *status_message;
-
-	objs = e_calendar_table_get_selected (cal_table);
-
-	status_message = _("Deleting selected objects");
-	calendar_table_emit_status_message (cal_table, status_message, -1.0);
-
-	for (l = objs; l; l = l->next) {
-		ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
-		GError *error = NULL;
-
-		e_cal_remove_object (comp_data->client,
-				     icalcomponent_get_uid (comp_data->icalcomp), &error);
-		delete_error_dialog (error, E_CAL_COMPONENT_TODO);
-		g_clear_error (&error);
-	}
-
-	calendar_table_emit_status_message (cal_table, NULL, -1.0);
-
-	g_slist_free (objs);
-}
 static void
 add_retract_data (ECalComponent *comp, const gchar *retract_comment)
 {
@@ -1223,210 +1507,6 @@ e_calendar_table_get_selected (ECalendarTable *cal_table)
 	return closure.objects;
 }
 
-/**
- * e_calendar_table_cut_clipboard:
- * @cal_table: A calendar table.
- *
- * Cuts selected tasks in the given calendar table
- */
-void
-e_calendar_table_cut_clipboard (ECalendarTable *cal_table)
-{
-	g_return_if_fail (E_IS_CALENDAR_TABLE (cal_table));
-
-	e_calendar_table_copy_clipboard (cal_table);
-	delete_selected_components (cal_table);
-}
-
-/* callback for e_table_selected_row_foreach */
-static void
-copy_row_cb (gint model_row, gpointer data)
-{
-	ECalendarTable *cal_table;
-	ECalModelComponent *comp_data;
-	ECalModel *model;
-	gchar *comp_str;
-	icalcomponent *child;
-
-	cal_table = E_CALENDAR_TABLE (data);
-
-	g_return_if_fail (cal_table->tmp_vcal != NULL);
-
-	model = e_calendar_table_get_model (cal_table);
-	comp_data = e_cal_model_get_component_at (model, model_row);
-	if (!comp_data)
-		return;
-
-	/* add timezones to the VCALENDAR component */
-	e_cal_util_add_timezones_from_component (cal_table->tmp_vcal, comp_data->icalcomp);
-
-	/* add the new component to the VCALENDAR component */
-	comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
-	child = icalparser_parse_string (comp_str);
-	if (child) {
-		icalcomponent_add_component (cal_table->tmp_vcal,
-					     icalcomponent_new_clone (child));
-		icalcomponent_free (child);
-	}
-	g_free (comp_str);
-}
-
-/**
- * e_calendar_table_copy_clipboard:
- * @cal_table: A calendar table.
- *
- * Copies selected tasks into the clipboard
- */
-void
-e_calendar_table_copy_clipboard (ECalendarTable *cal_table)
-{
-	GtkClipboard *clipboard;
-	gchar *comp_str;
-
-	g_return_if_fail (E_IS_CALENDAR_TABLE (cal_table));
-
-	/* create temporary VCALENDAR object */
-	cal_table->tmp_vcal = e_cal_util_new_top_level ();
-
-	e_table_selected_row_foreach (
-		E_TABLE (cal_table), copy_row_cb, cal_table);
-	comp_str = icalcomponent_as_ical_string_r (cal_table->tmp_vcal);
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	e_clipboard_set_calendar (clipboard, comp_str, -1);
-	gtk_clipboard_store (clipboard);
-
-	/* free memory */
-	icalcomponent_free (cal_table->tmp_vcal);
-	g_free (comp_str);
-	cal_table->tmp_vcal = NULL;
-}
-
-static void
-clipboard_get_calendar_data (ECalendarTable *cal_table, const gchar *text)
-{
-	icalcomponent *icalcomp;
-	gchar *uid;
-	ECalComponent *comp;
-	ECalModel *model;
-	ECal *client;
-	icalcomponent_kind kind;
-	const gchar *status_message;
-
-	g_return_if_fail (E_IS_CALENDAR_TABLE (cal_table));
-
-	if (!text || !*text)
-		return;
-
-	icalcomp = icalparser_parse_string (text);
-	if (!icalcomp)
-		return;
-
-	/* check the type of the component */
-	kind = icalcomponent_isa (icalcomp);
-	if (kind != ICAL_VCALENDAR_COMPONENT &&
-	    kind != ICAL_VEVENT_COMPONENT &&
-	    kind != ICAL_VTODO_COMPONENT &&
-	    kind != ICAL_VJOURNAL_COMPONENT) {
-		return;
-	}
-
-	model = e_calendar_table_get_model (cal_table);
-	client = e_cal_model_get_default_client (model);
-
-	status_message = _("Updating objects");
-	calendar_table_emit_status_message (cal_table, status_message, -1.0);
-
-	if (kind == ICAL_VCALENDAR_COMPONENT) {
-		icalcomponent_kind child_kind;
-		icalcomponent *subcomp;
-		icalcomponent *vcal_comp;
-
-		vcal_comp = icalcomp;
-		subcomp = icalcomponent_get_first_component (
-			vcal_comp, ICAL_ANY_COMPONENT);
-		while (subcomp) {
-			child_kind = icalcomponent_isa (subcomp);
-			if (child_kind == ICAL_VEVENT_COMPONENT ||
-			    child_kind == ICAL_VTODO_COMPONENT ||
-			    child_kind == ICAL_VJOURNAL_COMPONENT) {
-				ECalComponent *tmp_comp;
-
-				uid = e_cal_component_gen_uid ();
-				tmp_comp = e_cal_component_new ();
-				e_cal_component_set_icalcomponent (
-					tmp_comp, icalcomponent_new_clone (subcomp));
-				e_cal_component_set_uid (tmp_comp, uid);
-				free (uid);
-
-				/* FIXME should we convert start/due/complete times? */
-				/* FIXME Error handling */
-				e_cal_create_object (client, e_cal_component_get_icalcomponent (tmp_comp), NULL, NULL);
-
-				g_object_unref (tmp_comp);
-			}
-			subcomp = icalcomponent_get_next_component (
-				vcal_comp, ICAL_ANY_COMPONENT);
-		}
-	} else {
-		comp = e_cal_component_new ();
-		e_cal_component_set_icalcomponent (comp, icalcomp);
-		uid = e_cal_component_gen_uid ();
-		e_cal_component_set_uid (comp, (const gchar *) uid);
-		free (uid);
-
-		e_cal_create_object (client, e_cal_component_get_icalcomponent (comp), NULL, NULL);
-
-		g_object_unref (comp);
-	}
-
-	calendar_table_emit_status_message (cal_table, NULL, -1.0);
-}
-
-/**
- * e_calendar_table_paste_clipboard:
- * @cal_table: A calendar table.
- *
- * Pastes tasks currently in the clipboard into the given calendar table
- */
-void
-e_calendar_table_paste_clipboard (ECalendarTable *cal_table)
-{
-	GtkClipboard *clipboard;
-	GnomeCanvasItem *item;
-	GnomeCanvas *table_canvas;
-
-	g_return_if_fail (E_IS_CALENDAR_TABLE (cal_table));
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-	table_canvas = E_TABLE (cal_table)->table_canvas;
-	item = table_canvas->focused_item;
-
-	/* Paste text into a cell being edited. */
-	if (gtk_clipboard_wait_is_text_available (clipboard) &&
-		GTK_WIDGET_HAS_FOCUS (table_canvas) &&
-		E_IS_TABLE_ITEM (item) &&
-		E_TABLE_ITEM (item)->editing_col >= 0 &&
-		E_TABLE_ITEM (item)->editing_row >= 0) {
-
-		ETableItem *etable_item = E_TABLE_ITEM (item);
-
-		e_cell_text_paste_clipboard (
-			etable_item->cell_views[etable_item->editing_col],
-			etable_item->editing_col,
-			etable_item->editing_row);
-
-	/* Paste iCalendar data into the table. */
-	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
-		gchar *calendar_source;
-
-		calendar_source = e_clipboard_wait_for_calendar (clipboard);
-		clipboard_get_calendar_data (cal_table, calendar_source);
-		g_free (calendar_source);
-	}
-}
-
 static void
 hide_completed_rows (ECalModel *model, GList *clients_list, gchar *hide_sexp, GPtrArray *comp_objects)
 {
diff --git a/calendar/gui/e-calendar-table.h b/calendar/gui/e-calendar-table.h
index aa98aee..71c7b71 100644
--- a/calendar/gui/e-calendar-table.h
+++ b/calendar/gui/e-calendar-table.h
@@ -92,11 +92,6 @@ EShellView *	e_calendar_table_get_shell_view	(ECalendarTable *cal_table);
 void		e_calendar_table_delete_selected(ECalendarTable *cal_table);
 GSList *	e_calendar_table_get_selected	(ECalendarTable *cal_table);
 
-/* Clipboard related functions */
-void		e_calendar_table_cut_clipboard	(ECalendarTable *cal_table);
-void		e_calendar_table_copy_clipboard	(ECalendarTable *cal_table);
-void		e_calendar_table_paste_clipboard(ECalendarTable *cal_table);
-
 ECalModelComponent *
 		e_calendar_table_get_selected_comp
 						(ECalendarTable *cal_table);
diff --git a/calendar/gui/e-calendar-view.c b/calendar/gui/e-calendar-view.c
index f23088a..5c04c78 100644
--- a/calendar/gui/e-calendar-view.c
+++ b/calendar/gui/e-calendar-view.c
@@ -39,6 +39,7 @@
 #include <e-util/e-icon-factory.h>
 #include <libecal/e-cal-time-util.h>
 #include <libecal/e-cal-component.h>
+#include <misc/e-selectable.h>
 #include <shell/e-shell.h>
 
 #include "common/authentication.h"
@@ -94,10 +95,9 @@ enum {
 	LAST_SIGNAL
 };
 
+static gpointer parent_class;
 static guint signals[LAST_SIGNAL];
 
-G_DEFINE_TYPE (ECalendarView, e_calendar_view, GTK_TYPE_TABLE)
-
 static void
 calendar_view_set_model (ECalendarView *calendar_view,
                          ECalModel *model)
@@ -158,7 +158,7 @@ calendar_view_dispose (GObject *object)
 	}
 
 	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (e_calendar_view_parent_class)->dispose (object);
+	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
@@ -171,11 +171,352 @@ calendar_view_finalize (GObject *object)
 	g_free (priv->default_category);
 
 	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (e_calendar_view_parent_class)->finalize (object);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+calendar_view_update_actions (ESelectable *selectable,
+                              EFocusTracker *focus_tracker,
+                              GdkAtom *clipboard_targets,
+                              gint n_clipboard_targets)
+{
+	ECalendarView *view;
+	GtkAction *action;
+	GList *list, *iter;
+	gboolean sources_are_editable = TRUE;
+	gboolean clipboard_has_calendar;
+	gboolean sensitive;
+	const gchar *tooltip;
+	gint n_selected;
+
+	view = E_CALENDAR_VIEW (selectable);
+
+	list = e_calendar_view_get_selected_events (view);
+	n_selected = g_list_length (list);
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		ECalendarViewEvent *event = iter->data;
+		gboolean read_only;
+
+		if (event == NULL || event->comp_data == NULL)
+			continue;
+
+		e_cal_is_read_only (event->comp_data->client, &read_only, NULL);
+		sources_are_editable &= !read_only;
+	}
+
+	g_list_free (list);
+
+	clipboard_has_calendar = (clipboard_targets != NULL) &&
+		e_targets_include_calendar (
+		clipboard_targets, n_clipboard_targets);
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0) && sources_are_editable;
+	tooltip = _("Cut selected events to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0);
+	tooltip = _("Copy selected events to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	sensitive = sources_are_editable && clipboard_has_calendar;
+	tooltip = _("Paste events from the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+}
+
+static void
+calendar_view_cut_clipboard (ESelectable *selectable)
+{
+	ECalendarView *cal_view;
+	GList *selected, *l;
+	const gchar *uid;
+
+	cal_view = E_CALENDAR_VIEW (selectable);
+
+	selected = e_calendar_view_get_selected_events (cal_view);
+	if (!selected)
+		return;
+
+#if 0  /* KILL-BONOBO */
+	e_calendar_view_set_status_message (cal_view, _("Deleting selected objects"), -1);
+#endif
+
+	e_selectable_copy_clipboard (selectable);
+
+	for (l = selected; l != NULL; l = l->next) {
+		ECalComponent *comp;
+		ECalendarViewEvent *event = (ECalendarViewEvent *) l->data;
+		GError *error = NULL;
+
+		if (!event)
+			continue;
+
+		comp = e_cal_component_new ();
+		e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
+
+		if ((itip_organizer_is_user (comp, event->comp_data->client) || itip_sentby_is_user (comp, event->comp_data->client))
+		    && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
+						event->comp_data->client, comp, TRUE))
+			itip_send_comp (E_CAL_COMPONENT_METHOD_CANCEL, comp,
+					event->comp_data->client, NULL, NULL, NULL, TRUE, FALSE);
+
+		e_cal_component_get_uid (comp, &uid);
+		if (e_cal_component_is_instance (comp)) {
+			gchar *rid = NULL;
+			icalcomponent *icalcomp;
+
+			/* when cutting detached instances, only cut that instance */
+			rid = e_cal_component_get_recurid_as_string (comp);
+			if (e_cal_get_object (event->comp_data->client, uid, rid, &icalcomp, NULL)) {
+				e_cal_remove_object_with_mod (event->comp_data->client, uid,
+							      rid, CALOBJ_MOD_THIS,
+							      &error);
+				icalcomponent_free (icalcomp);
+			} else
+				e_cal_remove_object_with_mod (event->comp_data->client, uid, NULL,
+						CALOBJ_MOD_ALL, &error);
+			g_free (rid);
+		} else
+			e_cal_remove_object (event->comp_data->client, uid, &error);
+		delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
+
+		g_clear_error (&error);
+
+		g_object_unref (comp);
+	}
+
+#if 0  /* KILL-BONOBO */
+	e_calendar_view_set_status_message (cal_view, NULL, -1);
+#endif
+
+	g_list_free (selected);
+}
+
+static void
+add_related_timezones (icalcomponent *des_icalcomp, icalcomponent *src_icalcomp, ECal *client)
+{
+	icalproperty_kind look_in[] = {
+		ICAL_DTSTART_PROPERTY,
+		ICAL_DTEND_PROPERTY,
+		ICAL_NO_PROPERTY
+	};
+	gint i;
+
+	g_return_if_fail (des_icalcomp != NULL);
+	g_return_if_fail (src_icalcomp != NULL);
+	g_return_if_fail (client != NULL);
+
+	for (i = 0; look_in[i] != ICAL_NO_PROPERTY; i++) {
+		icalproperty *prop = icalcomponent_get_first_property (src_icalcomp, look_in[i]);
+
+		if (prop) {
+			icalparameter *par = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+
+			if (par) {
+				const gchar *tzid = icalparameter_get_tzid (par);
+
+				if (tzid) {
+					GError *error = NULL;
+					icaltimezone *zone = NULL;
+
+					if (!e_cal_get_timezone (client, tzid, &zone, &error)) {
+						g_warning ("%s: Cannot get timezone for '%s'. %s", G_STRFUNC, tzid, error ? error->message : "");
+						if (error)
+							g_error_free (error);
+					} else if (zone &&
+						icalcomponent_get_timezone (des_icalcomp, icaltimezone_get_tzid (zone)) == NULL) {
+						/* do not duplicate timezones in the component */
+						icalcomponent *vtz_comp;
+
+						vtz_comp = icaltimezone_get_component (zone);
+						if (vtz_comp)
+							icalcomponent_add_component (des_icalcomp, icalcomponent_new_clone (vtz_comp));
+					}
+				}
+			}
+		}
+	}
+}
+
+static void
+calendar_view_copy_clipboard (ESelectable *selectable)
+{
+	ECalendarView *cal_view;
+	GList *selected, *l;
+	gchar *comp_str;
+	icalcomponent *vcal_comp;
+	icalcomponent *new_icalcomp;
+	ECalendarViewEvent *event;
+	GtkClipboard *clipboard;
+
+	cal_view = E_CALENDAR_VIEW (selectable);
+
+	selected = e_calendar_view_get_selected_events (cal_view);
+	if (!selected)
+		return;
+
+	/* create top-level VCALENDAR component and add VTIMEZONE's */
+	vcal_comp = e_cal_util_new_top_level ();
+	for (l = selected; l != NULL; l = l->next) {
+		event = (ECalendarViewEvent *) l->data;
+
+		if (event) {
+			e_cal_util_add_timezones_from_component (vcal_comp, event->comp_data->icalcomp);
+
+			add_related_timezones (vcal_comp, event->comp_data->icalcomp, event->comp_data->client);
+		}
+	}
+
+	for (l = selected; l != NULL; l = l->next) {
+		event = (ECalendarViewEvent *) l->data;
+
+		new_icalcomp = icalcomponent_new_clone (event->comp_data->icalcomp);
+
+		/* remove RECURRENCE-IDs from copied objects */
+		if (e_cal_util_component_is_instance (new_icalcomp)) {
+			icalproperty *prop;
+
+			prop = icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY);
+			if (prop)
+				icalcomponent_remove_property (new_icalcomp, prop);
+		}
+		icalcomponent_add_component (vcal_comp, new_icalcomp);
+	}
+
+	comp_str = icalcomponent_as_ical_string_r (vcal_comp);
+
+	/* copy the VCALENDAR to the clipboard */
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	e_clipboard_set_calendar (clipboard, comp_str, -1);
+	gtk_clipboard_store (clipboard);
+
+	/* free memory */
+	icalcomponent_free (vcal_comp);
+	g_free (comp_str);
+	g_list_free (selected);
+}
+
+static void
+clipboard_get_calendar_data (ECalendarView *cal_view,
+                             const gchar *text)
+{
+	icalcomponent *icalcomp;
+	icalcomponent_kind kind;
+	time_t selected_time_start, selected_time_end;
+	icaltimezone *default_zone;
+	ECal *client;
+	gboolean in_top_canvas;
+
+	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
+
+	if (!text || !*text)
+		return;
+
+	icalcomp = icalparser_parse_string ((const gchar *) text);
+	if (!icalcomp)
+		return;
+
+	default_zone = calendar_config_get_icaltimezone ();
+	client = e_cal_model_get_default_client (cal_view->priv->model);
+
+	/* check the type of the component */
+	/* FIXME An error dialog if we return? */
+	kind = icalcomponent_isa (icalcomp);
+	if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT)
+		return;
+
+#if 0  /* KILL-BONOBO */
+	e_calendar_view_set_status_message (cal_view, _("Updating objects"), -1);
+#endif
+	e_calendar_view_get_selected_time_range (cal_view, &selected_time_start, &selected_time_end);
+
+	if ((selected_time_end - selected_time_start) == 60 * 60 * 24)
+		in_top_canvas = TRUE;
+	else
+		in_top_canvas = FALSE;
+
+	if (kind == ICAL_VCALENDAR_COMPONENT) {
+		icalcomponent *subcomp;
+
+		/* add timezones first, to have them ready */
+		for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VTIMEZONE_COMPONENT);
+		     subcomp;
+		     subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VTIMEZONE_COMPONENT)) {
+			icaltimezone *zone;
+			GError *error = NULL;
+
+			zone = icaltimezone_new ();
+			icaltimezone_set_component (zone, subcomp);
+			if (!e_cal_add_timezone (client, zone, &error)) {
+				icalproperty *tzidprop = icalcomponent_get_first_property (subcomp, ICAL_TZID_PROPERTY);
+
+				g_warning ("%s: Add zone '%s' failed. %s", G_STRFUNC, tzidprop ? icalproperty_get_tzid (tzidprop) : "???", error ? error->message : "");
+				if (error)
+					g_error_free (error);
+			}
+
+			icaltimezone_free (zone, 1);
+		}
+
+		for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VEVENT_COMPONENT);
+		     subcomp;
+		     subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VEVENT_COMPONENT)) {
+			if (e_cal_util_component_has_recurrences (subcomp)) {
+				icalproperty *icalprop = icalcomponent_get_first_property (subcomp, ICAL_RRULE_PROPERTY);
+				if (icalprop)
+					icalproperty_remove_parameter_by_name (icalprop, "X-EVOLUTION-ENDDATE");
+			}
+
+			e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, subcomp, in_top_canvas);
+		}
+
+		icalcomponent_free (icalcomp);
+	} else {
+		e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, icalcomp, in_top_canvas);
+	}
+
+#if 0  /* KILL-BONOBO */
+	e_calendar_view_set_status_message (cal_view, NULL, -1);
+#endif
+}
+
+static void
+calendar_view_paste_clipboard (ESelectable *selectable)
+{
+	ECalendarView *cal_view;
+	GtkClipboard *clipboard;
+
+	cal_view = E_CALENDAR_VIEW (selectable);
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+	/* Paste text into an event being edited. */
+	if (gtk_clipboard_wait_is_text_available (clipboard)) {
+		ECalendarViewClass *class;
+
+		class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
+		g_return_if_fail (class->paste_text != NULL);
+
+		class->paste_text (cal_view);
+
+	/* Paste iCalendar data into the view. */
+	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
+		gchar *calendar_source;
+
+		calendar_source = e_clipboard_wait_for_calendar (clipboard);
+		clipboard_get_calendar_data (cal_view, calendar_source);
+		g_free (calendar_source);
+	}
 }
 
 static void
-e_calendar_view_class_init (ECalendarViewClass *class)
+calendar_view_class_init (ECalendarViewClass *class)
 {
 	GObjectClass *object_class;
 	GtkBindingSet *binding_set;
@@ -290,9 +631,7 @@ e_calendar_view_class_init (ECalendarViewClass *class)
 		g_cclosure_marshal_VOID__VOID,
 		G_TYPE_NONE, 0);
 
-        /*
-         * Key bindings
-         */
+	/* Key bindings */
 
 	binding_set = gtk_binding_set_by_class (class);
 
@@ -304,11 +643,56 @@ e_calendar_view_class_init (ECalendarViewClass *class)
 }
 
 static void
-e_calendar_view_init (ECalendarView *calendar_view)
+calendar_view_init (ECalendarView *calendar_view)
 {
 	calendar_view->priv = E_CALENDAR_VIEW_GET_PRIVATE (calendar_view);
 }
 
+static void
+calendar_view_selectable_init (ESelectableInterface *interface)
+{
+	interface->update_actions = calendar_view_update_actions;
+	interface->cut_clipboard = calendar_view_cut_clipboard;
+	interface->copy_clipboard = calendar_view_copy_clipboard;
+	interface->paste_clipboard = calendar_view_paste_clipboard;
+}
+
+GType
+e_calendar_view_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (ECalendarViewClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) calendar_view_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (ECalendarView),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) calendar_view_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo selectable_info = {
+			(GInterfaceInitFunc) calendar_view_selectable_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_TABLE, "ECalendarView", &type_info,
+			G_TYPE_FLAG_ABSTRACT);
+
+		g_type_add_interface_static (
+			type, E_TYPE_SELECTABLE, &selectable_info);
+	}
+
+	return type;
+}
+
 void
 e_calendar_view_popup_event (ECalendarView *calendar_view,
                              GdkEventButton *event)
@@ -586,286 +970,6 @@ e_calendar_view_update_query (ECalendarView *cal_view)
 	class->update_query (cal_view);
 }
 
-void
-e_calendar_view_cut_clipboard (ECalendarView *cal_view)
-{
-	GList *selected, *l;
-	const gchar *uid;
-
-	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
-
-	selected = e_calendar_view_get_selected_events (cal_view);
-	if (!selected)
-		return;
-
-#if 0  /* KILL-BONOBO */
-	e_calendar_view_set_status_message (cal_view, _("Deleting selected objects"), -1);
-#endif
-
-	e_calendar_view_copy_clipboard (cal_view);
-	for (l = selected; l != NULL; l = l->next) {
-		ECalComponent *comp;
-		ECalendarViewEvent *event = (ECalendarViewEvent *) l->data;
-		GError *error = NULL;
-
-		if (!event)
-			continue;
-
-		comp = e_cal_component_new ();
-		e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
-
-		if ((itip_organizer_is_user (comp, event->comp_data->client) || itip_sentby_is_user (comp, event->comp_data->client))
-		    && cancel_component_dialog ((GtkWindow *) gtk_widget_get_toplevel (GTK_WIDGET (cal_view)),
-						event->comp_data->client, comp, TRUE))
-			itip_send_comp (E_CAL_COMPONENT_METHOD_CANCEL, comp,
-					event->comp_data->client, NULL, NULL, NULL, TRUE, FALSE);
-
-		e_cal_component_get_uid (comp, &uid);
-		if (e_cal_component_is_instance (comp)) {
-			gchar *rid = NULL;
-			icalcomponent *icalcomp;
-
-			/* when cutting detached instances, only cut that instance */
-			rid = e_cal_component_get_recurid_as_string (comp);
-			if (e_cal_get_object (event->comp_data->client, uid, rid, &icalcomp, NULL)) {
-				e_cal_remove_object_with_mod (event->comp_data->client, uid,
-							      rid, CALOBJ_MOD_THIS,
-							      &error);
-				icalcomponent_free (icalcomp);
-			} else
-				e_cal_remove_object_with_mod (event->comp_data->client, uid, NULL,
-						CALOBJ_MOD_ALL, &error);
-			g_free (rid);
-		} else
-			e_cal_remove_object (event->comp_data->client, uid, &error);
-		delete_error_dialog (error, E_CAL_COMPONENT_EVENT);
-
-		g_clear_error (&error);
-
-		g_object_unref (comp);
-	}
-
-#if 0  /* KILL-BONOBO */
-	e_calendar_view_set_status_message (cal_view, NULL, -1);
-#endif
-
-	g_list_free (selected);
-}
-
-static void
-add_related_timezones (icalcomponent *des_icalcomp, icalcomponent *src_icalcomp, ECal *client)
-{
-	icalproperty_kind look_in[] = {
-		ICAL_DTSTART_PROPERTY,
-		ICAL_DTEND_PROPERTY,
-		ICAL_NO_PROPERTY
-	};
-	gint i;
-
-	g_return_if_fail (des_icalcomp != NULL);
-	g_return_if_fail (src_icalcomp != NULL);
-	g_return_if_fail (client != NULL);
-
-	for (i = 0; look_in[i] != ICAL_NO_PROPERTY; i++) {
-		icalproperty *prop = icalcomponent_get_first_property (src_icalcomp, look_in[i]);
-
-		if (prop) {
-			icalparameter *par = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
-
-			if (par) {
-				const gchar *tzid = icalparameter_get_tzid (par);
-
-				if (tzid) {
-					GError *error = NULL;
-					icaltimezone *zone = NULL;
-
-					if (!e_cal_get_timezone (client, tzid, &zone, &error)) {
-						g_warning ("%s: Cannot get timezone for '%s'. %s", G_STRFUNC, tzid, error ? error->message : "");
-						if (error)
-							g_error_free (error);
-					} else if (zone &&
-						icalcomponent_get_timezone (des_icalcomp, icaltimezone_get_tzid (zone)) == NULL) {
-						/* do not duplicate timezones in the component */
-						icalcomponent *vtz_comp;
-
-						vtz_comp = icaltimezone_get_component (zone);
-						if (vtz_comp)
-							icalcomponent_add_component (des_icalcomp, icalcomponent_new_clone (vtz_comp));
-					}
-				}
-			}
-		}
-	}
-}
-
-void
-e_calendar_view_copy_clipboard (ECalendarView *cal_view)
-{
-	GList *selected, *l;
-	gchar *comp_str;
-	icalcomponent *vcal_comp;
-	icalcomponent *new_icalcomp;
-	ECalendarViewEvent *event;
-	GtkClipboard *clipboard;
-
-	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
-
-	selected = e_calendar_view_get_selected_events (cal_view);
-	if (!selected)
-		return;
-
-	/* create top-level VCALENDAR component and add VTIMEZONE's */
-	vcal_comp = e_cal_util_new_top_level ();
-	for (l = selected; l != NULL; l = l->next) {
-		event = (ECalendarViewEvent *) l->data;
-
-		if (event) {
-			e_cal_util_add_timezones_from_component (vcal_comp, event->comp_data->icalcomp);
-
-			add_related_timezones (vcal_comp, event->comp_data->icalcomp, event->comp_data->client);
-		}
-	}
-
-	for (l = selected; l != NULL; l = l->next) {
-		event = (ECalendarViewEvent *) l->data;
-
-		new_icalcomp = icalcomponent_new_clone (event->comp_data->icalcomp);
-
-		/* remove RECURRENCE-IDs from copied objects */
-		if (e_cal_util_component_is_instance (new_icalcomp)) {
-			icalproperty *prop;
-
-			prop = icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY);
-			if (prop)
-				icalcomponent_remove_property (new_icalcomp, prop);
-		}
-		icalcomponent_add_component (vcal_comp, new_icalcomp);
-	}
-
-	comp_str = icalcomponent_as_ical_string_r (vcal_comp);
-
-	/* copy the VCALENDAR to the clipboard */
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	e_clipboard_set_calendar (clipboard, comp_str, -1);
-	gtk_clipboard_store (clipboard);
-
-	/* free memory */
-	icalcomponent_free (vcal_comp);
-	g_free (comp_str);
-	g_list_free (selected);
-}
-
-static void
-clipboard_get_calendar_data (ECalendarView *cal_view, const gchar *text)
-{
-	icalcomponent *icalcomp;
-	icalcomponent_kind kind;
-	time_t selected_time_start, selected_time_end;
-	icaltimezone *default_zone;
-	ECal *client;
-	gboolean in_top_canvas;
-
-	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
-
-	if (!text || !*text)
-		return;
-
-	icalcomp = icalparser_parse_string ((const gchar *) text);
-	if (!icalcomp)
-		return;
-
-	default_zone = calendar_config_get_icaltimezone ();
-	client = e_cal_model_get_default_client (cal_view->priv->model);
-
-	/* check the type of the component */
-	/* FIXME An error dialog if we return? */
-	kind = icalcomponent_isa (icalcomp);
-	if (kind != ICAL_VCALENDAR_COMPONENT && kind != ICAL_VEVENT_COMPONENT)
-		return;
-
-#if 0  /* KILL-BONOBO */
-	e_calendar_view_set_status_message (cal_view, _("Updating objects"), -1);
-#endif
-	e_calendar_view_get_selected_time_range (cal_view, &selected_time_start, &selected_time_end);
-
-	if ((selected_time_end - selected_time_start) == 60 * 60 * 24)
-		in_top_canvas = TRUE;
-	else
-		in_top_canvas = FALSE;
-
-	if (kind == ICAL_VCALENDAR_COMPONENT) {
-		icalcomponent *subcomp;
-
-		/* add timezones first, to have them ready */
-		for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VTIMEZONE_COMPONENT);
-		     subcomp;
-		     subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VTIMEZONE_COMPONENT)) {
-			icaltimezone *zone;
-			GError *error = NULL;
-
-			zone = icaltimezone_new ();
-			icaltimezone_set_component (zone, subcomp);
-			if (!e_cal_add_timezone (client, zone, &error)) {
-				icalproperty *tzidprop = icalcomponent_get_first_property (subcomp, ICAL_TZID_PROPERTY);
-
-				g_warning ("%s: Add zone '%s' failed. %s", G_STRFUNC, tzidprop ? icalproperty_get_tzid (tzidprop) : "???", error ? error->message : "");
-				if (error)
-					g_error_free (error);
-			}
-
-			icaltimezone_free (zone, 1);
-		}
-
-		for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VEVENT_COMPONENT);
-		     subcomp;
-		     subcomp = icalcomponent_get_next_component (icalcomp, ICAL_VEVENT_COMPONENT)) {
-			if (e_cal_util_component_has_recurrences (subcomp)) {
-				icalproperty *icalprop = icalcomponent_get_first_property (subcomp, ICAL_RRULE_PROPERTY);
-				if (icalprop)
-					icalproperty_remove_parameter_by_name (icalprop, "X-EVOLUTION-ENDDATE");
-			}
-
-			e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, subcomp, in_top_canvas);
-		}
-
-		icalcomponent_free (icalcomp);
-	} else {
-		e_calendar_view_add_event (cal_view, client, selected_time_start, default_zone, icalcomp, in_top_canvas);
-	}
-
-#if 0  /* KILL-BONOBO */
-	e_calendar_view_set_status_message (cal_view, NULL, -1);
-#endif
-}
-
-void
-e_calendar_view_paste_clipboard (ECalendarView *cal_view)
-{
-	GtkClipboard *clipboard;
-
-	g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view));
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-	/* Paste text into an event being edited. */
-	if (gtk_clipboard_wait_is_text_available (clipboard)) {
-		ECalendarViewClass *class;
-
-		class = E_CALENDAR_VIEW_GET_CLASS (cal_view);
-		g_return_if_fail (class->paste_text != NULL);
-
-		class->paste_text (cal_view);
-
-	/* Paste iCalendar data into the view. */
-	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
-		gchar *calendar_source;
-
-		calendar_source = e_clipboard_wait_for_calendar (clipboard);
-		clipboard_get_calendar_data (cal_view, calendar_source);
-		g_free (calendar_source);
-	}
-}
-
 static void
 add_retract_data (ECalComponent *comp, const gchar *retract_comment, CalObjModType mod)
 {
diff --git a/calendar/gui/e-calendar-view.h b/calendar/gui/e-calendar-view.h
index 7ae643a..a7ed224 100644
--- a/calendar/gui/e-calendar-view.h
+++ b/calendar/gui/e-calendar-view.h
@@ -174,9 +174,6 @@ gboolean	e_calendar_view_get_visible_time_range
 						 time_t *end_time);
 void		e_calendar_view_update_query	(ECalendarView *cal_view);
 
-void		e_calendar_view_cut_clipboard	(ECalendarView *cal_view);
-void		e_calendar_view_copy_clipboard	(ECalendarView *cal_view);
-void		e_calendar_view_paste_clipboard	(ECalendarView *cal_view);
 void		e_calendar_view_delete_selected_event
 						(ECalendarView *cal_view);
 void		e_calendar_view_delete_selected_events
diff --git a/calendar/gui/e-day-view-main-item.c b/calendar/gui/e-day-view-main-item.c
index 6b34018..16be4ff 100644
--- a/calendar/gui/e-day-view-main-item.c
+++ b/calendar/gui/e-day-view-main-item.c
@@ -33,6 +33,7 @@
 #include <e-calendar-view.h>
 
 #include "e-util/e-categories-config.h"
+#include "e-util/e-util.h"
 #include "e-day-view-layout.h"
 #include "e-day-view-main-item.h"
 #include "ea-calendar.h"
diff --git a/calendar/gui/e-memo-table.c b/calendar/gui/e-memo-table.c
index 9777158..4df17c5 100644
--- a/calendar/gui/e-memo-table.c
+++ b/calendar/gui/e-memo-table.c
@@ -36,7 +36,8 @@
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <e-util/e-binding.h>
-#include <widgets/misc/e-gui-utils.h>
+#include <misc/e-gui-utils.h>
+#include <misc/e-selectable.h>
 #include <table/e-cell-checkbox.h>
 #include <table/e-cell-toggle.h>
 #include <table/e-cell-text.h>
@@ -181,6 +182,33 @@ memo_table_model_cal_view_done_cb (EMemoTable *memo_table,
 	memo_table_emit_status_message (memo_table, NULL, -1.0);
 }
 
+/* Deletes all of the selected components in the table */
+static void
+delete_selected_components (EMemoTable *memo_table)
+{
+	GSList *objs, *l;
+	const gchar *status_message;
+
+	objs = e_memo_table_get_selected (memo_table);
+
+	status_message = _("Deleting selected objects");
+	memo_table_emit_status_message (memo_table, status_message, -1.0);
+
+	for (l = objs; l; l = l->next) {
+		ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
+		GError *error = NULL;
+
+		e_cal_remove_object (comp_data->client,
+				     icalcomponent_get_uid (comp_data->icalcomp), &error);
+		delete_error_dialog (error, E_CAL_COMPONENT_JOURNAL);
+		g_clear_error (&error);
+	}
+
+	memo_table_emit_status_message (memo_table, NULL, -1.0);
+
+	g_slist_free (objs);
+}
+
 static void
 memo_table_set_model (EMemoTable *memo_table,
                       ECalModel *model)
@@ -629,6 +657,270 @@ memo_table_right_click (ETable *table,
 }
 
 static void
+memo_table_update_actions (ESelectable *selectable,
+                           EFocusTracker *focus_tracker,
+                           GdkAtom *clipboard_targets,
+                           gint n_clipboard_targets)
+{
+	EMemoTable *memo_table;
+	GtkAction *action;
+	GSList *list, *iter;
+	gboolean sources_are_editable = TRUE;
+	gboolean clipboard_has_calendar;
+	gboolean sensitive;
+	const gchar *tooltip;
+	gint n_selected;
+
+	memo_table = E_MEMO_TABLE (selectable);
+	n_selected = e_table_selected_count (E_TABLE (memo_table));
+
+	list = e_memo_table_get_selected (memo_table);
+	for (iter = list; iter != NULL; iter = iter->next) {
+		ECalModelComponent *comp_data = iter->data;
+		gboolean read_only;
+
+		e_cal_is_read_only (comp_data->client, &read_only, NULL);
+		sources_are_editable &= !read_only;
+	}
+	g_slist_free (list);
+
+	clipboard_has_calendar = (clipboard_targets != NULL) &&
+		e_targets_include_calendar (
+		clipboard_targets, n_clipboard_targets);
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0) && sources_are_editable;
+	tooltip = _("Cut selected memos to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	sensitive = (n_selected > 0);
+	tooltip = _("Copy selected memos to the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	sensitive = sources_are_editable && clipboard_has_calendar;
+	tooltip = _("Paste memos from the clipboard");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	sensitive = TRUE;
+	tooltip = _("Select all visible memos");
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+}
+
+static void
+memo_table_cut_clipboard (ESelectable *selectable)
+{
+	EMemoTable *memo_table;
+
+	memo_table = E_MEMO_TABLE (selectable);
+
+	e_selectable_copy_clipboard (selectable);
+	delete_selected_components (memo_table);
+}
+
+/* Helper for memo_table_copy_clipboard() */
+static void
+copy_row_cb (gint model_row, gpointer data)
+{
+	EMemoTable *memo_table;
+	ECalModelComponent *comp_data;
+	ECalModel *model;
+	gchar *comp_str;
+	icalcomponent *child;
+
+	memo_table = E_MEMO_TABLE (data);
+
+	g_return_if_fail (memo_table->tmp_vcal != NULL);
+
+	model = e_memo_table_get_model (memo_table);
+	comp_data = e_cal_model_get_component_at (model, model_row);
+	if (comp_data == NULL)
+		return;
+
+	/* Add timezones to the VCALENDAR component. */
+	e_cal_util_add_timezones_from_component (
+		memo_table->tmp_vcal, comp_data->icalcomp);
+
+	/* Add the new component to the VCALENDAR component. */
+	comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
+	child = icalparser_parse_string (comp_str);
+	if (child) {
+		icalcomponent_add_component (
+			memo_table->tmp_vcal,
+			icalcomponent_new_clone (child));
+		icalcomponent_free (child);
+	}
+	g_free (comp_str);
+}
+
+static void
+memo_table_copy_clipboard (ESelectable *selectable)
+{
+	EMemoTable *memo_table;
+	GtkClipboard *clipboard;
+	gchar *comp_str;
+
+	memo_table = E_MEMO_TABLE (selectable);
+
+	/* Create a temporary VCALENDAR object. */
+	memo_table->tmp_vcal = e_cal_util_new_top_level ();
+
+	e_table_selected_row_foreach (
+		E_TABLE (memo_table), copy_row_cb, memo_table);
+	comp_str = icalcomponent_as_ical_string_r (memo_table->tmp_vcal);
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+	e_clipboard_set_calendar (clipboard, comp_str, -1);
+	gtk_clipboard_store (clipboard);
+
+	g_free (comp_str);
+
+	icalcomponent_free (memo_table->tmp_vcal);
+	memo_table->tmp_vcal = NULL;
+}
+
+/* Helper for memo_table_paste_clipboard() */
+static void
+clipboard_get_calendar_data (EMemoTable *memo_table,
+                             const gchar *text)
+{
+	icalcomponent *icalcomp;
+	gchar *uid;
+	ECalComponent *comp;
+	ECal *client;
+	ECalModel *model;
+	icalcomponent_kind kind;
+	const gchar *status_message;
+
+	g_return_if_fail (E_IS_MEMO_TABLE (memo_table));
+
+	if (!text || !*text)
+		return;
+
+	icalcomp = icalparser_parse_string (text);
+	if (!icalcomp)
+		return;
+
+	/* check the type of the component */
+	kind = icalcomponent_isa (icalcomp);
+	if (kind != ICAL_VCALENDAR_COMPONENT &&
+	    kind != ICAL_VEVENT_COMPONENT &&
+	    kind != ICAL_VTODO_COMPONENT &&
+	    kind != ICAL_VJOURNAL_COMPONENT) {
+		return;
+	}
+
+	model = e_memo_table_get_model (memo_table);
+	client = e_cal_model_get_default_client (model);
+
+	status_message = _("Updating objects");
+	memo_table_emit_status_message (memo_table, status_message, -1.0);
+
+	if (kind == ICAL_VCALENDAR_COMPONENT) {
+		icalcomponent_kind child_kind;
+		icalcomponent *subcomp;
+		icalcomponent *vcal_comp;
+
+		vcal_comp = icalcomp;
+		subcomp = icalcomponent_get_first_component (
+			vcal_comp, ICAL_ANY_COMPONENT);
+		while (subcomp) {
+			child_kind = icalcomponent_isa (subcomp);
+			if (child_kind == ICAL_VEVENT_COMPONENT ||
+			    child_kind == ICAL_VTODO_COMPONENT ||
+			    child_kind == ICAL_VJOURNAL_COMPONENT) {
+				ECalComponent *tmp_comp;
+
+				uid = e_cal_component_gen_uid ();
+				tmp_comp = e_cal_component_new ();
+				e_cal_component_set_icalcomponent (
+					tmp_comp,
+					icalcomponent_new_clone (subcomp));
+				e_cal_component_set_uid (tmp_comp, uid);
+				free (uid);
+
+				/* FIXME Should we convert start/due/complete
+				 *       times?  Also, need error handling.*/
+				e_cal_create_object (
+					client, e_cal_component_get_icalcomponent (tmp_comp),
+					NULL, NULL);
+
+				g_object_unref (tmp_comp);
+			}
+			subcomp = icalcomponent_get_next_component (
+				vcal_comp, ICAL_ANY_COMPONENT);
+		}
+	} else {
+		comp = e_cal_component_new ();
+		e_cal_component_set_icalcomponent (comp, icalcomp);
+		uid = e_cal_component_gen_uid ();
+		e_cal_component_set_uid (comp, (const gchar *) uid);
+		free (uid);
+
+		e_cal_create_object (
+			client, e_cal_component_get_icalcomponent (comp),
+			NULL, NULL);
+
+		g_object_unref (comp);
+	}
+
+	memo_table_emit_status_message (memo_table, NULL, -1.0);
+}
+
+static void
+memo_table_paste_clipboard (ESelectable *selectable)
+{
+	EMemoTable *memo_table;
+	GtkClipboard *clipboard;
+	GnomeCanvasItem *item;
+	GnomeCanvas *table_canvas;
+
+	memo_table = E_MEMO_TABLE (selectable);
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+	table_canvas = E_TABLE (memo_table)->table_canvas;
+	item = table_canvas->focused_item;
+
+	/* XXX Should ECellText implement GtkEditable? */
+
+	/* Paste text into a cell being edited. */
+	if (gtk_clipboard_wait_is_text_available (clipboard) &&
+		GTK_WIDGET_HAS_FOCUS (table_canvas) &&
+		E_IS_TABLE_ITEM (item) &&
+		E_TABLE_ITEM (item)->editing_col >= 0 &&
+		E_TABLE_ITEM (item)->editing_row >= 0) {
+
+		ETableItem *etable_item = E_TABLE_ITEM (item);
+
+		e_cell_text_paste_clipboard (
+			etable_item->cell_views[etable_item->editing_col],
+			etable_item->editing_col,
+			etable_item->editing_row);
+
+	/* Paste iCalendar data into the table. */
+	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
+		gchar *calendar_source;
+
+		calendar_source = e_clipboard_wait_for_calendar (clipboard);
+		clipboard_get_calendar_data (memo_table, calendar_source);
+		g_free (calendar_source);
+	}
+}
+
+static void
+memo_table_select_all (ESelectable *selectable)
+{
+	e_table_select_all (E_TABLE (selectable));
+}
+
+static void
 memo_table_class_init (EMemoTableClass *class)
 {
 	GObjectClass *object_class;
@@ -720,6 +1012,16 @@ memo_table_init (EMemoTable *memo_table)
 	memo_table->priv = E_MEMO_TABLE_GET_PRIVATE (memo_table);
 }
 
+static void
+memo_table_selectable_init (ESelectableInterface *interface)
+{
+	interface->update_actions = memo_table_update_actions;
+	interface->cut_clipboard = memo_table_cut_clipboard;
+	interface->copy_clipboard = memo_table_copy_clipboard;
+	interface->paste_clipboard = memo_table_paste_clipboard;
+	interface->select_all = memo_table_select_all;
+}
+
 GType
 e_memo_table_get_type (void)
 {
@@ -739,8 +1041,17 @@ e_memo_table_get_type (void)
 			NULL   /* value_table */
 		};
 
+		static const GInterfaceInfo selectable_info = {
+			(GInterfaceInitFunc) memo_table_selectable_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
 		type = g_type_register_static (
 			E_TABLE_TYPE, "EMemoTable", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_SELECTABLE, &selectable_info);
 	}
 
 	return type;
@@ -847,33 +1158,6 @@ add_uid_cb (gint model_row, gpointer data)
 	closure->objects = g_slist_prepend (closure->objects, comp_data);
 }
 
-/* Deletes all of the selected components in the table */
-static void
-delete_selected_components (EMemoTable *memo_table)
-{
-	GSList *objs, *l;
-	const gchar *status_message;
-
-	objs = e_memo_table_get_selected (memo_table);
-
-	status_message = _("Deleting selected objects");
-	memo_table_emit_status_message (memo_table, status_message, -1.0);
-
-	for (l = objs; l; l = l->next) {
-		ECalModelComponent *comp_data = (ECalModelComponent *) l->data;
-		GError *error = NULL;
-
-		e_cal_remove_object (comp_data->client,
-				     icalcomponent_get_uid (comp_data->icalcomp), &error);
-		delete_error_dialog (error, E_CAL_COMPONENT_JOURNAL);
-		g_clear_error (&error);
-	}
-
-	memo_table_emit_status_message (memo_table, NULL, -1.0);
-
-	g_slist_free (objs);
-}
-
 /**
  * e_memo_table_delete_selected:
  * @memo_table: A memo table.
@@ -938,210 +1222,6 @@ e_memo_table_get_selected (EMemoTable *memo_table)
 	return closure.objects;
 }
 
-/**
- * e_memo_table_cut_clipboard:
- * @memo_table: A calendar table.
- *
- * Cuts selected tasks in the given calendar table
- */
-void
-e_memo_table_cut_clipboard (EMemoTable *memo_table)
-{
-	g_return_if_fail (E_IS_MEMO_TABLE (memo_table));
-
-	e_memo_table_copy_clipboard (memo_table);
-	delete_selected_components (memo_table);
-}
-
-/* callback for e_table_selected_row_foreach */
-static void
-copy_row_cb (gint model_row, gpointer data)
-{
-	EMemoTable *memo_table;
-	ECalModelComponent *comp_data;
-	ECalModel *model;
-	gchar *comp_str;
-	icalcomponent *child;
-
-	memo_table = E_MEMO_TABLE (data);
-
-	g_return_if_fail (memo_table->tmp_vcal != NULL);
-
-	model = e_memo_table_get_model (memo_table);
-	comp_data = e_cal_model_get_component_at (model, model_row);
-	if (!comp_data)
-		return;
-
-	/* add timezones to the VCALENDAR component */
-	e_cal_util_add_timezones_from_component (memo_table->tmp_vcal, comp_data->icalcomp);
-
-	/* add the new component to the VCALENDAR component */
-	comp_str = icalcomponent_as_ical_string_r (comp_data->icalcomp);
-	child = icalparser_parse_string (comp_str);
-	if (child) {
-		icalcomponent_add_component (memo_table->tmp_vcal,
-					     icalcomponent_new_clone (child));
-		icalcomponent_free (child);
-	}
-	g_free (comp_str);
-}
-
-/**
- * e_memo_table_copy_clipboard:
- * @memo_table: A calendar table.
- *
- * Copies selected tasks into the clipboard
- */
-void
-e_memo_table_copy_clipboard (EMemoTable *memo_table)
-{
-	GtkClipboard *clipboard;
-	gchar *comp_str;
-
-	g_return_if_fail (E_IS_MEMO_TABLE (memo_table));
-
-	/* create temporary VCALENDAR object */
-	memo_table->tmp_vcal = e_cal_util_new_top_level ();
-
-	e_table_selected_row_foreach (
-		E_TABLE (memo_table), copy_row_cb, memo_table);
-	comp_str = icalcomponent_as_ical_string_r (memo_table->tmp_vcal);
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-	e_clipboard_set_calendar (clipboard, comp_str, -1);
-	gtk_clipboard_store (clipboard);
-
-	/* free memory */
-	icalcomponent_free (memo_table->tmp_vcal);
-	g_free (comp_str);
-	memo_table->tmp_vcal = NULL;
-}
-
-static void
-clipboard_get_calendar_data (EMemoTable *memo_table, const gchar *text)
-{
-	icalcomponent *icalcomp;
-	gchar *uid;
-	ECalComponent *comp;
-	ECal *client;
-	ECalModel *model;
-	icalcomponent_kind kind;
-	const gchar *status_message;
-
-	g_return_if_fail (E_IS_MEMO_TABLE (memo_table));
-
-	if (!text || !*text)
-		return;
-
-	icalcomp = icalparser_parse_string (text);
-	if (!icalcomp)
-		return;
-
-	/* check the type of the component */
-	kind = icalcomponent_isa (icalcomp);
-	if (kind != ICAL_VCALENDAR_COMPONENT &&
-	    kind != ICAL_VEVENT_COMPONENT &&
-	    kind != ICAL_VTODO_COMPONENT &&
-	    kind != ICAL_VJOURNAL_COMPONENT) {
-		return;
-	}
-
-	model = e_memo_table_get_model (memo_table);
-	client = e_cal_model_get_default_client (model);
-
-	status_message = _("Updating objects");
-	memo_table_emit_status_message (memo_table, status_message, -1.0);
-
-	if (kind == ICAL_VCALENDAR_COMPONENT) {
-		icalcomponent_kind child_kind;
-		icalcomponent *subcomp;
-		icalcomponent *vcal_comp;
-
-		vcal_comp = icalcomp;
-		subcomp = icalcomponent_get_first_component (
-			vcal_comp, ICAL_ANY_COMPONENT);
-		while (subcomp) {
-			child_kind = icalcomponent_isa (subcomp);
-			if (child_kind == ICAL_VEVENT_COMPONENT ||
-			    child_kind == ICAL_VTODO_COMPONENT ||
-			    child_kind == ICAL_VJOURNAL_COMPONENT) {
-				ECalComponent *tmp_comp;
-
-				uid = e_cal_component_gen_uid ();
-				tmp_comp = e_cal_component_new ();
-				e_cal_component_set_icalcomponent (
-					tmp_comp, icalcomponent_new_clone (subcomp));
-				e_cal_component_set_uid (tmp_comp, uid);
-				free (uid);
-
-				/* FIXME should we convert start/due/complete times? */
-				/* FIXME Error handling */
-				e_cal_create_object (client, e_cal_component_get_icalcomponent (tmp_comp), NULL, NULL);
-
-				g_object_unref (tmp_comp);
-			}
-			subcomp = icalcomponent_get_next_component (
-				vcal_comp, ICAL_ANY_COMPONENT);
-		}
-	} else {
-		comp = e_cal_component_new ();
-		e_cal_component_set_icalcomponent (comp, icalcomp);
-		uid = e_cal_component_gen_uid ();
-		e_cal_component_set_uid (comp, (const gchar *) uid);
-		free (uid);
-
-		e_cal_create_object (client, e_cal_component_get_icalcomponent (comp), NULL, NULL);
-
-		g_object_unref (comp);
-	}
-
-	memo_table_emit_status_message (memo_table, NULL, -1.0);
-}
-
-/**
- * e_memo_table_paste_clipboard:
- * @memo_table: A calendar table.
- *
- * Pastes tasks currently in the clipboard into the given calendar table
- */
-void
-e_memo_table_paste_clipboard (EMemoTable *memo_table)
-{
-	GtkClipboard *clipboard;
-	GnomeCanvasItem *item;
-	GnomeCanvas *table_canvas;
-
-	g_return_if_fail (E_IS_MEMO_TABLE (memo_table));
-
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-	table_canvas = E_TABLE (memo_table)->table_canvas;
-	item = table_canvas->focused_item;
-
-	/* Paste text into a cell being edited. */
-	if (gtk_clipboard_wait_is_text_available (clipboard) &&
-		GTK_WIDGET_HAS_FOCUS (table_canvas) &&
-		E_IS_TABLE_ITEM (item) &&
-		E_TABLE_ITEM (item)->editing_col >= 0 &&
-		E_TABLE_ITEM (item)->editing_row >= 0) {
-
-		ETableItem *etable_item = E_TABLE_ITEM (item);
-
-		e_cell_text_paste_clipboard (
-			etable_item->cell_views[etable_item->editing_col],
-			etable_item->editing_col,
-			etable_item->editing_row);
-
-	/* Paste iCalendar data into the table. */
-	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
-		gchar *calendar_source;
-
-		calendar_source = e_clipboard_wait_for_calendar (clipboard);
-		clipboard_get_calendar_data (memo_table, calendar_source);
-		g_free (calendar_source);
-	}
-}
-
 /* Returns the current time, for the ECellDateEdit items.
    FIXME: Should probably use the timezone of the item rather than the
    current timezone, though that may be difficult to get from here. */
diff --git a/calendar/gui/e-memo-table.h b/calendar/gui/e-memo-table.h
index 4d4347d..b55ddc5 100644
--- a/calendar/gui/e-memo-table.h
+++ b/calendar/gui/e-memo-table.h
@@ -106,11 +106,6 @@ void		e_memo_table_set_use_24_hour_format
 void		e_memo_table_delete_selected	(EMemoTable *memo_table);
 GSList *	e_memo_table_get_selected	(EMemoTable *memo_table);
 
-/* Clipboard related functions */
-void		e_memo_table_cut_clipboard	(EMemoTable *memo_table);
-void		e_memo_table_copy_clipboard	(EMemoTable *memo_table);
-void		e_memo_table_paste_clipboard	(EMemoTable *memo_table);
-
 G_END_DECLS
 
 #endif /* _E_MEMO_TABLE_H_ */
diff --git a/calendar/gui/gnome-cal.h b/calendar/gui/gnome-cal.h
index 0cc528f..197396d 100644
--- a/calendar/gui/gnome-cal.h
+++ b/calendar/gui/gnome-cal.h
@@ -33,7 +33,7 @@
 #include <libecal/e-cal.h>
 #include <shell/e-shell-settings.h>
 
-#include "e-calendar-table.h"
+#include "e-cal-model.h"
 
 /* Standard GObject macros */
 #define GNOME_TYPE_CALENDAR \
diff --git a/calendar/gui/goto.c b/calendar/gui/goto.c
index 86f06f3..569c15f 100644
--- a/calendar/gui/goto.c
+++ b/calendar/gui/goto.c
@@ -26,6 +26,7 @@
 
 #include <config.h>
 #include <gtk/gtk.h>
+#include "e-util/e-util.h"
 #include "e-util/e-util-private.h"
 #include "calendar-config.h"
 #include "tag-calendar.h"
diff --git a/calendar/gui/print.h b/calendar/gui/print.h
index 39d5608..5217c47 100644
--- a/calendar/gui/print.h
+++ b/calendar/gui/print.h
@@ -25,6 +25,7 @@
 #ifndef PRINT_H
 #define PRINT_H
 
+#include <table/e-table.h>
 #include "calendar/gui/gnome-cal.h"
 
 typedef enum {
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index d16afa3..8dfdeeb 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -83,12 +83,13 @@ composer_setup_recent_menu (EMsgComposer *composer)
 }
 
 void
-e_composer_private_init (EMsgComposer *composer)
+e_composer_private_constructed (EMsgComposer *composer)
 {
 	EMsgComposerPrivate *priv = composer->priv;
-
+	EFocusTracker *focus_tracker;
 	GtkhtmlEditor *editor;
 	GtkUIManager *ui_manager;
+	GtkAction *action;
 	GtkWidget *container;
 	GtkWidget *widget;
 	GtkWidget *send_widget;
@@ -157,6 +158,24 @@ e_composer_private_init (EMsgComposer *composer)
 		g_clear_error (&error);
 	}
 
+	/* Configure an EFocusTracker to manage selection actions. */
+
+	focus_tracker = e_focus_tracker_new (GTK_WINDOW (composer));
+
+	action = gtkhtml_editor_get_action (editor, "cut");
+	e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
+
+	action = gtkhtml_editor_get_action (editor, "copy");
+	e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
+
+	action = gtkhtml_editor_get_action (editor, "paste");
+	e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
+
+	action = gtkhtml_editor_get_action (editor, "select-all");
+	e_focus_tracker_set_select_all_action (focus_tracker, action);
+
+	priv->focus_tracker = focus_tracker;
+
 	/* Construct the header table. */
 
 	container = editor->vbox;
@@ -314,6 +333,11 @@ e_composer_private_dispose (EMsgComposer *composer)
 		composer->priv->attachment_paned = NULL;
 	}
 
+	if (composer->priv->focus_tracker != NULL) {
+		g_object_unref (composer->priv->focus_tracker);
+		composer->priv->focus_tracker = NULL;
+	}
+
 	if (composer->priv->window_group != NULL) {
 		g_object_unref (composer->priv->window_group);
 		composer->priv->window_group = NULL;
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index 54837b9..bab8dd1 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -94,6 +94,7 @@ struct _EMsgComposerPrivate {
 	GtkWidget *header_table;
 	GtkWidget *attachment_paned;
 
+	EFocusTracker *focus_tracker;
 	GtkWindowGroup *window_group;
 
 	GtkActionGroup *charset_actions;
@@ -130,7 +131,7 @@ struct _EMsgComposerPrivate {
 	gboolean mail_sent;
 };
 
-void		e_composer_private_init		(EMsgComposer *composer);
+void		e_composer_private_constructed	(EMsgComposer *composer);
 void		e_composer_private_dispose	(EMsgComposer *composer);
 void		e_composer_private_finalize	(EMsgComposer *composer);
 
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 6477936..eed3893 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -55,6 +55,7 @@
 #include "shell/e-shell.h"
 #include "em-format/em-format.h"
 #include "em-format/em-format-quote.h"
+#include "misc/e-web-view.h"
 
 #include <camel/camel-charset-map.h>
 #include <camel/camel-cipher-context.h>
@@ -92,6 +93,11 @@
 	((obj), E_TYPE_MSG_COMPOSER, EMsgComposerPrivate))
 
 enum {
+	PROP_0,
+	PROP_FOCUS_TRACKER
+};
+
+enum {
 	SEND,
 	SAVE_DRAFT,
 	PRINT,
@@ -118,6 +124,14 @@ static void handle_multipart_alternative (EMsgComposer *composer, CamelMultipart
 static void handle_multipart_encrypted (EMsgComposer *composer, CamelMimePart *multipart, gint depth);
 static void handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, gint depth);
 
+static void	msg_composer_drag_data_received	(GtkWidget *widget,
+						 GdkDragContext *context,
+						 gint x,
+						 gint y,
+						 GtkSelectionData *selection,
+						 guint info,
+						 guint time);
+
 /**
  * emcu_part_to_html:
  * @part:
@@ -1514,32 +1528,90 @@ msg_composer_notify_header_cb (EMsgComposer *composer)
 	gtkhtml_editor_set_changed (editor, TRUE);
 }
 
-static GObject *
-msg_composer_constructor (GType type,
-                          guint n_construct_properties,
-                          GObjectConstructParam *construct_properties)
+static void
+msg_composer_get_property (GObject *object,
+                           guint property_id,
+                           GValue *value,
+                           GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_FOCUS_TRACKER:
+			g_value_set_object (
+				value, e_msg_composer_get_focus_tracker (
+				E_MSG_COMPOSER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+msg_composer_dispose (GObject *object)
+{
+	EMsgComposer *composer = E_MSG_COMPOSER (object);
+
+	e_composer_private_dispose (composer);
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+msg_composer_finalize (GObject *object)
+{
+	EMsgComposer *composer = E_MSG_COMPOSER (object);
+
+	e_composer_autosave_unregister (composer);
+	e_composer_private_finalize (composer);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+msg_composer_constructed (GObject *object)
 {
 	EShell *shell;
 	EShellSettings *shell_settings;
-	GObject *object;
+	GtkhtmlEditor *editor;
 	EMsgComposer *composer;
+	EAttachmentView *view;
+	EAttachmentStore *store;
+	EComposerHeaderTable *table;
+	GdkDragAction drag_actions;
+	GtkTargetList *target_list;
+	GtkTargetEntry *targets;
+	GtkUIManager *ui_manager;
 	GtkToggleAction *action;
+	GtkHTML *html;
 	GArray *array;
+	const gchar *id;
 	gboolean active;
 	guint binding_id;
+	gint n_targets;
 
-	/* Chain up to parent's constructor() method. */
-	object = G_OBJECT_CLASS (parent_class)->constructor (
-		type, n_construct_properties, construct_properties);
-
+	editor = GTKHTML_EDITOR (object);
 	composer = E_MSG_COMPOSER (object);
-	array = composer->priv->gconf_bridge_binding_ids;
 
 	shell = e_shell_get_default ();
 	shell_settings = e_shell_get_shell_settings (shell);
 
+	e_composer_private_constructed (composer);
+
+	html = gtkhtml_editor_get_html (editor);
+	ui_manager = gtkhtml_editor_get_ui_manager (editor);
+	view = e_msg_composer_get_attachment_view (composer);
+	table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
+
+	gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message"));
+	gtk_window_set_icon_name (GTK_WINDOW (composer), "mail-message-new");
+
+	e_shell_watch_window (shell, GTK_WINDOW (object));
+
 	/* Restore Persistent State */
 
+	array = composer->priv->gconf_bridge_binding_ids;
+
 	binding_id = gconf_bridge_bind_property (
 		gconf_bridge_get (),
 		COMPOSER_GCONF_CURRENT_FOLDER_KEY,
@@ -1563,32 +1635,77 @@ msg_composer_constructor (GType type,
 		shell_settings, "composer-request-receipt");
 	gtk_toggle_action_set_active (action, active);
 
-	e_shell_watch_window (shell, GTK_WINDOW (object));
+	/* Drag-and-Drop Support */
 
-	return object;
-}
+	target_list = e_attachment_view_get_target_list (view);
+	drag_actions = e_attachment_view_get_drag_actions (view);
 
-static void
-msg_composer_dispose (GObject *object)
-{
-	EMsgComposer *composer = E_MSG_COMPOSER (object);
+	targets = gtk_target_table_new_from_list (target_list, &n_targets);
 
-	e_composer_private_dispose (composer);
+	gtk_drag_dest_set (
+		GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
+		targets, n_targets, drag_actions);
 
-	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (parent_class)->dispose (object);
-}
+	g_signal_connect (
+		html, "drag-data-received",
+		G_CALLBACK (msg_composer_drag_data_received), NULL);
 
-static void
-msg_composer_finalize (GObject *object)
-{
-	EMsgComposer *composer = E_MSG_COMPOSER (object);
+	gtk_target_table_free (targets, n_targets);
 
-	e_composer_autosave_unregister (composer);
-	e_composer_private_finalize (composer);
+	/* Configure Headers */
 
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (parent_class)->finalize (object);
+	e_composer_header_table_set_account_list (
+		table, e_get_account_list ());
+	e_composer_header_table_set_signature_list (
+		table, e_get_signature_list ());
+
+	g_signal_connect_swapped (
+		table, "notify::account",
+		G_CALLBACK (msg_composer_account_changed_cb), composer);
+	g_signal_connect_swapped (
+		table, "notify::destinations-bcc",
+		G_CALLBACK (msg_composer_notify_header_cb), composer);
+	g_signal_connect_swapped (
+		table, "notify::destinations-cc",
+		G_CALLBACK (msg_composer_notify_header_cb), composer);
+	g_signal_connect_swapped (
+		table, "notify::destinations-to",
+		G_CALLBACK (msg_composer_notify_header_cb), composer);
+	g_signal_connect_swapped (
+		table, "notify::reply-to",
+		G_CALLBACK (msg_composer_notify_header_cb), composer);
+	g_signal_connect_swapped (
+		table, "notify::signature",
+		G_CALLBACK (e_msg_composer_show_sig_file), composer);
+	g_signal_connect_swapped (
+		table, "notify::subject",
+		G_CALLBACK (msg_composer_subject_changed_cb), composer);
+	g_signal_connect_swapped (
+		table, "notify::subject",
+		G_CALLBACK (msg_composer_notify_header_cb), composer);
+
+	msg_composer_account_changed_cb (composer);
+
+	/* Attachments */
+
+	store = e_attachment_view_get_store (view);
+
+	g_signal_connect_swapped (
+		store, "row-deleted",
+		G_CALLBACK (attachment_store_changed_cb), composer);
+
+	g_signal_connect_swapped (
+		store, "row-inserted",
+		G_CALLBACK (attachment_store_changed_cb), composer);
+
+	e_composer_autosave_register (composer);
+
+	/* Initialization may have tripped the "changed" state. */
+	gtkhtml_editor_set_changed (editor, FALSE);
+
+	id = "org.gnome.evolution.composer";
+	e_plugin_ui_register_manager (ui_manager, id, composer);
+	e_plugin_ui_enable_manager (ui_manager, id);
 }
 
 static void
@@ -2045,9 +2162,10 @@ msg_composer_class_init (EMsgComposerClass *class)
 	g_type_class_add_private (class, sizeof (EMsgComposerPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
-	object_class->constructor = msg_composer_constructor;
+	object_class->get_property = msg_composer_get_property;
 	object_class->dispose = msg_composer_dispose;
 	object_class->finalize = msg_composer_finalize;
+	object_class->constructed = msg_composer_constructed;
 
 	gtk_object_class = GTK_OBJECT_CLASS (class);
 	gtk_object_class->destroy = msg_composer_destroy;
@@ -2071,6 +2189,16 @@ msg_composer_class_init (EMsgComposerClass *class)
 	editor_class->object_deleted = msg_composer_object_deleted;
 	editor_class->uri_requested = msg_composer_uri_requested;
 
+	g_object_class_install_property (
+		object_class,
+		PROP_FOCUS_TRACKER,
+		g_param_spec_object (
+			"focus-tracker",
+			NULL,
+			NULL,
+			E_TYPE_FOCUS_TRACKER,
+			G_PARAM_READABLE));
+
 	signals[SEND] = g_signal_new (
 		"send",
 		G_OBJECT_CLASS_TYPE (class),
@@ -2100,103 +2228,8 @@ msg_composer_class_init (EMsgComposerClass *class)
 static void
 msg_composer_init (EMsgComposer *composer)
 {
-	EAttachmentView *view;
-	EAttachmentStore *store;
-	EComposerHeaderTable *table;
-	GdkDragAction drag_actions;
-	GtkTargetList *target_list;
-	GtkTargetEntry *targets;
-	GtkUIManager *ui_manager;
-	GtkhtmlEditor *editor;
-	GtkHTML *html;
-	const gchar *id;
-	gint n_targets;
-
 	composer->lite = composer_lite;
 	composer->priv = E_MSG_COMPOSER_GET_PRIVATE (composer);
-
-	e_composer_private_init (composer);
-
-	editor = GTKHTML_EDITOR (composer);
-	html = gtkhtml_editor_get_html (editor);
-	ui_manager = gtkhtml_editor_get_ui_manager (editor);
-	view = e_msg_composer_get_attachment_view (composer);
-	table = E_COMPOSER_HEADER_TABLE (composer->priv->header_table);
-
-	gtk_window_set_title (GTK_WINDOW (composer), _("Compose Message"));
-	gtk_window_set_icon_name (GTK_WINDOW (composer), "mail-message-new");
-
-	/* Drag-and-Drop Support */
-
-	target_list = e_attachment_view_get_target_list (view);
-	drag_actions = e_attachment_view_get_drag_actions (view);
-
-	targets = gtk_target_table_new_from_list (target_list, &n_targets);
-
-	gtk_drag_dest_set (
-		GTK_WIDGET (composer), GTK_DEST_DEFAULT_ALL,
-		targets, n_targets, drag_actions);
-
-	g_signal_connect (
-		html, "drag-data-received",
-		G_CALLBACK (msg_composer_drag_data_received), NULL);
-
-	gtk_target_table_free (targets, n_targets);
-
-	/* Configure Headers */
-
-	e_composer_header_table_set_account_list (
-		table, e_get_account_list ());
-	e_composer_header_table_set_signature_list (
-		table, e_get_signature_list ());
-
-	g_signal_connect_swapped (
-		table, "notify::account",
-		G_CALLBACK (msg_composer_account_changed_cb), composer);
-	g_signal_connect_swapped (
-		table, "notify::destinations-bcc",
-		G_CALLBACK (msg_composer_notify_header_cb), composer);
-	g_signal_connect_swapped (
-		table, "notify::destinations-cc",
-		G_CALLBACK (msg_composer_notify_header_cb), composer);
-	g_signal_connect_swapped (
-		table, "notify::destinations-to",
-		G_CALLBACK (msg_composer_notify_header_cb), composer);
-	g_signal_connect_swapped (
-		table, "notify::reply-to",
-		G_CALLBACK (msg_composer_notify_header_cb), composer);
-	g_signal_connect_swapped (
-		table, "notify::signature",
-		G_CALLBACK (e_msg_composer_show_sig_file), composer);
-	g_signal_connect_swapped (
-		table, "notify::subject",
-		G_CALLBACK (msg_composer_subject_changed_cb), composer);
-	g_signal_connect_swapped (
-		table, "notify::subject",
-		G_CALLBACK (msg_composer_notify_header_cb), composer);
-
-	msg_composer_account_changed_cb (composer);
-
-	/* Attachments */
-
-	store = e_attachment_view_get_store (view);
-
-	g_signal_connect_swapped (
-		store, "row-deleted",
-		G_CALLBACK (attachment_store_changed_cb), composer);
-
-	g_signal_connect_swapped (
-		store, "row-inserted",
-		G_CALLBACK (attachment_store_changed_cb), composer);
-
-	e_composer_autosave_register (composer);
-
-	/* Initialization may have tripped the "changed" state. */
-	gtkhtml_editor_set_changed (editor, FALSE);
-
-	id = "org.gnome.evolution.composer";
-	e_plugin_ui_register_manager (ui_manager, id, composer);
-	e_plugin_ui_enable_manager (ui_manager, id);
 }
 
 GType
@@ -2237,7 +2270,9 @@ e_msg_composer_get_type (void)
 EMsgComposer *
 e_msg_composer_new (void)
 {
-	return g_object_new (E_TYPE_MSG_COMPOSER, NULL);
+	return g_object_new (
+		E_TYPE_MSG_COMPOSER,
+		"html", e_web_view_new (), NULL);
 }
 
 void
@@ -2265,6 +2300,14 @@ e_msg_composer_lite_new (void)
 	return composer;
 }
 
+EFocusTracker *
+e_msg_composer_get_focus_tracker (EMsgComposer *composer)
+{
+	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+	return composer->priv->focus_tracker;
+}
+
 static void
 e_msg_composer_set_pending_body (EMsgComposer *composer,
                                  gchar *text,
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index 74128e0..c55237c 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -31,6 +31,7 @@
 #include <libebook/e-destination.h>
 #include <gtkhtml-editor.h>
 #include <misc/e-attachment-view.h>
+#include <misc/e-focus-tracker.h>
 
 #include "e-composer-header-table.h"
 
@@ -78,6 +79,7 @@ EMsgComposer *	e_msg_composer_new_with_message	(CamelMimeMessage *msg);
 EMsgComposer *	e_msg_composer_new_from_url	(const gchar *url);
 EMsgComposer *	e_msg_composer_new_redirect	(CamelMimeMessage *message,
 						 const gchar *resent_from);
+EFocusTracker *	e_msg_composer_get_focus_tracker(EMsgComposer *composer);
 CamelSession *	e_msg_composer_get_session	(EMsgComposer *composer);
 
 void		e_msg_composer_send		(EMsgComposer *composer);
diff --git a/configure.ac b/configure.ac
index 1d97d64..ecf88ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,7 +46,7 @@ m4_define([gtk_minimum_version], [2.16.0])
 m4_define([eds_minimum_version], [evo_version])
 m4_define([gnome_icon_theme_minimum_version], [2.19.91])
 m4_define([gnome_desktop_minimum_version], [2.26.0])
-m4_define([libgtkhtml_minimum_version], [3.29.2])
+m4_define([libgtkhtml_minimum_version], [3.29.5])
 m4_define([gconf_minimum_version], [2.0.0])		dnl XXX Just a Guess
 m4_define([libgnomecanvas_minimum_version], [2.0.0])	dnl XXX Just a Guess
 m4_define([libxml_minimum_version], [2.7.3])
diff --git a/doc/reference/shell/tmpl/e-shell-window.sgml b/doc/reference/shell/tmpl/e-shell-window.sgml
index 33a5928..89da4a1 100644
--- a/doc/reference/shell/tmpl/e-shell-window.sgml
+++ b/doc/reference/shell/tmpl/e-shell-window.sgml
@@ -28,6 +28,11 @@ EShellWindow
 
 </para>
 
+<!-- ##### ARG EShellWindow:focus-tracker ##### -->
+<para>
+
+</para>
+
 <!-- ##### ARG EShellWindow:geometry ##### -->
 <para>
 
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index 6dae4de..9b7cc4d 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -166,19 +166,6 @@ action_mail_check_for_junk_cb (GtkAction *action,
 }
 
 static void
-action_mail_clipboard_copy_cb (GtkAction *action,
-                               EMailReader *reader)
-{
-	EMFormatHTMLDisplay *html_display;
-	EWebView *web_view;
-
-	html_display = e_mail_reader_get_html_display (reader);
-	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
-
-	e_web_view_clipboard_copy (web_view);
-}
-
-static void
 action_mail_copy_cb (GtkAction *action,
                      EMailReader *reader)
 {
@@ -875,26 +862,6 @@ action_mail_search_folder_from_subject_cb (GtkAction *action,
 }
 
 static void
-action_mail_select_all_cb (GtkAction *action,
-                           EMailReader *reader)
-{
-	EMFormatHTMLDisplay *html_display;
-	EWebView *web_view;
-	const gchar *action_name;
-	gboolean selection_active;
-
-	html_display = e_mail_reader_get_html_display (reader);
-	web_view = E_WEB_VIEW (EM_FORMAT_HTML (html_display)->html);
-
-	e_web_view_select_all (web_view);
-
-	action_name = "mail-clipboard-copy";
-	action = e_mail_reader_get_action (reader, action_name);
-	selection_active = e_web_view_is_selection_active (web_view);
-	gtk_action_set_sensitive (action, selection_active);
-}
-
-static void
 action_mail_show_all_headers_cb (GtkToggleAction *action,
                                  EMailReader *reader)
 {
@@ -1112,13 +1079,6 @@ static GtkActionEntry mail_reader_entries[] = {
 	  N_("Filter the selected messages for junk status"),
 	  G_CALLBACK (action_mail_check_for_junk_cb) },
 
-	{ "mail-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy selected messages to the clipboard"),
-	  G_CALLBACK (action_mail_clipboard_copy_cb) },
-
 	{ "mail-copy",
 	  "mail-copy",
 	  N_("_Copy to Folder..."),
@@ -1441,13 +1401,6 @@ static GtkActionEntry mail_reader_entries[] = {
 	  N_("Create a search folder for this subject"),
 	  G_CALLBACK (action_mail_search_folder_from_subject_cb) },
 
-	{ "mail-select-all",
-	  NULL,
-	  N_("Select _All Text"),
-	  "<Shift><Control>x",
-	  N_("Select all the text in a message"),
-	  G_CALLBACK (action_mail_select_all_cb) },
-
 	{ "mail-show-source",
 	  NULL,
 	  N_("_Message Source"),
@@ -1544,10 +1497,6 @@ static GtkActionEntry mail_reader_entries[] = {
 
 static EPopupActionEntry mail_reader_popup_entries[] = {
 
-	{ "mail-popup-clipboard-copy",
-	  NULL,
-	  "mail-clipboard-copy" },
-
 	{ "mail-popup-copy",
 	  NULL,
 	  "mail-copy" },
@@ -1644,25 +1593,6 @@ static GtkToggleActionEntry mail_reader_toggle_entries[] = {
 	  FALSE }
 };
 
-static gboolean
-mail_reader_button_release_event_cb (EMailReader *reader,
-                                     GdkEventButton *button,
-                                     GtkHTML *html)
-{
-	GtkAction *action;
-	EWebView *web_view;
-	const gchar *action_name;
-	gboolean selection_active;
-
-	web_view = E_WEB_VIEW (html);
-	action_name = "mail-clipboard-copy";
-	action = e_mail_reader_get_action (reader, action_name);
-	selection_active = e_web_view_is_selection_active (web_view);
-	gtk_action_set_sensitive (action, selection_active);
-
-	return FALSE;
-}
-
 static void
 mail_reader_double_click_cb (EMailReader *reader,
                              gint row,
@@ -2377,11 +2307,6 @@ mail_reader_update_actions (EMailReader *reader)
 	action = e_mail_reader_get_action (reader, action_name);
 	gtk_action_set_sensitive (action, sensitive);
 
-	action_name = "mail-select-all";
-	sensitive = single_message_selected;
-	action = e_mail_reader_get_action (reader, action_name);
-	gtk_action_set_sensitive (action, sensitive);
-
 	action_name = "mail-show-source";
 	sensitive = single_message_selected;
 	action = e_mail_reader_get_action (reader, action_name);
@@ -2585,10 +2510,6 @@ e_mail_reader_init (EMailReader *reader)
 
 	/* Fine tuning. */
 
-	action_name = "mail-clipboard-copy";
-	action = e_mail_reader_get_action (reader, action_name);
-	gtk_action_set_sensitive (action, FALSE);
-
 	action_name = "mail-delete";
 	action = e_mail_reader_get_action (reader, action_name);
 	g_object_set (action, "short-label", _("Delete"), NULL);
@@ -2661,10 +2582,6 @@ e_mail_reader_init (EMailReader *reader)
 	/* Connect signals. */
 
 	g_signal_connect_swapped (
-		web_view, "button-release-event",
-		G_CALLBACK (mail_reader_button_release_event_cb), reader);
-
-	g_signal_connect_swapped (
 		web_view, "key-press-event",
 		G_CALLBACK (mail_reader_key_press_event_cb), reader);
 
diff --git a/mail/message-list.c b/mail/message-list.c
index dcf2e4f..49a839a 100644
--- a/mail/message-list.c
+++ b/mail/message-list.c
@@ -52,6 +52,7 @@
 #include "e-util/e-util.h"
 
 #include "misc/e-gui-utils.h"
+#include "misc/e-selectable.h"
 
 #include "shell/e-shell.h"
 #include "shell/e-shell-settings.h"
@@ -126,6 +127,8 @@ enum {
 	PROP_SHELL_BACKEND
 };
 
+static gpointer parent_class;
+
 static struct {
 	const gchar *target;
 	GdkAtom atom;
@@ -197,8 +200,6 @@ struct _EMailAddress {
 typedef struct _EMailAddress EMailAddress;
 #endif /* SMART_ADDRESS_COMPARE */
 
-G_DEFINE_TYPE (MessageList, message_list, E_TREE_TYPE)
-
 static void on_cursor_activated_cmd (ETree *tree, gint row, ETreePath path, gpointer user_data);
 static void on_selection_changed_cmd(ETree *tree, MessageList *ml);
 static gint on_click (ETree *tree, gint row, ETreePath path, gint col, GdkEvent *event, MessageList *list);
@@ -718,6 +719,18 @@ message_list_select_next_thread (MessageList *ml)
 	}
 }
 
+static gboolean
+message_list_select_all_timeout_cb (MessageList *message_list)
+{
+	ESelectionModel *etsm;
+
+	etsm = e_tree_get_selection_model (E_TREE (message_list));
+
+	e_selection_model_select_all (etsm);
+
+	return FALSE;
+}
+
 /**
  * message_list_select_all:
  * @message_list: Message List widget
@@ -727,11 +740,21 @@ message_list_select_next_thread (MessageList *ml)
 void
 message_list_select_all (MessageList *message_list)
 {
-	ESelectionModel *etsm;
-
-	etsm = e_tree_get_selection_model (E_TREE (message_list));
+	g_return_if_fail (IS_MESSAGE_LIST (message_list));
 
-	e_selection_model_select_all (etsm);
+	if (message_list->threaded) {
+		/* XXX The timeout below is added so that the execution
+		 *     thread to expand all conversation threads would
+		 *     have completed.  The timeout 505 is just to ensure
+		 *     that the value is a small delta more than the
+		 *     timeout value in mail_regen_list(). */
+		g_timeout_add (
+			505, (GSourceFunc)
+			message_list_select_all_timeout_cb,
+			message_list);
+	} else
+		/* If there is no threading, just select all immediately. */
+		message_list_select_all_timeout_cb (message_list);
 }
 
 typedef struct thread_select_info {
@@ -2295,9 +2318,6 @@ message_list_init (MessageList *message_list)
 
 	message_list->priv = MESSAGE_LIST_GET_PRIVATE (message_list);
 
-/*	adjustment = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, G_MAXDOUBLE, 0.0, 0.0, 0.0);
-	gtk_scrolled_window_set_vadjustment ((GtkScrolledWindow *) message_list, adjustment);*/
-
 	message_list->normalised_hash = g_hash_table_new_full (
 		g_str_hash, g_str_equal,
 		(GDestroyNotify) NULL,
@@ -2390,7 +2410,7 @@ message_list_destroy(GtkObject *object)
 	}
 
 	/* Chain up to parent's destroy() method. */
-	GTK_OBJECT_CLASS (message_list_parent_class)->destroy(object);
+	GTK_OBJECT_CLASS (parent_class)->destroy(object);
 }
 
 static void
@@ -2440,7 +2460,7 @@ message_list_dispose (GObject *object)
 	}
 
 	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (message_list_parent_class)->dispose (object);
+	G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
@@ -2480,7 +2500,7 @@ message_list_finalize (GObject *object)
 	clear_selection(message_list, &priv->clipboard);
 
 	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (message_list_parent_class)->finalize (object);
+	G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
@@ -2490,6 +2510,25 @@ message_list_built (MessageList *message_list)
 }
 
 static void
+message_list_selectable_update_actions (ESelectable *selectable,
+                                        EFocusTracker *focus_tracker,
+                                        GdkAtom *clipboard_targets,
+                                        gint n_clipboard_targets)
+{
+	GtkAction *action;
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	gtk_action_set_tooltip (action, _("Select all visible messages"));
+	gtk_action_set_sensitive (action, TRUE);
+}
+
+static void
+message_list_selectable_select_all (ESelectable *selectable)
+{
+	message_list_select_all (MESSAGE_LIST (selectable));
+}
+
+static void
 message_list_class_init (MessageListClass *class)
 {
 	GObjectClass *object_class;
@@ -2499,6 +2538,7 @@ message_list_class_init (MessageListClass *class)
 	for (i = 0; i < G_N_ELEMENTS (ml_drag_info); i++)
 		ml_drag_info[i].atom = gdk_atom_intern(ml_drag_info[i].target, FALSE);
 
+	parent_class = g_type_class_peek_parent (class);
 	g_type_class_add_private (class, sizeof (MessageListPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
@@ -2546,6 +2586,13 @@ message_list_class_init (MessageListClass *class)
 	message_list_init_images ();
 }
 
+static void
+message_list_selectable_init (ESelectableInterface *interface)
+{
+	interface->update_actions = message_list_selectable_update_actions;
+	interface->select_all = message_list_selectable_select_all;
+}
+
 static gboolean
 read_boolean_with_default (GConfClient *gconf, const gchar *key, gboolean def_value)
 {
@@ -2668,6 +2715,41 @@ message_list_construct (MessageList *message_list)
 		G_CALLBACK (ml_tree_sorting_changed), message_list);
 }
 
+GType
+message_list_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (MessageListClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) message_list_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (MessageList),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) message_list_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo selectable_info = {
+			(GInterfaceInitFunc) message_list_selectable_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
+		type = g_type_register_static (
+			E_TREE_TYPE, "MessageList", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_SELECTABLE, &selectable_info);
+	}
+
+	return type;
+}
+
 /**
  * message_list_new:
  *
diff --git a/mail/message-list.h b/mail/message-list.h
index 72960ba..e239944 100644
--- a/mail/message-list.h
+++ b/mail/message-list.h
@@ -170,9 +170,9 @@ struct _MessageListClass {
 	ETreeClass parent_class;
 
 	/* signals - select a message */
-	void (*message_selected) (MessageList *ml, const gchar *uid);
-	void (*message_list_built) (MessageList *ml);
-	void (*message_list_scrolled) (MessageList *ml);
+	void (*message_selected) (MessageList *message_list, const gchar *uid);
+	void (*message_list_built) (MessageList *message_list);
+	void (*message_list_scrolled) (MessageList *message_list);
 };
 
 typedef enum {
@@ -182,58 +182,59 @@ typedef enum {
 	MESSAGE_LIST_SELECT_WRAP = 1<<1 /* option bit */
 } MessageListSelectDirection;
 
-GType          message_list_get_type   (void);
-GtkWidget     *message_list_new        (EShellBackend *shell_backend);
-EShellBackend  *message_list_get_shell_backend (MessageList *message_list);
-void           message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, const gchar *uri, gboolean outgoing);
-
-void	       message_list_freeze(MessageList *ml);
-void	       message_list_thaw(MessageList *ml);
-
-GPtrArray     *message_list_get_uids(MessageList *message_list);
-GPtrArray     *message_list_get_selected(MessageList *ml);
-void           message_list_set_selected(MessageList *ml, GPtrArray *uids);
-
-/* select next/prev message helpers */
-gboolean       message_list_select     (MessageList *message_list,
-					MessageListSelectDirection direction,
-					guint32 flags,
-					guint32 mask);
-gboolean message_list_can_select(MessageList *ml, MessageListSelectDirection direction, guint32 flags, guint32 mask);
-
-void           message_list_select_uid (MessageList *message_list,
-					const gchar *uid);
-
-void           message_list_select_next_thread (MessageList *ml);
-
-/* selection manipulation */
-void           message_list_select_all (MessageList *ml);
-void           message_list_select_thread (MessageList *ml);
-void           message_list_select_subthread (MessageList *ml);
-void           message_list_invert_selection (MessageList *ml);
-
-/* clipboard stuff */
-void	       message_list_copy(MessageList *ml, gboolean cut);
-void           message_list_paste (MessageList *ml);
-
-/* info */
-guint   message_list_length (MessageList *ml);
-guint   message_list_hidden (MessageList *ml);
-
-/* hide specific messages */
-void	       message_list_hide_add (MessageList *ml, const gchar *expr, guint lower, guint upper);
-void	       message_list_hide_uids (MessageList *ml, GPtrArray *uids);
-void	       message_list_hide_clear (MessageList *ml);
-
-void	       message_list_set_threaded (MessageList *ml, gboolean threaded);
-void           message_list_set_threaded_expand_all (MessageList *ml);
-void           message_list_set_threaded_collapse_all (MessageList *ml);
-
-void	       message_list_set_hidedeleted (MessageList *ml, gboolean hidedeleted);
-void	       message_list_set_search (MessageList *ml, const gchar *search);
-void	       message_list_ensure_message (MessageList *ml, const gchar *uid);
-
-void           message_list_save_state (MessageList *ml);
+GType		message_list_get_type		(void);
+GtkWidget *	message_list_new		(EShellBackend *shell_backend);
+EShellBackend *	message_list_get_shell_backend	(MessageList *message_list);
+void		message_list_set_folder		(MessageList *message_list,
+						 CamelFolder *camel_folder,
+						 const gchar *uri,
+						 gboolean outgoing);
+void		message_list_freeze		(MessageList *message_list);
+void		message_list_thaw		(MessageList *message_list);
+GPtrArray *	message_list_get_uids		(MessageList *message_list);
+GPtrArray *	message_list_get_selected	(MessageList *message_list);
+void		message_list_set_selected	(MessageList *message_list,
+						 GPtrArray *uids);
+gboolean	message_list_select		(MessageList *message_list,
+					 	 MessageListSelectDirection direction,
+						 guint32 flags,
+						 guint32 mask);
+gboolean	message_list_can_select		(MessageList *message_list,
+						 MessageListSelectDirection direction,
+						 guint32 flags,
+						 guint32 mask);
+void		message_list_select_uid		(MessageList *message_list,
+						 const gchar *uid);
+void		message_list_select_next_thread	(MessageList *message_list);
+void		message_list_select_all		(MessageList *message_list);
+void		message_list_select_thread	(MessageList *message_list);
+void		message_list_select_subthread	(MessageList *message_list);
+void		message_list_invert_selection	(MessageList *message_list);
+void		message_list_copy		(MessageList *message_list,
+						 gboolean cut);
+void		message_list_paste		(MessageList *message_list);
+guint		message_list_length		(MessageList *message_list);
+guint		message_list_hidden		(MessageList *message_list);
+void		message_list_hide_add		(MessageList *message_list,
+						 const gchar *expr,
+						 guint lower,
+						 guint upper);
+void		message_list_hide_uids		(MessageList *message_list,
+						 GPtrArray *uids);
+void		message_list_hide_clear		(MessageList *message_list);
+void		message_list_set_threaded	(MessageList *message_list,
+						 gboolean threaded);
+void		message_list_set_threaded_expand_all
+						(MessageList *message_list);
+void		message_list_set_threaded_collapse_all
+						(MessageList *message_list);
+void		message_list_set_hidedeleted	(MessageList *message_list,
+						 gboolean hidedeleted);
+void		message_list_set_search		(MessageList *message_list,
+						 const gchar *search);
+void		message_list_ensure_message	(MessageList *message_list,
+						 const gchar *uid);
+void		message_list_save_state		(MessageList *message_list);
 
 #define MESSAGE_LIST_LOCK(m, l) g_mutex_lock(((MessageList *)m)->l)
 #define MESSAGE_LIST_UNLOCK(m, l) g_mutex_unlock(((MessageList *)m)->l)
diff --git a/modules/addressbook/e-book-shell-content.c b/modules/addressbook/e-book-shell-content.c
index 5eb4459..a7a616f 100644
--- a/modules/addressbook/e-book-shell-content.c
+++ b/modules/addressbook/e-book-shell-content.c
@@ -284,11 +284,9 @@ book_shell_content_check_state (EShellContent *shell_content)
 	ESelectionModel *selection_model;
 	EAddressbookModel *model;
 	EAddressbookView *view;
-	GtkClipboard *clipboard;
 	gboolean has_email = TRUE;
 	gboolean is_contact_list = TRUE;
 	guint32 state = 0;
-	gint n_contacts;
 	gint n_selected;
 
 	struct {
@@ -301,8 +299,6 @@ book_shell_content_check_state (EShellContent *shell_content)
 	model = e_addressbook_view_get_model (view);
 
 	selection_model = e_addressbook_view_get_selection_model (view);
-	n_contacts = (selection_model != NULL) ?
-		e_selection_model_row_count (selection_model) : 0;
 	n_selected = (selection_model != NULL) ?
 		e_selection_model_selected_count (selection_model) : 0;
 
@@ -333,8 +329,6 @@ book_shell_content_check_state (EShellContent *shell_content)
 			foreach_data.list, foreach_data.list);
 	}
 
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
 	if (n_selected == 1)
 		state |= E_BOOK_SHELL_CONTENT_SELECTION_SINGLE;
 	if (n_selected > 1)
@@ -347,10 +341,6 @@ book_shell_content_check_state (EShellContent *shell_content)
 		state |= E_BOOK_SHELL_CONTENT_SOURCE_IS_BUSY;
 	if (e_addressbook_model_get_editable (model))
 		state |= E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE;
-	if (n_contacts == 0)
-		state |= E_BOOK_SHELL_CONTENT_SOURCE_IS_EMPTY;
-	if (e_clipboard_wait_is_directory_available (clipboard))
-		state |= E_BOOK_SHELL_CONTENT_CLIPBOARD_HAS_DIRECTORY;
 
 	return state;
 }
@@ -649,24 +639,3 @@ e_book_shell_content_get_searchbar (EBookShellContent *book_shell_content)
 
 	return E_SHELL_SEARCHBAR (widget);
 }
-
-void
-e_book_shell_content_clipboard_copy (EBookShellContent *book_shell_content)
-{
-	EAddressbookView *addressbook_view;
-	EWebView *web_view;
-
-	g_return_if_fail (E_IS_BOOK_SHELL_CONTENT (book_shell_content));
-
-	web_view = E_WEB_VIEW (book_shell_content->priv->preview);
-	addressbook_view =
-		e_book_shell_content_get_current_view (book_shell_content);
-	g_return_if_fail (addressbook_view != NULL);
-
-	if (!GTK_WIDGET_HAS_FOCUS (web_view)) {
-		e_addressbook_view_copy (addressbook_view);
-		return;
-	}
-
-	e_web_view_clipboard_copy (web_view);
-}
diff --git a/modules/addressbook/e-book-shell-content.h b/modules/addressbook/e-book-shell-content.h
index 62cc7bf..6ed5b38 100644
--- a/modules/addressbook/e-book-shell-content.h
+++ b/modules/addressbook/e-book-shell-content.h
@@ -62,9 +62,7 @@ enum {
 	E_BOOK_SHELL_CONTENT_SELECTION_HAS_EMAIL	= 1 << 2,
 	E_BOOK_SHELL_CONTENT_SELECTION_IS_CONTACT_LIST	= 1 << 3,
 	E_BOOK_SHELL_CONTENT_SOURCE_IS_BUSY		= 1 << 4,
-	E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE		= 1 << 5,
-	E_BOOK_SHELL_CONTENT_SOURCE_IS_EMPTY		= 1 << 6,
-	E_BOOK_SHELL_CONTENT_CLIPBOARD_HAS_DIRECTORY	= 1 << 7
+	E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE		= 1 << 5
 };
 
 struct _EBookShellContent {
@@ -108,8 +106,6 @@ void		e_book_shell_content_set_preview_visible
 EShellSearchbar *
 		e_book_shell_content_get_searchbar
 					(EBookShellContent *book_shell_content);
-void		e_book_shell_content_clipboard_copy
-					(EBookShellContent *book_shell_content);
 
 G_END_DECLS
 
diff --git a/modules/addressbook/e-book-shell-view-actions.c b/modules/addressbook/e-book-shell-view-actions.c
index 904b777..5cfdbd3 100644
--- a/modules/addressbook/e-book-shell-view-actions.c
+++ b/modules/addressbook/e-book-shell-view-actions.c
@@ -312,44 +312,6 @@ action_address_book_stop_cb (GtkAction *action,
 }
 
 static void
-action_contact_clipboard_copy_cb (GtkAction *action,
-                                  EBookShellView *book_shell_view)
-{
-	EBookShellContent *book_shell_content;
-
-	book_shell_content = book_shell_view->priv->book_shell_content;
-	e_book_shell_content_clipboard_copy (book_shell_content);
-}
-
-static void
-action_contact_clipboard_cut_cb (GtkAction *action,
-                                 EBookShellView *book_shell_view)
-{
-	EBookShellContent *book_shell_content;
-	EAddressbookView *view;
-
-	book_shell_content = book_shell_view->priv->book_shell_content;
-	view = e_book_shell_content_get_current_view (book_shell_content);
-	g_return_if_fail (view != NULL);
-
-	e_addressbook_view_cut (view);
-}
-
-static void
-action_contact_clipboard_paste_cb (GtkAction *action,
-                                   EBookShellView *book_shell_view)
-{
-	EBookShellContent *book_shell_content;
-	EAddressbookView *view;
-
-	book_shell_content = book_shell_view->priv->book_shell_content;
-	view = e_book_shell_content_get_current_view (book_shell_content);
-	g_return_if_fail (view != NULL);
-
-	e_addressbook_view_paste (view);
-}
-
-static void
 action_contact_copy_cb (GtkAction *action,
                         EBookShellView *book_shell_view)
 {
@@ -599,20 +561,6 @@ exit:
 }
 
 static void
-action_contact_select_all_cb (GtkAction *action,
-                              EBookShellView *book_shell_view)
-{
-	EBookShellContent *book_shell_content;
-	EAddressbookView *view;
-
-	book_shell_content = book_shell_view->priv->book_shell_content;
-	view = e_book_shell_content_get_current_view (book_shell_content);
-	g_return_if_fail (view != NULL);
-
-	e_addressbook_view_select_all (view);
-}
-
-static void
 action_contact_send_message_cb (GtkAction *action,
                                 EBookShellView *book_shell_view)
 {
@@ -758,27 +706,6 @@ static GtkActionEntry contact_entries[] = {
 	  N_("Stop loading"),
 	  G_CALLBACK (action_address_book_stop_cb) },
 
-	{ "contact-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy the selection"),
-	  G_CALLBACK (action_contact_clipboard_copy_cb) },
-
-	{ "contact-clipboard-cut",
-	  GTK_STOCK_CUT,
-	  NULL,
-	  NULL,
-	  N_("Cut the selection"),
-	  G_CALLBACK (action_contact_clipboard_cut_cb) },
-
-	{ "contact-clipboard-paste",
-	  GTK_STOCK_PASTE,
-	  NULL,
-	  NULL,
-	  N_("Paste the clipboard"),
-	  G_CALLBACK (action_contact_clipboard_paste_cb) },
-
 	{ "contact-copy",
 	  NULL,
 	  N_("_Copy Contact To..."),
@@ -828,13 +755,6 @@ static GtkActionEntry contact_entries[] = {
 	  N_("View the current contact"),
 	  G_CALLBACK (action_contact_open_cb) },
 
-	{ "contact-select-all",
-	  GTK_STOCK_SELECT_ALL,
-	  NULL,
-	  NULL,
-	  N_("Select all contacts"),
-	  G_CALLBACK (action_contact_select_all_cb) },
-
 	{ "contact-send-message",
 	  "mail-message-new",
 	  N_("_Send Message to Contact..."),
@@ -877,18 +797,6 @@ static EPopupActionEntry contact_popup_entries[] = {
 	  N_("_Save as vCard..."),
 	  "address-book-save-as" },
 
-	{ "contact-popup-clipboard-copy",
-	  NULL,
-	  "contact-clipboard-copy" },
-
-	{ "contact-popup-clipboard-cut",
-	  NULL,
-	  "contact-clipboard-cut" },
-
-	{ "contact-popup-clipboard-paste",
-	  NULL,
-	  "contact-clipboard-paste" },
-
 	{ "contact-popup-copy",
 	  NULL,
 	  "contact-copy" },
diff --git a/modules/addressbook/e-book-shell-view-actions.h b/modules/addressbook/e-book-shell-view-actions.h
index 98cd8b6..250ec5f 100644
--- a/modules/addressbook/e-book-shell-view-actions.h
+++ b/modules/addressbook/e-book-shell-view-actions.h
@@ -45,12 +45,6 @@
 	E_SHELL_WINDOW_ACTION ((window), "address-book-stop")
 
 /* Contact Actions */
-#define E_SHELL_WINDOW_ACTION_CONTACT_CLIPBOARD_COPY(window) \
-	E_SHELL_WINDOW_ACTION ((window), "contact-clipboard-copy")
-#define E_SHELL_WINDOW_ACTION_CONTACT_CLIPBOARD_CUT(window) \
-	E_SHELL_WINDOW_ACTION ((window), "contact-clipboard-cut")
-#define E_SHELL_WINDOW_ACTION_CONTACT_CLIPBOARD_PASTE(window) \
-	E_SHELL_WINDOW_ACTION ((window), "contact-clipboard-paste")
 #define E_SHELL_WINDOW_ACTION_CONTACT_COPY(window) \
 	E_SHELL_WINDOW_ACTION ((window), "contact-copy")
 #define E_SHELL_WINDOW_ACTION_CONTACT_DELETE(window) \
@@ -71,8 +65,6 @@
 	E_SHELL_WINDOW_ACTION ((window), "contact-print")
 #define E_SHELL_WINDOW_ACTION_CONTACT_SAVE_AS(window) \
 	E_SHELL_WINDOW_ACTION ((window), "contact-save-as")
-#define E_SHELL_WINDOW_ACTION_CONTACT_SELECT_ALL(window) \
-	E_SHELL_WINDOW_ACTION ((window), "contact-select-all")
 #define E_SHELL_WINDOW_ACTION_CONTACT_SEND_MESSAGE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "contact-send-message")
 #define E_SHELL_WINDOW_ACTION_CONTACT_VIEW_CLASSIC(window) \
diff --git a/modules/addressbook/e-book-shell-view.c b/modules/addressbook/e-book-shell-view.c
index fa5e5bb..9ab9cb7 100644
--- a/modules/addressbook/e-book-shell-view.c
+++ b/modules/addressbook/e-book-shell-view.c
@@ -268,8 +268,9 @@ book_shell_view_update_actions (EShellView *shell_view)
 	gboolean selection_has_email;
 	gboolean source_is_busy;
 	gboolean source_is_editable;
-	gboolean source_is_empty;
-	gboolean clipboard_has_directory;
+
+	/* Chain up to parent's update_actions() method. */
+	E_SHELL_VIEW_CLASS (parent_class)->update_actions (shell_view);
 
 	priv = E_BOOK_SHELL_VIEW_GET_PRIVATE (shell_view);
 
@@ -290,10 +291,6 @@ book_shell_view_update_actions (EShellView *shell_view)
 		(state & E_BOOK_SHELL_CONTENT_SOURCE_IS_BUSY);
 	source_is_editable =
 		(state & E_BOOK_SHELL_CONTENT_SOURCE_IS_EDITABLE);
-	source_is_empty =
-		(state & E_BOOK_SHELL_CONTENT_SOURCE_IS_EMPTY);
-	clipboard_has_directory =
-		(state & E_BOOK_SHELL_CONTENT_CLIPBOARD_HAS_DIRECTORY);
 
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	state = e_shell_sidebar_check_state (shell_sidebar);
@@ -328,18 +325,6 @@ book_shell_view_update_actions (EShellView *shell_view)
 	sensitive = source_is_busy;
 	gtk_action_set_sensitive (action, sensitive);
 
-	action = ACTION (CONTACT_CLIPBOARD_COPY);
-	sensitive = any_contacts_selected;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (CONTACT_CLIPBOARD_CUT);
-	sensitive = source_is_editable && any_contacts_selected;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (CONTACT_CLIPBOARD_PASTE);
-	sensitive = source_is_editable && clipboard_has_directory;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (CONTACT_COPY);
 	sensitive = any_contacts_selected;
 	gtk_action_set_sensitive (action, sensitive);
@@ -381,10 +366,6 @@ book_shell_view_update_actions (EShellView *shell_view)
 	sensitive = any_contacts_selected;
 	gtk_action_set_sensitive (action, sensitive);
 
-	action = ACTION (CONTACT_SELECT_ALL);
-	sensitive = !(source_is_empty);
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (CONTACT_SEND_MESSAGE);
 	sensitive = any_contacts_selected && selection_has_email;
 	gtk_action_set_sensitive (action, sensitive);
diff --git a/modules/calendar/e-cal-shell-content.c b/modules/calendar/e-cal-shell-content.c
index c3112cf..4946972 100644
--- a/modules/calendar/e-cal-shell-content.c
+++ b/modules/calendar/e-cal-shell-content.c
@@ -721,114 +721,6 @@ e_cal_shell_content_get_view_instance (ECalShellContent *cal_shell_content)
 }
 
 void
-e_cal_shell_content_copy_clipboard (ECalShellContent *cal_shell_content)
-{
-	GnomeCalendar *calendar;
-	EMemoTable *memo_table;
-	ECalendarTable *task_table;
-	GnomeCalendarViewType view_type;
-	ECalendarView *calendar_view;
-
-	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
-
-	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
-	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
-	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
-
-	view_type = gnome_calendar_get_view (calendar);
-	calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
-
-	switch (cal_shell_content_get_focus_location (cal_shell_content)) {
-		case FOCUS_CALENDAR:
-			e_calendar_view_copy_clipboard (calendar_view);
-			break;
-
-		case FOCUS_MEMO_TABLE:
-			e_memo_table_copy_clipboard (memo_table);
-			break;
-
-		case FOCUS_TASK_TABLE:
-			e_calendar_table_copy_clipboard (task_table);
-			break;
-
-		default:
-			g_return_if_reached ();
-	}
-}
-
-void
-e_cal_shell_content_cut_clipboard (ECalShellContent *cal_shell_content)
-{
-	GnomeCalendar *calendar;
-	EMemoTable *memo_table;
-	ECalendarTable *task_table;
-	GnomeCalendarViewType view_type;
-	ECalendarView *calendar_view;
-
-	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
-
-	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
-	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
-	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
-
-	view_type = gnome_calendar_get_view (calendar);
-	calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
-
-	switch (cal_shell_content_get_focus_location (cal_shell_content)) {
-		case FOCUS_CALENDAR:
-			e_calendar_view_cut_clipboard (calendar_view);
-			break;
-
-		case FOCUS_MEMO_TABLE:
-			e_memo_table_copy_clipboard (memo_table);
-			break;
-
-		case FOCUS_TASK_TABLE:
-			e_calendar_table_copy_clipboard (task_table);
-			break;
-
-		default:
-			g_return_if_reached ();
-	}
-}
-
-void
-e_cal_shell_content_paste_clipboard (ECalShellContent *cal_shell_content)
-{
-	GnomeCalendar *calendar;
-	EMemoTable *memo_table;
-	ECalendarTable *task_table;
-	GnomeCalendarViewType view_type;
-	ECalendarView *calendar_view;
-
-	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
-
-	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
-	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
-	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
-
-	view_type = gnome_calendar_get_view (calendar);
-	calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
-
-	switch (cal_shell_content_get_focus_location (cal_shell_content)) {
-		case FOCUS_CALENDAR:
-			e_calendar_view_paste_clipboard (calendar_view);
-			break;
-
-		case FOCUS_MEMO_TABLE:
-			e_memo_table_paste_clipboard (memo_table);
-			break;
-
-		case FOCUS_TASK_TABLE:
-			e_calendar_table_paste_clipboard (task_table);
-			break;
-
-		default:
-			g_return_if_reached ();
-	}
-}
-
-void
 e_cal_shell_content_delete_selection (ECalShellContent *cal_shell_content)
 {
 	GnomeCalendar *calendar;
diff --git a/modules/calendar/e-cal-shell-content.h b/modules/calendar/e-cal-shell-content.h
index eae8bf3..db82889 100644
--- a/modules/calendar/e-cal-shell-content.h
+++ b/modules/calendar/e-cal-shell-content.h
@@ -26,6 +26,7 @@
 #include <shell/e-shell-searchbar.h>
 #include <shell/e-shell-view.h>
 
+#include <calendar/gui/e-calendar-table.h>
 #include <calendar/gui/e-memo-table.h>
 #include <calendar/gui/gnome-cal.h>
 #include <menus/gal-view-instance.h>
@@ -96,12 +97,6 @@ EShellSearchbar *
 GalViewInstance *
 		e_cal_shell_content_get_view_instance
 					(ECalShellContent *cal_shell_content);
-void		e_cal_shell_content_copy_clipboard
-					(ECalShellContent *cal_shell_content);
-void		e_cal_shell_content_cut_clipboard
-					(ECalShellContent *cal_shell_content);
-void		e_cal_shell_content_paste_clipboard
-					(ECalShellContent *cal_shell_content);
 void		e_cal_shell_content_delete_selection
 					(ECalShellContent *cal_shell_content);
 void		e_cal_shell_content_delete_selected_occurrence
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
index 148cc4a..117868d 100644
--- a/modules/calendar/e-cal-shell-view-actions.c
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -482,36 +482,6 @@ action_event_all_day_new_cb (GtkAction *action,
 }
 
 static void
-action_event_clipboard_copy_cb (GtkAction *action,
-                                ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	e_cal_shell_content_copy_clipboard (cal_shell_content);
-}
-
-static void
-action_event_clipboard_cut_cb (GtkAction *action,
-                               ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	e_cal_shell_content_cut_clipboard (cal_shell_content);
-}
-
-static void
-action_event_clipboard_paste_cb (GtkAction *action,
-                                 ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	e_cal_shell_content_paste_clipboard (cal_shell_content);
-}
-
-static void
 action_event_copy_cb (GtkAction *action,
                       ECalShellView *cal_shell_view)
 {
@@ -1314,27 +1284,6 @@ static GtkActionEntry calendar_entries[] = {
 	  NULL,  /* XXX Add a tooltip! */
 	  G_CALLBACK (action_calendar_select_one_cb) },
 
-	{ "event-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy the selection"),
-	  G_CALLBACK (action_event_clipboard_copy_cb) },
-
-	{ "event-clipboard-cut",
-	  GTK_STOCK_CUT,
-	  NULL,
-	  NULL,
-	  N_("Cut the selection"),
-	  G_CALLBACK (action_event_clipboard_cut_cb) },
-
-	{ "event-clipboard-paste",
-	  GTK_STOCK_PASTE,
-	  NULL,
-	  NULL,
-	  N_("Paste the clipboard"),
-	  G_CALLBACK (action_event_clipboard_paste_cb) },
-
 	{ "event-copy",
 	  NULL,
 	  N_("Cop_y to Calendar..."),
@@ -1502,18 +1451,6 @@ static EPopupActionEntry calendar_popup_entries[] = {
 	  NULL,
 	  "calendar-select-one" },
 
-	{ "event-popup-clipboard-copy",
-	  NULL,
-	  "event-clipboard-copy" },
-
-	{ "event-popup-clipboard-cut",
-	  NULL,
-	  "event-clipboard-cut" },
-
-	{ "event-popup-clipboard-paste",
-	  NULL,
-	  "event-clipboard-paste" },
-
 	{ "event-popup-copy",
 	  NULL,
 	  "event-copy" },
diff --git a/modules/calendar/e-cal-shell-view-actions.h b/modules/calendar/e-cal-shell-view-actions.h
index a6b1c3d..a1cb313 100644
--- a/modules/calendar/e-cal-shell-view-actions.h
+++ b/modules/calendar/e-cal-shell-view-actions.h
@@ -65,12 +65,6 @@
 	E_SHELL_WINDOW_ACTION ((window), "calendar-view-workweek")
 
 /* Event Actions */
-#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_COPY(window) \
-	E_SHELL_WINDOW_ACTION ((window), "event-clipboard-copy")
-#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_CUT(window) \
-	E_SHELL_WINDOW_ACTION ((window), "event-clipboard-cut")
-#define E_SHELL_WINDOW_ACTION_EVENT_CLIPBOARD_PASTE(window) \
-	E_SHELL_WINDOW_ACTION ((window), "event-clipboard-paste")
 #define E_SHELL_WINDOW_ACTION_EVENT_DELEGATE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "event-delegate")
 #define E_SHELL_WINDOW_ACTION_EVENT_DELETE(window) \
@@ -97,12 +91,6 @@
 	E_SHELL_WINDOW_ACTION ((window), "event-occurrence-movable")
 
 /* Memo Pad Actions */
-#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_COPY(window) \
-        E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-copy")
-#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_CUT(window) \
-        E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-cut")
-#define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_CLIPBOARD_PASTE(window) \
-        E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-clipboard-paste")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_DELETE(window) \
         E_SHELL_WINDOW_ACTION ((window), "calendar-memopad-delete")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_MEMOPAD_FORWARD(window) \
@@ -121,12 +109,6 @@
 /* Task Pad Actions */
 #define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_ASSIGN(window) \
 	E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-assign")
-#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_COPY(window) \
-        E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-copy")
-#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_CUT(window) \
-        E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-cut")
-#define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_CLIPBOARD_PASTE(window) \
-        E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-clipboard-paste")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_DELETE(window) \
         E_SHELL_WINDOW_ACTION ((window), "calendar-taskpad-delete")
 #define E_SHELL_WINDOW_ACTION_CALENDAR_TASKPAD_FORWARD(window) \
diff --git a/modules/calendar/e-cal-shell-view-memopad.c b/modules/calendar/e-cal-shell-view-memopad.c
index 3a92cbc..a65bf82 100644
--- a/modules/calendar/e-cal-shell-view-memopad.c
+++ b/modules/calendar/e-cal-shell-view-memopad.c
@@ -24,45 +24,6 @@
 /* Much of this file is based on e-memo-shell-view-actions.c. */
 
 static void
-action_calendar_memopad_clipboard_copy_cb (GtkAction *action,
-                                           ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-	EMemoTable *memo_table;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
-
-	e_memo_table_copy_clipboard (memo_table);
-}
-
-static void
-action_calendar_memopad_clipboard_cut_cb (GtkAction *action,
-                                          ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-	EMemoTable *memo_table;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
-
-	e_memo_table_cut_clipboard (memo_table);
-}
-
-static void
-action_calendar_memopad_clipboard_paste_cb (GtkAction *action,
-                                            ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-	EMemoTable *memo_table;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	memo_table = e_cal_shell_content_get_memo_table (cal_shell_content);
-
-	e_memo_table_paste_clipboard (memo_table);
-}
-
-static void
 action_calendar_memopad_delete_cb (GtkAction *action,
                                    ECalShellView *cal_shell_view)
 {
@@ -292,27 +253,6 @@ action_calendar_memopad_save_as_cb (GtkAction *action,
 
 static GtkActionEntry calendar_memopad_entries[] = {
 
-	{ "calendar-memopad-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy selected memo"),
-	  G_CALLBACK (action_calendar_memopad_clipboard_copy_cb) },
-
-	{ "calendar-memopad-clipboard-cut",
-	  GTK_STOCK_CUT,
-	  NULL,
-	  NULL,
-	  N_("Cut selected memo"),
-	  G_CALLBACK (action_calendar_memopad_clipboard_cut_cb) },
-
-	{ "calendar-memopad-clipboard-paste",
-	  GTK_STOCK_PASTE,
-	  NULL,
-	  NULL,
-	  N_("Paste memo from the clipboard"),
-	  G_CALLBACK (action_calendar_memopad_clipboard_paste_cb) },
-
 	{ "calendar-memopad-delete",
 	  GTK_STOCK_DELETE,
 	  N_("_Delete Memo"),
@@ -427,18 +367,6 @@ e_cal_shell_view_memopad_actions_update (ECalShellView *cal_shell_view)
 	}
 	g_slist_free (list);
 
-	action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_COPY);
-	sensitive = (n_selected > 0);
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_CUT);
-	sensitive = (n_selected > 0) && editable;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (CALENDAR_MEMOPAD_CLIPBOARD_PASTE);
-	sensitive = editable;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (CALENDAR_MEMOPAD_DELETE);
 	sensitive = (n_selected > 0) && editable;
 	gtk_action_set_sensitive (action, sensitive);
diff --git a/modules/calendar/e-cal-shell-view-taskpad.c b/modules/calendar/e-cal-shell-view-taskpad.c
index c88dee0..f73d583 100644
--- a/modules/calendar/e-cal-shell-view-taskpad.c
+++ b/modules/calendar/e-cal-shell-view-taskpad.c
@@ -47,45 +47,6 @@ action_calendar_taskpad_assign_cb (GtkAction *action,
 }
 
 static void
-action_calendar_taskpad_clipboard_copy_cb (GtkAction *action,
-                                           ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-	ECalendarTable *task_table;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
-
-	e_calendar_table_copy_clipboard (task_table);
-}
-
-static void
-action_calendar_taskpad_clipboard_cut_cb (GtkAction *action,
-                                          ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-	ECalendarTable *task_table;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
-
-	e_calendar_table_cut_clipboard (task_table);
-}
-
-static void
-action_calendar_taskpad_clipboard_paste_cb (GtkAction *action,
-                                            ECalShellView *cal_shell_view)
-{
-	ECalShellContent *cal_shell_content;
-	ECalendarTable *task_table;
-
-	cal_shell_content = cal_shell_view->priv->cal_shell_content;
-	task_table = e_cal_shell_content_get_task_table (cal_shell_content);
-
-	e_calendar_table_paste_clipboard (task_table);
-}
-
-static void
 action_calendar_taskpad_delete_cb (GtkAction *action,
                                    ECalShellView *cal_shell_view)
 {
@@ -366,27 +327,6 @@ static GtkActionEntry calendar_taskpad_entries[] = {
 	  NULL,  /* XXX Add a tooltip! */
 	  G_CALLBACK (action_calendar_taskpad_assign_cb) },
 
-	{ "calendar-taskpad-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy selected tasks"),
-	  G_CALLBACK (action_calendar_taskpad_clipboard_copy_cb) },
-
-	{ "calendar-taskpad-clipboard-cut",
-	  GTK_STOCK_CUT,
-	  NULL,
-	  NULL,
-	  N_("Cut selected tasks"),
-	  G_CALLBACK (action_calendar_taskpad_clipboard_cut_cb) },
-
-	{ "calendar-taskpad-clipboard-paste",
-	  GTK_STOCK_PASTE,
-	  NULL,
-	  NULL,
-	  N_("Paste tasks from the clipboard"),
-	  G_CALLBACK (action_calendar_taskpad_clipboard_paste_cb) },
-
 	{ "calendar-taskpad-delete",
 	  GTK_STOCK_DELETE,
 	  N_("_Delete Task"),
@@ -538,18 +478,6 @@ e_cal_shell_view_taskpad_actions_update (ECalShellView *cal_shell_view)
 	sensitive = (n_selected == 1) && editable && assignable;
 	gtk_action_set_sensitive (action, sensitive);
 
-	action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_COPY);
-	sensitive = (n_selected > 0);
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_CUT);
-	sensitive = (n_selected > 0) && editable;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (CALENDAR_TASKPAD_CLIPBOARD_PASTE);
-	sensitive = editable;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (CALENDAR_TASKPAD_DELETE);
 	sensitive = (n_selected > 0) && editable;
 	gtk_action_set_sensitive (action, sensitive);
diff --git a/modules/calendar/e-cal-shell-view.c b/modules/calendar/e-cal-shell-view.c
index 262cd39..5b9940b 100644
--- a/modules/calendar/e-cal-shell-view.c
+++ b/modules/calendar/e-cal-shell-view.c
@@ -296,9 +296,11 @@ cal_shell_view_update_actions (EShellView *shell_view)
 	gboolean is_instance = FALSE;
 	gboolean is_meeting = FALSE;
 	gboolean is_delegatable = FALSE;
-	gboolean clipboard_has_calendar;
 	gboolean refresh_supported = FALSE;
 
+	/* Chain up to parent's update_actions() method. */
+	E_SHELL_VIEW_CLASS (parent_class)->update_actions (shell_view);
+
 	priv = E_CAL_SHELL_VIEW_GET_PRIVATE (shell_view);
 
 	shell_window = e_shell_view_get_shell_window (shell_view);
@@ -361,10 +363,6 @@ cal_shell_view_update_actions (EShellView *shell_view)
 
 	g_list_free (list);
 
-	clipboard_has_calendar =
-		e_clipboard_wait_is_calendar_available (
-		gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
-
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	state = e_shell_sidebar_check_state (shell_sidebar);
 
@@ -397,18 +395,6 @@ cal_shell_view_update_actions (EShellView *shell_view)
 	sensitive = can_delete_primary_source;
 	gtk_action_set_sensitive (action, sensitive);
 
-	action = ACTION (EVENT_CLIPBOARD_COPY);
-	sensitive = (n_selected > 0);
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (EVENT_CLIPBOARD_CUT);
-	sensitive = (n_selected > 0) && editable;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (EVENT_CLIPBOARD_PASTE);
-	sensitive = editable && clipboard_has_calendar;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (EVENT_DELEGATE);
 	sensitive =
 		(n_selected == 1) && editable &&
diff --git a/modules/calendar/e-memo-shell-content.c b/modules/calendar/e-memo-shell-content.c
index 4de685c..709ae9a 100644
--- a/modules/calendar/e-memo-shell-content.c
+++ b/modules/calendar/e-memo-shell-content.c
@@ -537,7 +537,6 @@ memo_shell_content_check_state (EShellContent *shell_content)
 	EMemoShellContent *memo_shell_content;
 	EMemoTable *memo_table;
 	GSList *list, *iter;
-	GtkClipboard *clipboard;
 	gboolean editable = TRUE;
 	gboolean has_url = FALSE;
 	gint n_selected;
@@ -563,8 +562,6 @@ memo_shell_content_check_state (EShellContent *shell_content)
 	}
 	g_slist_free (list);
 
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
 	if (n_selected == 1)
 		state |= E_MEMO_SHELL_CONTENT_SELECTION_SINGLE;
 	if (n_selected > 1)
@@ -573,8 +570,6 @@ memo_shell_content_check_state (EShellContent *shell_content)
 		state |= E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT;
 	if (has_url)
 		state |= E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL;
-	if (e_clipboard_wait_is_calendar_available (clipboard))
-		state |= E_MEMO_SHELL_CONTENT_CLIPBOARD_HAS_CALENDAR;
 
 	return state;
 }
diff --git a/modules/calendar/e-memo-shell-content.h b/modules/calendar/e-memo-shell-content.h
index a923b27..16aae22 100644
--- a/modules/calendar/e-memo-shell-content.h
+++ b/modules/calendar/e-memo-shell-content.h
@@ -60,8 +60,7 @@ enum {
 	E_MEMO_SHELL_CONTENT_SELECTION_SINGLE		= 1 << 0,
 	E_MEMO_SHELL_CONTENT_SELECTION_MULTIPLE		= 1 << 1,
 	E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT		= 1 << 2,
-	E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL		= 1 << 3,
-	E_MEMO_SHELL_CONTENT_CLIPBOARD_HAS_CALENDAR	= 1 << 4
+	E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL		= 1 << 3
 };
 
 struct _EMemoShellContent {
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
index baf90f5..7a0fdaf 100644
--- a/modules/calendar/e-memo-shell-view-actions.c
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -43,45 +43,6 @@ action_gal_save_custom_view_cb (GtkAction *action,
 }
 
 static void
-action_memo_clipboard_copy_cb (GtkAction *action,
-                               EMemoShellView *memo_shell_view)
-{
-	EMemoShellContent *memo_shell_content;
-	EMemoTable *memo_table;
-
-	memo_shell_content = memo_shell_view->priv->memo_shell_content;
-	memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
-
-	e_memo_table_copy_clipboard (memo_table);
-}
-
-static void
-action_memo_clipboard_cut_cb (GtkAction *action,
-                              EMemoShellView *memo_shell_view)
-{
-	EMemoShellContent *memo_shell_content;
-	EMemoTable *memo_table;
-
-	memo_shell_content = memo_shell_view->priv->memo_shell_content;
-	memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
-
-	e_memo_table_cut_clipboard (memo_table);
-}
-
-static void
-action_memo_clipboard_paste_cb (GtkAction *action,
-                                EMemoShellView *memo_shell_view)
-{
-	EMemoShellContent *memo_shell_content;
-	EMemoTable *memo_table;
-
-	memo_shell_content = memo_shell_view->priv->memo_shell_content;
-	memo_table = e_memo_shell_content_get_memo_table (memo_shell_content);
-
-	e_memo_table_paste_clipboard (memo_table);
-}
-
-static void
 action_memo_delete_cb (GtkAction *action,
                        EMemoShellView *memo_shell_view)
 {
@@ -590,27 +551,6 @@ action_search_filter_cb (GtkRadioAction *action,
 
 static GtkActionEntry memo_entries[] = {
 
-	{ "memo-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy selected memo"),
-	  G_CALLBACK (action_memo_clipboard_copy_cb) },
-
-	{ "memo-clipboard-cut",
-	  GTK_STOCK_CUT,
-	  NULL,
-	  NULL,
-	  N_("Cut selected memo"),
-	  G_CALLBACK (action_memo_clipboard_cut_cb) },
-
-	{ "memo-clipboard-paste",
-	  GTK_STOCK_PASTE,
-	  NULL,
-	  NULL,
-	  N_("Paste memo from the clipboard"),
-	  G_CALLBACK (action_memo_clipboard_paste_cb) },
-
 	{ "memo-delete",
 	  GTK_STOCK_DELETE,
 	  N_("_Delete Memo"),
@@ -731,18 +671,6 @@ static EPopupActionEntry memo_popup_entries[] = {
 	  NULL,
 	  "memo-list-select-one" },
 
-	{ "memo-popup-clipboard-copy",
-	  NULL,
-	  "memo-clipboard-copy" },
-
-	{ "memo-popup-clipboard-cut",
-	  NULL,
-	  "memo-clipboard-cut" },
-
-	{ "memo-popup-clipboard-paste",
-	  NULL,
-	  "memo-clipboard-paste" },
-
 	{ "memo-popup-delete",
 	  NULL,
 	  "memo-delete" },
diff --git a/modules/calendar/e-memo-shell-view-actions.h b/modules/calendar/e-memo-shell-view-actions.h
index cb4309c..5b49ca0 100644
--- a/modules/calendar/e-memo-shell-view-actions.h
+++ b/modules/calendar/e-memo-shell-view-actions.h
@@ -25,12 +25,6 @@
 #include <shell/e-shell-window-actions.h>
 
 /* Memo Actions */
-#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_COPY(window) \
-	E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-copy")
-#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_CUT(window) \
-	E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-cut")
-#define E_SHELL_WINDOW_ACTION_MEMO_CLIPBOARD_PASTE(window) \
-	E_SHELL_WINDOW_ACTION ((window), "memo-clipboard-paste")
 #define E_SHELL_WINDOW_ACTION_MEMO_DELETE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "memo-delete")
 #define E_SHELL_WINDOW_ACTION_MEMO_FORWARD(window) \
diff --git a/modules/calendar/e-memo-shell-view.c b/modules/calendar/e-memo-shell-view.c
index 29a8252..0afbe18 100644
--- a/modules/calendar/e-memo-shell-view.c
+++ b/modules/calendar/e-memo-shell-view.c
@@ -175,7 +175,6 @@ memo_shell_view_update_actions (EShellView *shell_view)
 	/* Be descriptive. */
 	gboolean any_memos_selected;
 	gboolean can_delete_primary_source;
-	gboolean clipboard_has_calendar;
 	gboolean has_primary_source;
 	gboolean multiple_memos_selected;
 	gboolean primary_source_is_system;
@@ -184,6 +183,9 @@ memo_shell_view_update_actions (EShellView *shell_view)
 	gboolean sources_are_editable;
 	gboolean refresh_supported;
 
+	/* Chain up to parent's update_actions() method. */
+	E_SHELL_VIEW_CLASS (parent_class)->update_actions (shell_view);
+
 	priv = E_MEMO_SHELL_VIEW_GET_PRIVATE (shell_view);
 
 	shell_window = e_shell_view_get_shell_window (shell_view);
@@ -199,8 +201,6 @@ memo_shell_view_update_actions (EShellView *shell_view)
 		(state & E_MEMO_SHELL_CONTENT_SELECTION_CAN_EDIT);
 	selection_has_url =
 		(state & E_MEMO_SHELL_CONTENT_SELECTION_HAS_URL);
-	clipboard_has_calendar =
-		(state & E_MEMO_SHELL_CONTENT_CLIPBOARD_HAS_CALENDAR);
 
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	state = e_shell_sidebar_check_state (shell_sidebar);
@@ -217,18 +217,6 @@ memo_shell_view_update_actions (EShellView *shell_view)
 	any_memos_selected =
 		(single_memo_selected || multiple_memos_selected);
 
-	action = ACTION (MEMO_CLIPBOARD_COPY);
-	sensitive = any_memos_selected;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (MEMO_CLIPBOARD_CUT);
-	sensitive = any_memos_selected && sources_are_editable;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (MEMO_CLIPBOARD_PASTE);
-	sensitive = sources_are_editable && clipboard_has_calendar;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (MEMO_DELETE);
 	sensitive = any_memos_selected && sources_are_editable;
 	gtk_action_set_sensitive (action, sensitive);
diff --git a/modules/calendar/e-task-shell-content.c b/modules/calendar/e-task-shell-content.c
index 4692e7b..e50b67b 100644
--- a/modules/calendar/e-task-shell-content.c
+++ b/modules/calendar/e-task-shell-content.c
@@ -535,7 +535,6 @@ task_shell_content_check_state (EShellContent *shell_content)
 	ETaskShellContent *task_shell_content;
 	ECalendarTable *task_table;
 	GSList *list, *iter;
-	GtkClipboard *clipboard;
 	gboolean assignable = TRUE;
 	gboolean editable = TRUE;
 	gboolean has_url = FALSE;
@@ -580,8 +579,6 @@ task_shell_content_check_state (EShellContent *shell_content)
 	}
 	g_slist_free (list);
 
-	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
 	if (n_selected == 1)
 		state |= E_TASK_SHELL_CONTENT_SELECTION_SINGLE;
 	if (n_selected > 1)
@@ -596,8 +593,6 @@ task_shell_content_check_state (EShellContent *shell_content)
 		state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE;
 	if (has_url)
 		state |= E_TASK_SHELL_CONTENT_SELECTION_HAS_URL;
-	if (e_clipboard_wait_is_calendar_available (clipboard))
-		state |= E_TASK_SHELL_CONTENT_CLIPBOARD_HAS_CALENDAR;
 
 	return state;
 }
diff --git a/modules/calendar/e-task-shell-content.h b/modules/calendar/e-task-shell-content.h
index 971d46a..7a5e8ef 100644
--- a/modules/calendar/e-task-shell-content.h
+++ b/modules/calendar/e-task-shell-content.h
@@ -64,8 +64,7 @@ enum {
 	E_TASK_SHELL_CONTENT_SELECTION_CAN_EDIT		= 1 << 3,
 	E_TASK_SHELL_CONTENT_SELECTION_HAS_COMPLETE	= 1 << 4,
 	E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE	= 1 << 5,
-	E_TASK_SHELL_CONTENT_SELECTION_HAS_URL		= 1 << 6,
-	E_TASK_SHELL_CONTENT_CLIPBOARD_HAS_CALENDAR	= 1 << 7
+	E_TASK_SHELL_CONTENT_SELECTION_HAS_URL		= 1 << 6
 };
 
 struct _ETaskShellContent {
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
index 8e28b55..067bd48 100644
--- a/modules/calendar/e-task-shell-view-actions.c
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -74,45 +74,6 @@ action_task_assign_cb (GtkAction *action,
 }
 
 static void
-action_task_clipboard_copy_cb (GtkAction *action,
-                               ETaskShellView *task_shell_view)
-{
-	ETaskShellContent *task_shell_content;
-	ECalendarTable *task_table;
-
-	task_shell_content = task_shell_view->priv->task_shell_content;
-	task_table = e_task_shell_content_get_task_table (task_shell_content);
-
-	e_calendar_table_copy_clipboard (task_table);
-}
-
-static void
-action_task_clipboard_cut_cb (GtkAction *action,
-                              ETaskShellView *task_shell_view)
-{
-	ETaskShellContent *task_shell_content;
-	ECalendarTable *task_table;
-
-	task_shell_content = task_shell_view->priv->task_shell_content;
-	task_table = e_task_shell_content_get_task_table (task_shell_content);
-
-	e_calendar_table_cut_clipboard (task_table);
-}
-
-static void
-action_task_clipboard_paste_cb (GtkAction *action,
-                                ETaskShellView *task_shell_view)
-{
-	ETaskShellContent *task_shell_content;
-	ECalendarTable *task_table;
-
-	task_shell_content = task_shell_view->priv->task_shell_content;
-	task_table = e_task_shell_content_get_task_table (task_shell_content);
-
-	e_calendar_table_paste_clipboard (task_table);
-}
-
-static void
 action_task_delete_cb (GtkAction *action,
                        ETaskShellView *task_shell_view)
 {
@@ -714,27 +675,6 @@ static GtkActionEntry task_entries[] = {
 	  NULL,  /* XXX Add a tooltip! */
 	  G_CALLBACK (action_task_assign_cb) },
 
-	{ "task-clipboard-copy",
-	  GTK_STOCK_COPY,
-	  NULL,
-	  NULL,
-	  N_("Copy selected tasks"),
-	  G_CALLBACK (action_task_clipboard_copy_cb) },
-
-	{ "task-clipboard-cut",
-	  GTK_STOCK_CUT,
-	  NULL,
-	  NULL,
-	  N_("Cut selected tasks"),
-	  G_CALLBACK (action_task_clipboard_cut_cb) },
-
-	{ "task-clipboard-paste",
-	  GTK_STOCK_PASTE,
-	  NULL,
-	  NULL,
-	  N_("Paste tasks from the clipboard"),
-	  G_CALLBACK (action_task_clipboard_paste_cb) },
-
 	{ "task-delete",
 	  GTK_STOCK_DELETE,
 	  N_("_Delete Task"),
@@ -887,18 +827,6 @@ static EPopupActionEntry task_popup_entries[] = {
 	  NULL,
 	  "task-assign" },
 
-	{ "task-popup-clipboard-copy",
-	  NULL,
-	  "task-clipboard-copy" },
-
-	{ "task-popup-clipboard-cut",
-	  NULL,
-	  "task-clipboard-cut" },
-
-	{ "task-popup-clipboard-paste",
-	  NULL,
-	  "task-clipboard-paste" },
-
 	{ "task-popup-delete",
 	  NULL,
 	  "task-delete" },
diff --git a/modules/calendar/e-task-shell-view-actions.h b/modules/calendar/e-task-shell-view-actions.h
index 3d49ff6..f323fb3 100644
--- a/modules/calendar/e-task-shell-view-actions.h
+++ b/modules/calendar/e-task-shell-view-actions.h
@@ -27,12 +27,6 @@
 /* Task Actions */
 #define E_SHELL_WINDOW_ACTION_TASK_ASSIGN(window) \
 	E_SHELL_WINDOW_ACTION ((window), "task-assign")
-#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_COPY(window) \
-	E_SHELL_WINDOW_ACTION ((window), "task-clipboard-copy")
-#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_CUT(window) \
-	E_SHELL_WINDOW_ACTION ((window), "task-clipboard-cut")
-#define E_SHELL_WINDOW_ACTION_TASK_CLIPBOARD_PASTE(window) \
-	E_SHELL_WINDOW_ACTION ((window), "task-clipboard-paste")
 #define E_SHELL_WINDOW_ACTION_TASK_DELETE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "task-delete")
 #define E_SHELL_WINDOW_ACTION_TASK_FORWARD(window) \
diff --git a/modules/calendar/e-task-shell-view.c b/modules/calendar/e-task-shell-view.c
index a481356..07fb12e 100644
--- a/modules/calendar/e-task-shell-view.c
+++ b/modules/calendar/e-task-shell-view.c
@@ -297,9 +297,11 @@ task_shell_view_update_actions (EShellView *shell_view)
 	gboolean some_tasks_complete;
 	gboolean some_tasks_incomplete;
 	gboolean sources_are_editable;
-	gboolean clipboard_has_calendar;
 	gboolean refresh_supported;
 
+	/* Chain up to parent's update_actions() method. */
+	E_SHELL_VIEW_CLASS (parent_class)->update_actions (shell_view);
+
 	priv = E_TASK_SHELL_VIEW_GET_PRIVATE (shell_view);
 
 	shell_window = e_shell_view_get_shell_window (shell_view);
@@ -321,8 +323,6 @@ task_shell_view_update_actions (EShellView *shell_view)
 		(state & E_TASK_SHELL_CONTENT_SELECTION_HAS_INCOMPLETE);
 	selection_has_url =
 		(state & E_TASK_SHELL_CONTENT_SELECTION_HAS_URL);
-	clipboard_has_calendar =
-		(state & E_TASK_SHELL_CONTENT_CLIPBOARD_HAS_CALENDAR);
 
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	state = e_shell_sidebar_check_state (shell_sidebar);
@@ -345,18 +345,6 @@ task_shell_view_update_actions (EShellView *shell_view)
 		selection_is_assignable;
 	gtk_action_set_sensitive (action, sensitive);
 
-	action = ACTION (TASK_CLIPBOARD_COPY);
-	sensitive = any_tasks_selected;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (TASK_CLIPBOARD_CUT);
-	sensitive = any_tasks_selected && sources_are_editable;
-	gtk_action_set_sensitive (action, sensitive);
-
-	action = ACTION (TASK_CLIPBOARD_PASTE);
-	sensitive = sources_are_editable && clipboard_has_calendar;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (TASK_DELETE);
 	sensitive = any_tasks_selected && sources_are_editable;
 	gtk_action_set_sensitive (action, sensitive);
diff --git a/modules/mail/e-mail-shell-view-actions.c b/modules/mail/e-mail-shell-view-actions.c
index 2ab36d4..139dd94 100644
--- a/modules/mail/e-mail-shell-view-actions.c
+++ b/modules/mail/e-mail-shell-view-actions.c
@@ -353,48 +353,6 @@ action_mail_folder_rename_cb (GtkAction *action,
 	em_folder_tree_edit_selected (folder_tree);
 }
 
-/* Helper for action_mail_folder_select_all_cb() */
-static gboolean
-action_mail_folder_select_all_timeout_cb (GtkWidget *message_list)
-{
-	message_list_select_all (MESSAGE_LIST (message_list));
-	gtk_widget_grab_focus (message_list);
-
-	return FALSE;
-}
-
-static void
-action_mail_folder_select_all_cb (GtkAction *action,
-                                  EMailShellView *mail_shell_view)
-{
-	EMailReader *reader;
-	GtkWidget *message_list;
-	EShellWindow *shell_window;
-	EShellView *shell_view;
-
-	shell_view = E_SHELL_VIEW (mail_shell_view);
-	shell_window = e_shell_view_get_shell_window (shell_view);
-
-	reader = E_MAIL_READER (mail_shell_view->priv->mail_shell_content);
-	message_list = e_mail_reader_get_message_list (reader);
-
-	if (MESSAGE_LIST (message_list)->threaded) {
-		gtk_action_activate (ACTION (MAIL_THREADS_EXPAND_ALL));
-
-		/* XXX The timeout below is added so that the execution
-		 *     thread to expand all conversation threads would
-		 *     have completed.  The timeout 505 is just to ensure
-		 *     that the value is a small delta more than the
-		 *     timeout value in mail_regen_list(). */
-		g_timeout_add (
-			505, (GSourceFunc)
-			action_mail_folder_select_all_timeout_cb,
-			message_list);
-	} else
-		/* If there is no threading, just select all immediately. */
-		action_mail_folder_select_all_timeout_cb (message_list);
-}
-
 static void
 action_mail_folder_select_thread_cb (GtkAction *action,
                                      EMailShellView *mail_shell_view)
@@ -1059,13 +1017,6 @@ static GtkActionEntry mail_entries[] = {
 	  N_("Change the name of this folder"),
 	  G_CALLBACK (action_mail_folder_rename_cb) },
 
-	{ "mail-folder-select-all",
-	  NULL,
-	  N_("Select _All Messages"),
-	  "<Control>a",
-	  N_("Select all visible messages"),
-	  G_CALLBACK (action_mail_folder_select_all_cb) },
-
 	{ "mail-folder-select-thread",
 	  NULL,
 	  N_("Select Message _Thread"),
diff --git a/modules/mail/e-mail-shell-view-private.c b/modules/mail/e-mail-shell-view-private.c
index b198b69..85395fd 100644
--- a/modules/mail/e-mail-shell-view-private.c
+++ b/modules/mail/e-mail-shell-view-private.c
@@ -443,6 +443,7 @@ e_mail_shell_view_private_constructed (EMailShellView *mail_shell_view)
 	shell_content = e_shell_view_get_shell_content (shell_view);
 	shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
 	shell_window = e_shell_view_get_shell_window (shell_view);
+
 	ui_manager = e_shell_window_get_ui_manager (shell_window);
 
 	shell = e_shell_window_get_shell (shell_window);
diff --git a/modules/mail/e-mail-shell-view.c b/modules/mail/e-mail-shell-view.c
index de81e13..bd0b3cb 100644
--- a/modules/mail/e-mail-shell-view.c
+++ b/modules/mail/e-mail-shell-view.c
@@ -440,6 +440,9 @@ mail_shell_view_update_actions (EShellView *shell_view)
 	gboolean folder_has_unread_rec = FALSE;
 	gboolean folder_tree_and_message_list_agree = TRUE;
 
+	/* Chain up to parent's update_actions() method. */
+	E_SHELL_VIEW_CLASS (parent_class)->update_actions (shell_view);
+
 	mail_shell_view = E_MAIL_SHELL_VIEW (shell_view);
 
 	shell_window = e_shell_view_get_shell_window (shell_view);
@@ -554,10 +557,6 @@ mail_shell_view_update_actions (EShellView *shell_view)
 		folder_tree_and_message_list_agree;
 	gtk_action_set_sensitive (action, sensitive);
 
-	action = ACTION (MAIL_FOLDER_SELECT_ALL);
-	sensitive = !folder_is_store;
-	gtk_action_set_sensitive (action, sensitive);
-
 	action = ACTION (MAIL_FOLDER_SELECT_THREAD);
 	sensitive = !folder_is_store;
 	gtk_action_set_sensitive (action, sensitive);
diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c
index d87f8b7..fdffe92 100644
--- a/shell/e-shell-view.c
+++ b/shell/e-shell-view.c
@@ -507,21 +507,6 @@ shell_view_constructed (GObject *object)
 }
 
 static void
-shell_view_clear_search (EShellView *shell_view)
-{
-	e_shell_view_set_search_rule (shell_view, NULL);
-	e_shell_view_execute_search (shell_view);
-}
-
-static void
-shell_view_custom_search (EShellView *shell_view,
-                          EFilterRule *custom_rule)
-{
-	e_shell_view_set_search_rule (shell_view, custom_rule);
-	e_shell_view_execute_search (shell_view);
-}
-
-static void
 shell_view_toggled (EShellView *shell_view)
 {
 	EShellViewPrivate *priv = shell_view->priv;
@@ -553,6 +538,33 @@ shell_view_toggled (EShellView *shell_view)
 }
 
 static void
+shell_view_clear_search (EShellView *shell_view)
+{
+	e_shell_view_set_search_rule (shell_view, NULL);
+	e_shell_view_execute_search (shell_view);
+}
+
+static void
+shell_view_custom_search (EShellView *shell_view,
+                          EFilterRule *custom_rule)
+{
+	e_shell_view_set_search_rule (shell_view, custom_rule);
+	e_shell_view_execute_search (shell_view);
+}
+
+static void
+shell_view_update_actions (EShellView *shell_view)
+{
+	EShellWindow *shell_window;
+	EFocusTracker *focus_tracker;
+
+	shell_window = e_shell_view_get_shell_window (shell_view);
+	focus_tracker = e_shell_window_get_focus_tracker (shell_window);
+
+	e_focus_tracker_update_actions (focus_tracker);
+}
+
+static void
 shell_view_class_init (EShellViewClass *class)
 {
 	GObjectClass *object_class;
@@ -574,9 +586,10 @@ shell_view_class_init (EShellViewClass *class)
 	class->new_shell_sidebar = e_shell_sidebar_new;
 	class->new_shell_taskbar = e_shell_taskbar_new;
 
+	class->toggled = shell_view_toggled;
 	class->clear_search = shell_view_clear_search;
 	class->custom_search = shell_view_custom_search;
-	class->toggled = shell_view_toggled;
+	class->update_actions = shell_view_update_actions;
 
 	/**
 	 * EShellView:action:
diff --git a/shell/e-shell-window-actions.c b/shell/e-shell-window-actions.c
index 4483347..5cdf2a9 100644
--- a/shell/e-shell-window-actions.c
+++ b/shell/e-shell-window-actions.c
@@ -1437,6 +1437,27 @@ static GtkActionEntry shell_entries[] = {
 	  N_("Open the Evolution User Guide"),
 	  G_CALLBACK (action_contents_cb) },
 
+	{ "copy-clipboard",
+	  GTK_STOCK_COPY,
+	  NULL,
+	  NULL,
+	  N_("Copy the selection"),
+	  NULL },  /* Handled by EFocusTracker */
+
+	{ "cut-clipboard",
+	  GTK_STOCK_CUT,
+	  NULL,
+	  NULL,
+	  N_("Cut the selection"),
+	  NULL },  /* Handled by EFocusTracker */
+
+	{ "delete-selection",
+	  GTK_STOCK_DELETE,
+	  NULL,
+	  NULL,
+	  N_("Delete the selection"),
+	  NULL },  /* Handled by EFocusTracker */
+
 	{ "faq",
 	  "help-faq",
 	  N_("Evolution _FAQ"),
@@ -1465,6 +1486,13 @@ static GtkActionEntry shell_entries[] = {
 	  N_("Create a new window displaying this view"),
 	  G_CALLBACK (action_new_window_cb) },
 
+	{ "paste-clipboard",
+	  GTK_STOCK_PASTE,
+	  NULL,
+	  NULL,
+	  N_("Paste the clipboard"),
+	  NULL },  /* Handled by EFocusTracker */
+
 	{ "preferences",
 	  GTK_STOCK_PREFERENCES,
 	  NULL,
@@ -1528,6 +1556,13 @@ static GtkActionEntry shell_entries[] = {
 	  N_("Save the current search parameters"),
 	  G_CALLBACK (action_search_save_cb) },
 
+	{ "select-all",
+	  GTK_STOCK_SELECT_ALL,
+	  NULL,
+	  "<Control>a",
+	  N_("Select all text"),
+	  NULL },  /* Handled by EFocusTracker */
+
 	{ "send-receive",
 	  "mail-send-receive",
 	  N_("Send / _Receive"),
@@ -1629,6 +1664,25 @@ static GtkActionEntry shell_entries[] = {
 	  NULL }
 };
 
+static EPopupActionEntry shell_popup_entries[] = {
+
+	{ "popup-copy-clipboard",
+	  NULL,
+	  "copy-clipboard" },
+
+	{ "popup-cut-clipboard",
+	  NULL,
+	  "cut-clipboard" },
+
+	{ "popup-delete-selection",
+	  NULL,
+	  "delete-selection" },
+
+	{ "popup-paste-clipboard",
+	  NULL,
+	  "paste-clipboard" }
+};
+
 static GtkToggleActionEntry shell_toggle_entries[] = {
 
 	{ "show-sidebar",
@@ -1811,6 +1865,7 @@ void
 e_shell_window_actions_init (EShellWindow *shell_window)
 {
 	GtkActionGroup *action_group;
+	EFocusTracker *focus_tracker;
 	GtkUIManager *ui_manager;
 
 	g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
@@ -1824,6 +1879,9 @@ e_shell_window_actions_init (EShellWindow *shell_window)
 	gtk_action_group_add_actions (
 		action_group, shell_entries,
 		G_N_ELEMENTS (shell_entries), shell_window);
+	e_action_group_add_popup_actions (
+		action_group, shell_popup_entries,
+		G_N_ELEMENTS (shell_popup_entries));
 	gtk_action_group_add_toggle_actions (
 		action_group, shell_toggle_entries,
 		G_N_ELEMENTS (shell_toggle_entries), shell_window);
@@ -1854,6 +1912,21 @@ e_shell_window_actions_init (EShellWindow *shell_window)
 		G_N_ELEMENTS (shell_lockdown_print_setup_entries),
 		shell_window);
 
+	/* Configure an EFocusTracker to manage selection actions. */
+
+	focus_tracker = e_focus_tracker_new (GTK_WINDOW (shell_window));
+	e_focus_tracker_set_cut_clipboard_action (
+		focus_tracker, ACTION (CUT_CLIPBOARD));
+	e_focus_tracker_set_copy_clipboard_action (
+		focus_tracker, ACTION (COPY_CLIPBOARD));
+	e_focus_tracker_set_paste_clipboard_action (
+		focus_tracker, ACTION (PASTE_CLIPBOARD));
+	e_focus_tracker_set_delete_selection_action (
+		focus_tracker, ACTION (DELETE_SELECTION));
+	e_focus_tracker_set_select_all_action (
+		focus_tracker, ACTION (SELECT_ALL));
+	shell_window->priv->focus_tracker = focus_tracker;
+
 	/* Fine tuning. */
 
 	gtk_action_set_sensitive (ACTION (SEARCH_QUICK), FALSE);
diff --git a/shell/e-shell-window-actions.h b/shell/e-shell-window-actions.h
index 8636b69..4833ad0 100644
--- a/shell/e-shell-window-actions.h
+++ b/shell/e-shell-window-actions.h
@@ -35,6 +35,12 @@
 	E_SHELL_WINDOW_ACTION ((window), "close")
 #define E_SHELL_WINDOW_ACTION_CONTENTS(window) \
 	E_SHELL_WINDOW_ACTION ((window), "contents")
+#define E_SHELL_WINDOW_ACTION_COPY_CLIPBOARD(window) \
+	E_SHELL_WINDOW_ACTION ((window), "copy-clipboard")
+#define E_SHELL_WINDOW_ACTION_CUT_CLIPBOARD(window) \
+	E_SHELL_WINDOW_ACTION ((window), "cut-clipboard")
+#define E_SHELL_WINDOW_ACTION_DELETE_SELECTION(window) \
+	E_SHELL_WINDOW_ACTION ((window), "delete-selection")
 #define E_SHELL_WINDOW_ACTION_FAQ(window) \
 	E_SHELL_WINDOW_ACTION ((window), "faq")
 #define E_SHELL_WINDOW_ACTION_FORGET_PASSWORDS(window) \
@@ -51,6 +57,8 @@
 	E_SHELL_WINDOW_ACTION ((window), "new-window")
 #define E_SHELL_WINDOW_ACTION_PAGE_SETUP(window) \
 	E_SHELL_WINDOW_ACTION ((window), "page-setup")
+#define E_SHELL_WINDOW_ACTION_PASTE_CLIPBOARD(window) \
+	E_SHELL_WINDOW_ACTION ((window), "paste-clipboard")
 #define E_SHELL_WINDOW_ACTION_PREFERENCES(window) \
 	E_SHELL_WINDOW_ACTION ((window), "preferences")
 #define E_SHELL_WINDOW_ACTION_QUICK_REFERENCE(window) \
@@ -69,6 +77,8 @@
 	E_SHELL_WINDOW_ACTION ((window), "search-quick")
 #define E_SHELL_WINDOW_ACTION_SEARCH_SAVE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "search-save")
+#define E_SHELL_WINDOW_ACTION_SELECT_ALL(window) \
+	E_SHELL_WINDOW_ACTION ((window), "select-all")
 #define E_SHELL_WINDOW_ACTION_SEND_RECEIVE(window) \
 	E_SHELL_WINDOW_ACTION ((window), "send-receive")
 #define E_SHELL_WINDOW_ACTION_SHOW_SIDEBAR(window) \
diff --git a/shell/e-shell-window-private.c b/shell/e-shell-window-private.c
index 0d9dda5..a2805f4 100644
--- a/shell/e-shell-window-private.c
+++ b/shell/e-shell-window-private.c
@@ -473,6 +473,7 @@ e_shell_window_private_dispose (EShellWindow *shell_window)
 		priv->shell = NULL;
 	}
 
+	DISPOSE (priv->focus_tracker);
 	DISPOSE (priv->ui_manager);
 
 	g_hash_table_remove_all (priv->loaded_views);
diff --git a/shell/e-shell-window-private.h b/shell/e-shell-window-private.h
index 635b6b6..87e1864 100644
--- a/shell/e-shell-window-private.h
+++ b/shell/e-shell-window-private.h
@@ -34,6 +34,7 @@
 #include <widgets/misc/e-import-assistant.h>
 #include <widgets/misc/e-menu-tool-button.h>
 #include <widgets/misc/e-online-button.h>
+#include <widgets/misc/e-popup-action.h>
 
 #include <e-shell.h>
 #include <e-shell-content.h>
@@ -70,6 +71,7 @@ struct _EShellWindowPrivate {
 
 	/*** UI Management ***/
 
+	EFocusTracker *focus_tracker;
 	GtkUIManager *ui_manager;
 	guint custom_rule_merge_id;
 	guint gal_view_merge_id;
diff --git a/shell/e-shell-window.c b/shell/e-shell-window.c
index 25b56ca..d3d6107 100644
--- a/shell/e-shell-window.c
+++ b/shell/e-shell-window.c
@@ -29,6 +29,7 @@
 enum {
 	PROP_0,
 	PROP_ACTIVE_VIEW,
+	PROP_FOCUS_TRACKER,
 	PROP_GEOMETRY,
 	PROP_SAFE_MODE,
 	PROP_SHELL,
@@ -234,6 +235,12 @@ shell_window_get_property (GObject *object,
 				E_SHELL_WINDOW (object)));
 			return;
 
+		case PROP_FOCUS_TRACKER:
+			g_value_set_object (
+				value, e_shell_window_get_focus_tracker (
+				E_SHELL_WINDOW (object)));
+			return;
+
 		case PROP_SAFE_MODE:
 			g_value_set_boolean (
 				value, e_shell_window_get_safe_mode (
@@ -609,6 +616,21 @@ shell_window_class_init (EShellWindowClass *class)
 			G_PARAM_READWRITE));
 
 	/**
+	 * EShellWindow:focus-tracker
+	 *
+	 * The shell window's #EFocusTracker.
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_FOCUS_TRACKER,
+		g_param_spec_object (
+			"focus-tracker",
+			_("Focus Tracker"),
+			_("The shell window's EFocusTracker"),
+			E_TYPE_FOCUS_TRACKER,
+			G_PARAM_READABLE));
+
+	/**
 	 * EShellWindow:geometry
 	 *
 	 * User-specified initial window geometry string.
@@ -886,6 +908,24 @@ e_shell_window_get_shell_view_action (EShellWindow *shell_window,
 }
 
 /**
+ * e_shell_window_get_focus_tracker:
+ * @shell_window: an #EShellWindow
+ *
+ * Returns @shell_window<!-- -->'s focus tracker, which directs
+ * Cut, Copy, Paste and Select All main menu activations to the
+ * appropriate editable or selectable widget.
+ *
+ * Returns: the #EFocusTracker for @shell_window
+ **/
+EFocusTracker *
+e_shell_window_get_focus_tracker (EShellWindow *shell_window)
+{
+	g_return_val_if_fail (E_IS_SHELL_WINDOW (shell_window), NULL);
+
+	return shell_window->priv->focus_tracker;
+}
+
+/**
  * e_shell_window_get_ui_manager:
  * @shell_window: an #EShellWindow
  *
diff --git a/shell/e-shell-window.h b/shell/e-shell-window.h
index bb88003..4ac76ba 100644
--- a/shell/e-shell-window.h
+++ b/shell/e-shell-window.h
@@ -29,6 +29,7 @@
 #define E_SHELL_WINDOW_H
 
 #include <shell/e-shell.h>
+#include <misc/e-focus-tracker.h>
 
 /* Standard GObject macros */
 #define E_TYPE_SHELL_WINDOW \
@@ -94,6 +95,7 @@ struct _EShellView *
 GtkAction *	e_shell_window_get_shell_view_action
 						(EShellWindow *shell_window,
 						 const gchar *view_name);
+EFocusTracker *	e_shell_window_get_focus_tracker(EShellWindow *shell_window);
 GtkUIManager *	e_shell_window_get_ui_manager	(EShellWindow *shell_window);
 GtkAction *	e_shell_window_get_action	(EShellWindow *shell_window,
 						 const gchar *action_name);
diff --git a/ui/evolution-calendars.ui b/ui/evolution-calendars.ui
index 8bff0fc..818ed17 100644
--- a/ui/evolution-calendars.ui
+++ b/ui/evolution-calendars.ui
@@ -11,10 +11,6 @@
     </menu>
     <menu action='edit-menu'>
       <placeholder name='edit-actions'>
-        <menuitem action='event-clipboard-cut'/>
-        <menuitem action='event-clipboard-copy'/>
-        <menuitem action='event-clipboard-paste'/>
-        <separator/>
         <menuitem action='event-delete'/>
         <menuitem action='event-delete-occurrence'/>
         <menuitem action='event-delete-occurrence-all'/>
@@ -66,7 +62,7 @@
     <separator/>
     <menuitem action='event-popup-print'/>
     <separator/>
-    <menuitem action='event-popup-clipboard-paste'/>
+    <menuitem action='popup-paste-clipboard'/>
     <separator/>
     <menu action='gal-view-menu'>
       <menuitem action='calendar-view-day'/>
@@ -85,9 +81,9 @@
     <menuitem action='event-popup-save-as'/>
     <menuitem action='event-popup-print'/>
     <separator/>
-    <menuitem action='event-popup-clipboard-cut'/>
-    <menuitem action='event-popup-clipboard-copy'/>
-    <menuitem action='event-popup-clipboard-paste'/>
+    <menuitem action='popup-cut-clipboard'/>
+    <menuitem action='popup-copy-clipboard'/>
+    <menuitem action='popup-paste-clipboard'/>
     <separator/>
     <menuitem action='event-popup-copy'/>
     <menuitem action='event-popup-move'/>
@@ -113,9 +109,9 @@
     <menuitem action='calendar-memopad-save-as'/>
     <menuitem action='calendar-memopad-print'/>
     <separator/>
-    <menuitem action='calendar-memopad-clipboard-cut'/>
-    <menuitem action='calendar-memopad-clipboard-copy'/>
-    <menuitem action='calendar-memopad-clipboard-paste'/>
+    <menuitem action='popup-cut-clipboard'/>
+    <menuitem action='popup-copy-clipboard'/>
+    <menuitem action='popup-paste-clipboard'/>
     <separator/>
     <menuitem action='calendar-memopad-forward'/>
     <separator/>
@@ -129,9 +125,9 @@
     <menuitem action='calendar-taskpad-save-as'/>
     <menuitem action='calendar-taskpad-print'/>
     <separator/>
-    <menuitem action='calendar-taskpad-clipboard-cut'/>
-    <menuitem action='calendar-taskpad-clipboard-copy'/>
-    <menuitem action='calendar-taskpad-clipboard-paste'/>
+    <menuitem action='popup-cut-clipboard'/>
+    <menuitem action='popup-copy-clipboard'/>
+    <menuitem action='popup-paste-clipboard'/>
     <separator/>
     <menuitem action='calendar-taskpad-assign'/>
     <menuitem action='calendar-taskpad-forward'/>
diff --git a/ui/evolution-contacts.ui b/ui/evolution-contacts.ui
index 864a5aa..c3ce871 100644
--- a/ui/evolution-contacts.ui
+++ b/ui/evolution-contacts.ui
@@ -13,12 +13,6 @@
     </menu>
     <menu action='edit-menu'>
       <placeholder name='edit-actions'>
-        <menuitem action='contact-select-all'/>
-        <separator/>
-        <menuitem action='contact-clipboard-cut'/>
-        <menuitem action='contact-clipboard-copy'/>
-        <menuitem action='contact-clipboard-paste'/>
-        <separator/>
         <menuitem action='contact-delete'/>
         <menuitem action='address-book-delete'/>
       </placeholder>
@@ -77,9 +71,9 @@
     <menuitem action='contact-popup-copy'/>
     <menuitem action='contact-popup-move'/>
     <separator/>
-    <menuitem action='contact-popup-clipboard-cut'/>
-    <menuitem action='contact-popup-clipboard-copy'/>
-    <menuitem action='contact-popup-clipboard-paste'/>
+    <menuitem action='popup-cut-clipboard'/>
+    <menuitem action='popup-copy-clipboard'/>
+    <menuitem action='popup-paste-clipboard'/>
     <menuitem action='contact-popup-delete'/>
   </popup>
   <popup name='contact-search-options'>
diff --git a/ui/evolution-mail-reader.ui b/ui/evolution-mail-reader.ui
index 1209144..b54f4b9 100644
--- a/ui/evolution-mail-reader.ui
+++ b/ui/evolution-mail-reader.ui
@@ -11,10 +11,6 @@
     </menu>
     <menu action='edit-menu'>
       <placeholder name='edit-actions'>
-        <menuitem action='mail-clipboard-copy'/>
-        <separator/>
-        <menuitem action='mail-select-all'/>
-        <separator/>
         <menuitem action='mail-delete'/>
         <menuitem action='mail-undelete'/>
         <separator/>
@@ -145,8 +141,6 @@
     </placeholder>
   </popup>
   <popup name='mail-preview-popup'>
-    <menuitem action='mail-popup-clipboard-copy'/>
-    <separator/>
     <menuitem action='mail-popup-reply-sender'/>
     <menuitem action='mail-popup-reply-all'/>
     <menuitem action='mail-popup-forward'/>
diff --git a/ui/evolution-mail.ui b/ui/evolution-mail.ui
index f2c162c..ec97120 100644
--- a/ui/evolution-mail.ui
+++ b/ui/evolution-mail.ui
@@ -42,7 +42,6 @@
         <menuitem action='mail-folder-copy'/>
         <menuitem action='mail-folder-move'/>
         <separator/>
-        <menuitem action='mail-folder-select-all'/>
         <menuitem action='mail-folder-select-thread'/>
         <menuitem action='mail-folder-select-subthread'/>
         <menuitem action='mail-folder-mark-all-as-read'/>
diff --git a/ui/evolution-memos.ui b/ui/evolution-memos.ui
index 0d842b1..29c4263 100644
--- a/ui/evolution-memos.ui
+++ b/ui/evolution-memos.ui
@@ -11,10 +11,6 @@
     </menu>
     <menu action='edit-menu'>
       <placeholder name='edit-actions'>
-        <menuitem action='memo-clipboard-cut'/>
-        <menuitem action='memo-clipboard-copy'/>
-        <menuitem action='memo-clipboard-paste'/>
-        <separator/>
         <menuitem action='memo-delete'/>
       </placeholder>
     </menu>
@@ -31,9 +27,9 @@
   </menubar>
   <toolbar name='main-toolbar'>
     <placeholder name='toolbar-actions'>
-      <toolitem action='memo-clipboard-cut'/>
-      <toolitem action='memo-clipboard-copy'/>
-      <toolitem action='memo-clipboard-paste'/>
+      <toolitem action='cut-clipboard'/>
+      <toolitem action='copy-clipboard'/>
+      <toolitem action='paste-clipboard'/>
       <separator/>
       <toolitem action='memo-list-print'/>
       <toolitem action='memo-delete'/>
@@ -47,9 +43,9 @@
     <menuitem action='memo-popup-save-as'/>
     <menuitem action='memo-popup-print'/>
     <separator/>
-    <menuitem action='memo-popup-clipboard-cut'/>
-    <menuitem action='memo-popup-clipboard-copy'/>
-    <menuitem action='memo-popup-clipboard-paste'/>
+    <menuitem action='popup-cut-clipboard'/>
+    <menuitem action='popup-copy-clipboard'/>
+    <menuitem action='popup-paste-clipboard'/>
     <separator/>
     <menuitem action='memo-popup-forward'/>
     <separator/>
diff --git a/ui/evolution-shell.ui b/ui/evolution-shell.ui
index ccfeb86..654d322 100644
--- a/ui/evolution-shell.ui
+++ b/ui/evolution-shell.ui
@@ -20,6 +20,12 @@
       <menuitem action='quit'/>
     </menu>
     <menu action='edit-menu'>
+      <menuitem action='cut-clipboard'/>
+      <menuitem action='copy-clipboard'/>
+      <menuitem action='paste-clipboard'/>
+      <separator/>
+      <menuitem action='select-all'/>
+      <separator/>
       <placeholder name='edit-actions'/>
       <separator/>
       <menuitem action='sync-options'/>
diff --git a/ui/evolution-tasks.ui b/ui/evolution-tasks.ui
index 7069588..de4d97e 100644
--- a/ui/evolution-tasks.ui
+++ b/ui/evolution-tasks.ui
@@ -11,10 +11,6 @@
     </menu>
     <menu action='edit-menu'>
       <placeholder name='edit-actions'>
-        <menuitem action='task-clipboard-cut'/>
-        <menuitem action='task-clipboard-copy'/>
-        <menuitem action='task-clipboard-paste'/>
-        <separator/>
         <menuitem action='task-delete'/>
         <separator/>
         <menuitem action='task-mark-complete'/>
@@ -40,9 +36,9 @@
     </placeholder>
   </menubar>
   <toolbar name='main-toolbar'>
-    <toolitem action='task-clipboard-cut'/>
-    <toolitem action='task-clipboard-copy'/>
-    <toolitem action='task-clipboard-paste'/>
+    <toolitem action='cut-clipboard'/>
+    <toolitem action='copy-clipboard'/>
+    <toolitem action='paste-clipboard'/>
     <separator/>
     <toolitem action='task-print'/>
     <toolitem action='task-delete'/>
@@ -55,9 +51,9 @@
     <menuitem action='task-popup-save-as'/>
     <menuitem action='task-popup-print'/>
     <separator/>
-    <menuitem action='task-popup-clipboard-cut'/>
-    <menuitem action='task-popup-clipboard-copy'/>
-    <menuitem action='task-popup-clipboard-paste'/>
+    <menuitem action='popup-cut-clipboard'/>
+    <menuitem action='popup-copy-clipboard'/>
+    <menuitem action='popup-paste-clipboard'/>
     <separator/>
     <menuitem action='task-popup-assign'/>
     <menuitem action='task-popup-forward'/>
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 33b9311..aa29f40 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -46,6 +46,7 @@ widgetsinclude_HEADERS =			\
 	e-colors.h				\
 	e-combo-cell-editable.h			\
 	e-dateedit.h				\
+	e-focus-tracker.h			\
 	e-gui-utils.h				\
 	e-hinted-entry.h			\
 	e-hsv-utils.h				\
@@ -60,6 +61,7 @@ widgetsinclude_HEADERS =			\
 	e-popup-menu.h				\
 	e-preferences-window.h			\
 	e-printable.h				\
+	e-selectable.h				\
 	e-selection-model.h			\
 	e-selection-model-array.h		\
 	e-selection-model-simple.h		\
@@ -121,6 +123,7 @@ libemiscwidgets_la_SOURCES =			\
 	e-colors.c				\
 	e-combo-cell-editable.c			\
 	e-dateedit.c				\
+	e-focus-tracker.c			\
 	e-gui-utils.c				\
 	e-hinted-entry.c			\
 	e-hsv-utils.c				\
@@ -135,6 +138,7 @@ libemiscwidgets_la_SOURCES =			\
 	e-popup-menu.c				\
 	e-preferences-window.c			\
 	e-printable.c				\
+	e-selectable.c				\
 	e-selection-model.c			\
 	e-selection-model-array.c		\
 	e-selection-model-simple.c		\
diff --git a/widgets/misc/e-focus-tracker.c b/widgets/misc/e-focus-tracker.c
new file mode 100644
index 0000000..d1cb32a
--- /dev/null
+++ b/widgets/misc/e-focus-tracker.c
@@ -0,0 +1,859 @@
+/*
+ * e-focus-tracker.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-focus-tracker.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include <misc/e-selectable.h>
+
+#define E_FOCUS_TRACKER_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_FOCUS_TRACKER, EFocusTrackerPrivate))
+
+struct _EFocusTrackerPrivate {
+	GtkWidget *focus;  /* not referenced */
+	GtkWindow *window;
+
+	GtkAction *cut_clipboard;
+	GtkAction *copy_clipboard;
+	GtkAction *paste_clipboard;
+	GtkAction *delete_selection;
+	GtkAction *select_all;
+};
+
+enum {
+	PROP_0,
+	PROP_FOCUS,
+	PROP_WINDOW,
+	PROP_CUT_CLIPBOARD_ACTION,
+	PROP_COPY_CLIPBOARD_ACTION,
+	PROP_PASTE_CLIPBOARD_ACTION,
+	PROP_DELETE_SELECTION_ACTION,
+	PROP_SELECT_ALL_ACTION
+};
+
+static gpointer parent_class;
+
+static void
+focus_tracker_disable_actions (EFocusTracker *focus_tracker)
+{
+	GtkAction *action;
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	if (action != NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	if (action != NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	if (action != NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
+	if (action != NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	if (action != NULL)
+		gtk_action_set_sensitive (action, FALSE);
+}
+
+static void
+focus_tracker_editable_update_actions (EFocusTracker *focus_tracker,
+                                       GtkEditable *editable,
+                                       GdkAtom *targets,
+                                       gint n_targets)
+{
+	GtkAction *action;
+	gboolean can_edit_text;
+	gboolean clipboard_has_text;
+	gboolean text_is_selected;
+	gboolean sensitive;
+
+	can_edit_text =
+		gtk_editable_get_editable (editable);
+
+	clipboard_has_text = (targets != NULL) &&
+		gtk_targets_include_text (targets, n_targets);
+
+	text_is_selected =
+		gtk_editable_get_selection_bounds (editable, NULL, NULL);
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	if (action != NULL) {
+		sensitive = can_edit_text && text_is_selected;
+		gtk_action_set_sensitive (action, sensitive);
+		gtk_action_set_tooltip (action, _("Cut the selection"));
+	}
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	if (action != NULL) {
+		sensitive = text_is_selected;
+		gtk_action_set_sensitive (action, sensitive);
+		gtk_action_set_tooltip (action, _("Copy the selection"));
+	}
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	if (action != NULL) {
+		sensitive = can_edit_text && clipboard_has_text;
+		gtk_action_set_sensitive (action, sensitive);
+		gtk_action_set_tooltip (action, _("Paste the clipboard"));
+	}
+
+	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
+	if (action != NULL) {
+		sensitive = can_edit_text && text_is_selected;
+		gtk_action_set_sensitive (action, sensitive);
+		gtk_action_set_tooltip (action, _("Delete the selection"));
+	}
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	if (action != NULL) {
+		sensitive = TRUE;  /* always enabled */
+		gtk_action_set_sensitive (action, sensitive);
+		gtk_action_set_tooltip (action, _("Select all text"));
+	}
+}
+
+static void
+focus_tracker_selectable_update_actions (EFocusTracker *focus_tracker,
+                                         ESelectable *selectable,
+                                         GdkAtom *targets,
+                                         gint n_targets)
+{
+	ESelectableInterface *interface;
+	GtkAction *action;
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+
+	e_selectable_update_actions (
+		selectable, focus_tracker, targets, n_targets);
+
+	/* Disable actions for which the corresponding method is not
+	 * implemented.  This allows update_actions() implementations
+	 * to simply skip the actions they don't support, which in turn
+	 * allows us to add new actions without disturbing the existing
+	 * ESelectable implementations. */
+
+	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
+	if (action != NULL && interface->cut_clipboard == NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	if (action != NULL && interface->copy_clipboard == NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
+	if (action != NULL && interface->paste_clipboard == NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
+	if (action != NULL && interface->delete_selection == NULL)
+		gtk_action_set_sensitive (action, FALSE);
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	if (action != NULL && interface->select_all == NULL)
+		gtk_action_set_sensitive (action, FALSE);
+}
+
+static void
+focus_tracker_targets_received_cb (GtkClipboard *clipboard,
+                                   GdkAtom *targets,
+                                   gint n_targets,
+                                   EFocusTracker *focus_tracker)
+{
+	GtkWidget *focus;
+
+	focus = e_focus_tracker_get_focus (focus_tracker);
+
+	if (focus == NULL)
+		focus_tracker_disable_actions (focus_tracker);
+
+	else if (GTK_IS_EDITABLE (focus))
+		focus_tracker_editable_update_actions (
+			focus_tracker, GTK_EDITABLE (focus),
+			targets, n_targets);
+
+	else if (E_IS_SELECTABLE (focus))
+		focus_tracker_selectable_update_actions (
+			focus_tracker, E_SELECTABLE (focus),
+			targets, n_targets);
+
+	g_object_unref (focus_tracker);
+}
+
+static void
+focus_tracker_set_focus_cb (GtkWindow *window,
+                            GtkWidget *focus,
+                            EFocusTracker *focus_tracker)
+{
+	while (focus != NULL) {
+		if (GTK_IS_EDITABLE (focus))
+			break;
+
+		if (E_IS_SELECTABLE (focus))
+			break;
+
+		focus = gtk_widget_get_parent (focus);
+	}
+
+	if (focus == focus_tracker->priv->focus)
+		return;
+
+	focus_tracker->priv->focus = focus;
+	g_object_notify (G_OBJECT (focus_tracker), "focus");
+
+	e_focus_tracker_update_actions (focus_tracker);
+}
+
+static void
+focus_tracker_set_window (EFocusTracker *focus_tracker,
+                          GtkWindow *window)
+{
+	g_return_if_fail (GTK_IS_WINDOW (window));
+	g_return_if_fail (focus_tracker->priv->window == NULL);
+
+	focus_tracker->priv->window = g_object_ref (window);
+
+	g_signal_connect (
+		window, "set-focus",
+		G_CALLBACK (focus_tracker_set_focus_cb), focus_tracker);
+}
+
+static void
+focus_tracker_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_WINDOW:
+			focus_tracker_set_window (
+				E_FOCUS_TRACKER (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_CUT_CLIPBOARD_ACTION:
+			e_focus_tracker_set_cut_clipboard_action (
+				E_FOCUS_TRACKER (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_COPY_CLIPBOARD_ACTION:
+			e_focus_tracker_set_copy_clipboard_action (
+				E_FOCUS_TRACKER (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_PASTE_CLIPBOARD_ACTION:
+			e_focus_tracker_set_paste_clipboard_action (
+				E_FOCUS_TRACKER (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_DELETE_SELECTION_ACTION:
+			e_focus_tracker_set_delete_selection_action (
+				E_FOCUS_TRACKER (object),
+				g_value_get_object (value));
+			return;
+
+		case PROP_SELECT_ALL_ACTION:
+			e_focus_tracker_set_select_all_action (
+				E_FOCUS_TRACKER (object),
+				g_value_get_object (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+focus_tracker_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_FOCUS:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_focus (
+				E_FOCUS_TRACKER (object)));
+			return;
+
+		case PROP_WINDOW:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_window (
+				E_FOCUS_TRACKER (object)));
+			return;
+
+		case PROP_CUT_CLIPBOARD_ACTION:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_cut_clipboard_action (
+				E_FOCUS_TRACKER (object)));
+			return;
+
+		case PROP_COPY_CLIPBOARD_ACTION:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_copy_clipboard_action (
+				E_FOCUS_TRACKER (object)));
+			return;
+
+		case PROP_PASTE_CLIPBOARD_ACTION:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_paste_clipboard_action (
+				E_FOCUS_TRACKER (object)));
+			return;
+
+		case PROP_DELETE_SELECTION_ACTION:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_delete_selection_action (
+				E_FOCUS_TRACKER (object)));
+			return;
+
+		case PROP_SELECT_ALL_ACTION:
+			g_value_set_object (
+				value,
+				e_focus_tracker_get_select_all_action (
+				E_FOCUS_TRACKER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+focus_tracker_dispose (GObject *object)
+{
+	EFocusTrackerPrivate *priv;
+
+	priv = E_FOCUS_TRACKER_GET_PRIVATE (object);
+
+	g_signal_handlers_disconnect_matched (
+		gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+		G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+
+	if (priv->window != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->window, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->window);
+		priv->window = NULL;
+	}
+
+	if (priv->cut_clipboard != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->cut_clipboard, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->cut_clipboard);
+		priv->cut_clipboard = NULL;
+	}
+
+	if (priv->copy_clipboard != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->copy_clipboard, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->copy_clipboard);
+		priv->copy_clipboard = NULL;
+	}
+
+	if (priv->paste_clipboard != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->paste_clipboard, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->paste_clipboard);
+		priv->paste_clipboard = NULL;
+	}
+
+	if (priv->delete_selection != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->delete_selection, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->delete_selection);
+		priv->delete_selection = NULL;
+	}
+
+	if (priv->select_all != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->select_all, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->select_all);
+		priv->select_all = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+focus_tracker_constructed (GObject *object)
+{
+	GtkClipboard *clipboard;
+
+	/* Listen for "owner-change" signals from the primary selection
+	 * clipboard to learn when text selections change in GtkEditable
+	 * widgets.  It's a bit of an overkill, but I don't know of any
+	 * other notification mechanism. */
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+
+	g_signal_connect_swapped (
+		clipboard, "owner-change",
+		G_CALLBACK (e_focus_tracker_update_actions), object);
+}
+
+static void
+focus_tracker_class_init (EFocusTrackerClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EFocusTrackerPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = focus_tracker_set_property;
+	object_class->get_property = focus_tracker_get_property;
+	object_class->dispose = focus_tracker_dispose;
+	object_class->constructed = focus_tracker_constructed;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_FOCUS,
+		g_param_spec_object (
+			"focus",
+			"Focus",
+			NULL,
+			GTK_TYPE_WIDGET,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_WINDOW,
+		g_param_spec_object (
+			"window",
+			"Window",
+			NULL,
+			GTK_TYPE_WINDOW,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CUT_CLIPBOARD_ACTION,
+		g_param_spec_object (
+			"cut-clipboard-action",
+			"Cut Clipboard Action",
+			NULL,
+			GTK_TYPE_ACTION,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_COPY_CLIPBOARD_ACTION,
+		g_param_spec_object (
+			"copy-clipboard-action",
+			"Copy Clipboard Action",
+			NULL,
+			GTK_TYPE_ACTION,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PASTE_CLIPBOARD_ACTION,
+		g_param_spec_object (
+			"paste-clipboard-action",
+			"Paste Clipboard Action",
+			NULL,
+			GTK_TYPE_ACTION,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_DELETE_SELECTION_ACTION,
+		g_param_spec_object (
+			"delete-selection-action",
+			"Delete Selection Action",
+			NULL,
+			GTK_TYPE_ACTION,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_SELECT_ALL_ACTION,
+		g_param_spec_object (
+			"select-all-action",
+			"Select All Action",
+			NULL,
+			GTK_TYPE_ACTION,
+			G_PARAM_READWRITE));
+}
+
+static void
+focus_tracker_init (EFocusTracker *focus_tracker)
+{
+	focus_tracker->priv = E_FOCUS_TRACKER_GET_PRIVATE (focus_tracker);
+}
+
+GType
+e_focus_tracker_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EFocusTrackerClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) focus_tracker_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EFocusTracker),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) focus_tracker_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			G_TYPE_OBJECT, "EFocusTracker", &type_info, 0);
+	}
+
+	return type;
+}
+
+EFocusTracker *
+e_focus_tracker_new (GtkWindow *window)
+{
+	g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
+
+	return g_object_new (E_TYPE_FOCUS_TRACKER, "window", window, NULL);
+}
+
+GtkWidget *
+e_focus_tracker_get_focus (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->focus;
+}
+
+GtkWindow *
+e_focus_tracker_get_window (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->window;
+}
+
+GtkAction *
+e_focus_tracker_get_cut_clipboard_action (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->cut_clipboard;
+}
+
+void
+e_focus_tracker_set_cut_clipboard_action (EFocusTracker *focus_tracker,
+                                          GtkAction *cut_clipboard)
+{
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	if (cut_clipboard != NULL) {
+		g_return_if_fail (GTK_IS_ACTION (cut_clipboard));
+		g_object_ref (cut_clipboard);
+	}
+
+	if (focus_tracker->priv->cut_clipboard != NULL) {
+		g_signal_handlers_disconnect_matched (
+			focus_tracker->priv->cut_clipboard,
+			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+			focus_tracker);
+		g_object_unref (focus_tracker->priv->cut_clipboard);
+	}
+
+	focus_tracker->priv->cut_clipboard = cut_clipboard;
+
+	if (cut_clipboard != NULL)
+		g_signal_connect_swapped (
+			cut_clipboard, "activate",
+			G_CALLBACK (e_focus_tracker_cut_clipboard),
+			focus_tracker);
+
+	g_object_notify (G_OBJECT (focus_tracker), "cut-clipboard-action");
+}
+
+GtkAction *
+e_focus_tracker_get_copy_clipboard_action (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->copy_clipboard;
+}
+
+void
+e_focus_tracker_set_copy_clipboard_action (EFocusTracker *focus_tracker,
+                                           GtkAction *copy_clipboard)
+{
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	if (copy_clipboard != NULL) {
+		g_return_if_fail (GTK_IS_ACTION (copy_clipboard));
+		g_object_ref (copy_clipboard);
+	}
+
+	if (focus_tracker->priv->copy_clipboard != NULL) {
+		g_signal_handlers_disconnect_matched (
+			focus_tracker->priv->copy_clipboard,
+			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+			focus_tracker);
+		g_object_unref (focus_tracker->priv->copy_clipboard);
+	}
+
+	focus_tracker->priv->copy_clipboard = copy_clipboard;
+
+	if (copy_clipboard != NULL)
+		g_signal_connect_swapped (
+			copy_clipboard, "activate",
+			G_CALLBACK (e_focus_tracker_copy_clipboard),
+			focus_tracker);
+
+	g_object_notify (G_OBJECT (focus_tracker), "copy-clipboard-action");
+}
+
+GtkAction *
+e_focus_tracker_get_paste_clipboard_action (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->paste_clipboard;
+}
+
+void
+e_focus_tracker_set_paste_clipboard_action (EFocusTracker *focus_tracker,
+                                            GtkAction *paste_clipboard)
+{
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	if (paste_clipboard != NULL) {
+		g_return_if_fail (GTK_IS_ACTION (paste_clipboard));
+		g_object_ref (paste_clipboard);
+	}
+
+	if (focus_tracker->priv->paste_clipboard != NULL) {
+		g_signal_handlers_disconnect_matched (
+			focus_tracker->priv->paste_clipboard,
+			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+			focus_tracker);
+		g_object_unref (focus_tracker->priv->paste_clipboard);
+	}
+
+	focus_tracker->priv->paste_clipboard = paste_clipboard;
+
+	if (paste_clipboard != NULL)
+		g_signal_connect_swapped (
+			paste_clipboard, "activate",
+			G_CALLBACK (e_focus_tracker_paste_clipboard),
+			focus_tracker);
+
+	g_object_notify (G_OBJECT (focus_tracker), "paste-clipboard-action");
+}
+
+GtkAction *
+e_focus_tracker_get_delete_selection_action (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->delete_selection;
+}
+
+void
+e_focus_tracker_set_delete_selection_action (EFocusTracker *focus_tracker,
+                                             GtkAction *delete_selection)
+{
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	if (delete_selection != NULL) {
+		g_return_if_fail (GTK_IS_ACTION (delete_selection));
+		g_object_ref (delete_selection);
+	}
+
+	if (focus_tracker->priv->delete_selection != NULL) {
+		g_signal_handlers_disconnect_matched (
+			focus_tracker->priv->delete_selection,
+			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+			focus_tracker);
+		g_object_unref (focus_tracker->priv->delete_selection);
+	}
+
+	focus_tracker->priv->delete_selection = delete_selection;
+
+	if (delete_selection != NULL)
+		g_signal_connect_swapped (
+			delete_selection, "activate",
+			G_CALLBACK (e_focus_tracker_delete_selection),
+			focus_tracker);
+
+	g_object_notify (G_OBJECT (focus_tracker), "delete-selection-action");
+}
+
+GtkAction *
+e_focus_tracker_get_select_all_action (EFocusTracker *focus_tracker)
+{
+	g_return_val_if_fail (E_IS_FOCUS_TRACKER (focus_tracker), NULL);
+
+	return focus_tracker->priv->select_all;
+}
+
+void
+e_focus_tracker_set_select_all_action (EFocusTracker *focus_tracker,
+                                       GtkAction *select_all)
+{
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	if (select_all != NULL) {
+		g_return_if_fail (GTK_IS_ACTION (select_all));
+		g_object_ref (select_all);
+	}
+
+	if (focus_tracker->priv->select_all != NULL) {
+		g_signal_handlers_disconnect_matched (
+			focus_tracker->priv->select_all,
+			G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+			focus_tracker);
+		g_object_unref (focus_tracker->priv->select_all);
+	}
+
+	focus_tracker->priv->select_all = select_all;
+
+	if (select_all != NULL)
+		g_signal_connect_swapped (
+			select_all, "activate",
+			G_CALLBACK (e_focus_tracker_select_all),
+			focus_tracker);
+
+	g_object_notify (G_OBJECT (focus_tracker), "select-all-action");
+}
+
+void
+e_focus_tracker_update_actions (EFocusTracker *focus_tracker)
+{
+	GtkClipboard *clipboard;
+
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	/* Request clipboard targets asynchronously. */
+
+	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+
+	gtk_clipboard_request_targets (
+		clipboard, (GtkClipboardTargetsReceivedFunc)
+		focus_tracker_targets_received_cb,
+		g_object_ref (focus_tracker));
+}
+
+void
+e_focus_tracker_cut_clipboard (EFocusTracker *focus_tracker)
+{
+	GtkWidget *focus;
+
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	focus = e_focus_tracker_get_focus (focus_tracker);
+
+	if (GTK_IS_EDITABLE (focus))
+		gtk_editable_cut_clipboard (GTK_EDITABLE (focus));
+
+	else if (E_IS_SELECTABLE (focus))
+		e_selectable_cut_clipboard (E_SELECTABLE (focus));
+}
+
+void
+e_focus_tracker_copy_clipboard (EFocusTracker *focus_tracker)
+{
+	GtkWidget *focus;
+
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	focus = e_focus_tracker_get_focus (focus_tracker);
+
+	if (GTK_IS_EDITABLE (focus))
+		gtk_editable_copy_clipboard (GTK_EDITABLE (focus));
+
+	else if (E_IS_SELECTABLE (focus))
+		e_selectable_copy_clipboard (E_SELECTABLE (focus));
+}
+
+void
+e_focus_tracker_paste_clipboard (EFocusTracker *focus_tracker)
+{
+	GtkWidget *focus;
+
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	focus = e_focus_tracker_get_focus (focus_tracker);
+
+	if (GTK_IS_EDITABLE (focus))
+		gtk_editable_paste_clipboard (GTK_EDITABLE (focus));
+
+	else if (E_IS_SELECTABLE (focus))
+		e_selectable_paste_clipboard (E_SELECTABLE (focus));
+}
+
+void
+e_focus_tracker_delete_selection (EFocusTracker *focus_tracker)
+{
+	GtkWidget *focus;
+
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	focus = e_focus_tracker_get_focus (focus_tracker);
+
+	if (GTK_IS_EDITABLE (focus))
+		gtk_editable_delete_selection (GTK_EDITABLE (focus));
+
+	else if (E_IS_SELECTABLE (focus))
+		e_selectable_delete_selection (E_SELECTABLE (focus));
+}
+
+void
+e_focus_tracker_select_all (EFocusTracker *focus_tracker)
+{
+	GtkWidget *focus;
+
+	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
+
+	focus = e_focus_tracker_get_focus (focus_tracker);
+
+	if (GTK_IS_EDITABLE (focus))
+		gtk_editable_select_region (GTK_EDITABLE (focus), 0, -1);
+
+	else if (E_IS_SELECTABLE (focus))
+		e_selectable_select_all (E_SELECTABLE (focus));
+}
diff --git a/widgets/misc/e-focus-tracker.h b/widgets/misc/e-focus-tracker.h
new file mode 100644
index 0000000..679cc4b
--- /dev/null
+++ b/widgets/misc/e-focus-tracker.h
@@ -0,0 +1,99 @@
+/*
+ * e-focus-tracker.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_FOCUS_TRACKER_H
+#define E_FOCUS_TRACKER_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_FOCUS_TRACKER \
+	(e_focus_tracker_get_type ())
+#define E_FOCUS_TRACKER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_FOCUS_TRACKER, EFocusTracker))
+#define E_FOCUS_TRACKER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_FOCUS_TRACKER, EFocusTrackerClass))
+#define E_IS_FOCUS_TRACKER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_FOCUS_TRACKER))
+#define E_IS_FOCUS_TRACKER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_FOCUS_TRACKER))
+#define E_FOCUS_TRACKER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_FOCUS_TRACKER, EFocusTrackerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EFocusTracker EFocusTracker;
+typedef struct _EFocusTrackerClass EFocusTrackerClass;
+typedef struct _EFocusTrackerPrivate EFocusTrackerPrivate;
+
+struct _EFocusTracker {
+	GObject parent;
+	EFocusTrackerPrivate *priv;
+};
+
+struct _EFocusTrackerClass {
+	GObjectClass parent_class;
+};
+
+GType		e_focus_tracker_get_type	(void);
+EFocusTracker *	e_focus_tracker_new		(GtkWindow *window);
+GtkWidget *	e_focus_tracker_get_focus	(EFocusTracker *focus_tracker);
+GtkWindow *	e_focus_tracker_get_window	(EFocusTracker *focus_tracker);
+GtkAction *	e_focus_tracker_get_cut_clipboard_action
+						(EFocusTracker *focus_tracker);
+void		e_focus_tracker_set_cut_clipboard_action
+						(EFocusTracker *focus_tracker,
+						 GtkAction *cut_clipboard);
+GtkAction *	e_focus_tracker_get_copy_clipboard_action
+						(EFocusTracker *focus_tracker);
+void		e_focus_tracker_set_copy_clipboard_action
+						(EFocusTracker *focus_tracker,
+						 GtkAction *copy_clipboard);
+GtkAction *	e_focus_tracker_get_paste_clipboard_action
+						(EFocusTracker *focus_tracker);
+void		e_focus_tracker_set_paste_clipboard_action
+						(EFocusTracker *focus_tracker,
+						 GtkAction *paste_clipboard);
+GtkAction *	e_focus_tracker_get_delete_selection_action
+						(EFocusTracker *focus_tracker);
+void		e_focus_tracker_set_delete_selection_action
+						(EFocusTracker *focus_tracker,
+						 GtkAction *delete_selection);
+GtkAction *	e_focus_tracker_get_select_all_action
+						(EFocusTracker *focus_tracker);
+void		e_focus_tracker_set_select_all_action
+						(EFocusTracker *focus_tracker,
+						 GtkAction *select_all);
+void		e_focus_tracker_update_actions	(EFocusTracker *focus_tracker);
+void		e_focus_tracker_cut_clipboard	(EFocusTracker *focus_tracker);
+void		e_focus_tracker_copy_clipboard	(EFocusTracker *focus_tracker);
+void		e_focus_tracker_paste_clipboard	(EFocusTracker *focus_tracker);
+void		e_focus_tracker_delete_selection(EFocusTracker *focus_tracker);
+void		e_focus_tracker_select_all	(EFocusTracker *focus_tracker);
+
+G_END_DECLS
+
+#endif /* E_FOCUS_TRACKER_H */
diff --git a/widgets/misc/e-selectable.c b/widgets/misc/e-selectable.c
new file mode 100644
index 0000000..95cd2bb
--- /dev/null
+++ b/widgets/misc/e-selectable.c
@@ -0,0 +1,133 @@
+/*
+ * e-selectable.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-selectable.h"
+
+GType
+e_selectable_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (ESelectableInterface),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) NULL,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			0,     /* instance_size */
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) NULL,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			G_TYPE_INTERFACE, "ESelectable", &type_info, 0);
+
+		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+	}
+
+	return type;
+}
+
+void
+e_selectable_update_actions (ESelectable *selectable,
+                             EFocusTracker *focus_tracker,
+                             GdkAtom *clipboard_targets,
+                             gint n_clipboard_targets)
+{
+	ESelectableInterface *interface;
+
+	g_return_if_fail (E_IS_SELECTABLE (selectable));
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+	g_return_if_fail (interface->update_actions != NULL);
+
+	return interface->update_actions (
+		selectable, focus_tracker,
+		clipboard_targets, n_clipboard_targets);
+}
+
+void
+e_selectable_cut_clipboard (ESelectable *selectable)
+{
+	ESelectableInterface *interface;
+
+	g_return_if_fail (E_IS_SELECTABLE (selectable));
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+
+	if (interface->cut_clipboard != NULL)
+		interface->cut_clipboard (selectable);
+}
+
+void
+e_selectable_copy_clipboard (ESelectable *selectable)
+{
+	ESelectableInterface *interface;
+
+	g_return_if_fail (E_IS_SELECTABLE (selectable));
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+
+	if (interface->copy_clipboard != NULL)
+		interface->copy_clipboard (selectable);
+}
+
+void
+e_selectable_paste_clipboard (ESelectable *selectable)
+{
+	ESelectableInterface *interface;
+
+	g_return_if_fail (E_IS_SELECTABLE (selectable));
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+
+	if (interface->paste_clipboard != NULL)
+		interface->paste_clipboard (selectable);
+}
+
+void
+e_selectable_delete_selection (ESelectable *selectable)
+{
+	ESelectableInterface *interface;
+
+	g_return_if_fail (E_IS_SELECTABLE (selectable));
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+
+	if (interface->delete_selection != NULL)
+		interface->delete_selection (selectable);
+}
+
+void
+e_selectable_select_all (ESelectable *selectable)
+{
+	ESelectableInterface *interface;
+
+	g_return_if_fail (E_IS_SELECTABLE (selectable));
+
+	interface = E_SELECTABLE_GET_INTERFACE (selectable);
+
+	if (interface->select_all != NULL)
+		interface->select_all (selectable);
+}
diff --git a/widgets/misc/e-selectable.h b/widgets/misc/e-selectable.h
new file mode 100644
index 0000000..fca400c
--- /dev/null
+++ b/widgets/misc/e-selectable.h
@@ -0,0 +1,76 @@
+/*
+ * e-selectable.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifndef E_SELECTABLE_H
+#define E_SELECTABLE_H
+
+#include <gtk/gtk.h>
+#include <misc/e-focus-tracker.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SELECTABLE \
+	(e_selectable_get_type ())
+#define E_SELECTABLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SELECTABLE, ESelectable))
+#define E_IS_SELECTABLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SELECTABLE))
+#define E_SELECTABLE_GET_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE \
+	((obj), E_TYPE_SELECTABLE, ESelectableInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _ESelectable ESelectable;
+typedef struct _ESelectableInterface ESelectableInterface;
+
+struct _ESelectableInterface {
+	GTypeInterface parent_iface;
+
+	/* Required Methods */
+	void		(*update_actions)	(ESelectable *selectable,
+						 EFocusTracker *focus_tracker,
+						 GdkAtom *clipboard_targets,
+						 gint n_clipboard_targets);
+
+	/* Optional Methods */
+	void		(*cut_clipboard)	(ESelectable *selectable);
+	void		(*copy_clipboard)	(ESelectable *selectable);
+	void		(*paste_clipboard)	(ESelectable *selectable);
+	void		(*delete_selection)	(ESelectable *selectable);
+	void		(*select_all)		(ESelectable *selectable);
+};
+
+GType		e_selectable_get_type		(void);
+void		e_selectable_update_actions	(ESelectable *selectable,
+						 EFocusTracker *focus_tracker,
+						 GdkAtom *clipboard_targets,
+						 gint n_clipboard_targets);
+void		e_selectable_cut_clipboard	(ESelectable *selectable);
+void		e_selectable_copy_clipboard	(ESelectable *selectable);
+void		e_selectable_paste_clipboard	(ESelectable *selectable);
+void		e_selectable_delete_selection	(ESelectable *selectable);
+void		e_selectable_select_all		(ESelectable *selectable);
+
+G_END_DECLS
+
+#endif /* E_SELECTABLE_H */
diff --git a/widgets/misc/e-signature-editor.c b/widgets/misc/e-signature-editor.c
index 9266425..91a4d81 100644
--- a/widgets/misc/e-signature-editor.c
+++ b/widgets/misc/e-signature-editor.c
@@ -26,6 +26,7 @@
 
 #include <e-util/e-alert-dialog.h>
 #include <e-util/e-signature-utils.h>
+#include <misc/e-web-view.h>
 
 #define E_SIGNATURE_EDITOR_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -33,11 +34,13 @@
 
 enum {
 	PROP_0,
+	PROP_FOCUS_TRACKER,
 	PROP_SIGNATURE
 };
 
 struct _ESignatureEditorPrivate {
 	GtkActionGroup *action_group;
+	EFocusTracker *focus_tracker;
 	ESignature *signature;
 	GtkWidget *entry;
 	gchar *original_name;
@@ -255,6 +258,12 @@ signature_editor_get_property (GObject *object,
                                GParamSpec *pspec)
 {
 	switch (property_id) {
+		case PROP_FOCUS_TRACKER:
+			g_value_set_object (
+				value, e_signature_editor_get_focus_tracker (
+				E_SIGNATURE_EDITOR (object)));
+			return;
+
 		case PROP_SIGNATURE:
 			g_value_set_object (
 				value, e_signature_editor_get_signature (
@@ -277,6 +286,11 @@ signature_editor_dispose (GObject *object)
 		priv->action_group = NULL;
 	}
 
+	if (priv->focus_tracker != NULL) {
+		g_object_unref (priv->focus_tracker);
+		priv->focus_tracker = NULL;
+	}
+
 	if (priv->signature != NULL) {
 		g_object_unref (priv->signature);
 		priv->signature = NULL;
@@ -320,6 +334,16 @@ signature_editor_class_init (ESignatureEditorClass *class)
 
 	g_object_class_install_property (
 		object_class,
+		PROP_FOCUS_TRACKER,
+		g_param_spec_object (
+			"focus-tracker",
+			NULL,
+			NULL,
+			E_TYPE_FOCUS_TRACKER,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
 		PROP_SIGNATURE,
 		g_param_spec_object (
 			"signature",
@@ -333,7 +357,10 @@ static void
 signature_editor_init (ESignatureEditor *editor)
 {
 	GtkActionGroup *action_group;
+	EFocusTracker *focus_tracker;
+	GtkhtmlEditor *gtkhtml_editor;
 	GtkUIManager *ui_manager;
+	GtkAction *action;
 	GtkWidget *container;
 	GtkWidget *widget;
 	GtkWidget *vbox;
@@ -342,7 +369,8 @@ signature_editor_init (ESignatureEditor *editor)
 	editor->priv = E_SIGNATURE_EDITOR_GET_PRIVATE (editor);
 	vbox = GTKHTML_EDITOR (editor)->vbox;
 
-	ui_manager = gtkhtml_editor_get_ui_manager (GTKHTML_EDITOR (editor));
+	gtkhtml_editor = GTKHTML_EDITOR (editor);
+	ui_manager = gtkhtml_editor_get_ui_manager (gtkhtml_editor);
 
 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
 	handle_error (&error);
@@ -383,6 +411,28 @@ signature_editor_init (ESignatureEditor *editor)
 		G_CALLBACK (signature_editor_delete_event_cb), NULL);
 
 	e_signature_editor_set_signature (editor, NULL);
+
+	/* Configure an EFocusTracker to manage selection actions.
+	 *
+	 * XXX GtkhtmlEditor does not manage its own selection actions,
+	 *     which is technically a bug but works in our favor here
+	 *     because it won't cause any conflicts with EFocusTracker. */
+
+	focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor));
+
+	action = gtkhtml_editor_get_action (gtkhtml_editor, "cut");
+	e_focus_tracker_set_cut_clipboard_action (focus_tracker, action);
+
+	action = gtkhtml_editor_get_action (gtkhtml_editor, "copy");
+	e_focus_tracker_set_copy_clipboard_action (focus_tracker, action);
+
+	action = gtkhtml_editor_get_action (gtkhtml_editor, "paste");
+	e_focus_tracker_set_paste_clipboard_action (focus_tracker, action);
+
+	action = gtkhtml_editor_get_action (gtkhtml_editor, "select-all");
+	e_focus_tracker_set_select_all_action (focus_tracker, action);
+
+	editor->priv->focus_tracker = focus_tracker;
 }
 
 GType
@@ -415,7 +465,17 @@ e_signature_editor_get_type (void)
 GtkWidget *
 e_signature_editor_new (void)
 {
-	return g_object_new (E_TYPE_SIGNATURE_EDITOR, NULL);
+	return g_object_new (
+		E_TYPE_SIGNATURE_EDITOR,
+		"html", e_web_view_new (), NULL);
+}
+
+EFocusTracker *
+e_signature_editor_get_focus_tracker (ESignatureEditor *editor)
+{
+	g_return_val_if_fail (E_IS_SIGNATURE_EDITOR (editor), NULL);
+
+	return editor->priv->focus_tracker;
 }
 
 ESignature *
diff --git a/widgets/misc/e-signature-editor.h b/widgets/misc/e-signature-editor.h
index 1e8b88a..6d9f4a9 100644
--- a/widgets/misc/e-signature-editor.h
+++ b/widgets/misc/e-signature-editor.h
@@ -24,6 +24,7 @@
 
 #include <gtkhtml-editor.h>
 #include <e-util/e-signature.h>
+#include <misc/e-focus-tracker.h>
 
 /* Standard GObject macros */
 #define E_TYPE_SIGNATURE_EDITOR \
@@ -61,6 +62,8 @@ struct _ESignatureEditorClass {
 
 GType		e_signature_editor_get_type	 (void);
 GtkWidget *	e_signature_editor_new		 (void);
+EFocusTracker *	e_signature_editor_get_focus_tracker
+						 (ESignatureEditor *editor);
 ESignature *	e_signature_editor_get_signature (ESignatureEditor *editor);
 void		e_signature_editor_set_signature (ESignatureEditor *editor,
 						  ESignature *signature);
diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c
index f52d739..a873731 100644
--- a/widgets/misc/e-web-view.c
+++ b/widgets/misc/e-web-view.c
@@ -33,6 +33,7 @@
 #include "e-util/e-plugin-ui.h"
 
 #include "e-popup-action.h"
+#include "e-selectable.h"
 
 #define E_WEB_VIEW_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -89,7 +90,7 @@ static guint signals[LAST_SIGNAL];
 static const gchar *ui =
 "<ui>"
 "  <popup name='context'>"
-"    <menuitem action='clipboard-copy'/>"
+"    <menuitem action='copy-clipboard'/>"
 "    <separator/>"
 "    <placeholder name='custom-actions-1'>"
 "      <menuitem action='open'/>"
@@ -240,10 +241,10 @@ web_view_request_read_cb (GFile *file,
 }
 
 static void
-action_clipboard_copy_cb (GtkAction *action,
+action_copy_clipboard_cb (GtkAction *action,
                           EWebView *web_view)
 {
-	e_web_view_clipboard_copy (web_view);
+	e_web_view_copy_clipboard (web_view);
 }
 
 static void
@@ -372,12 +373,12 @@ static GtkActionEntry mailto_entries[] = {
 
 static GtkActionEntry selection_entries[] = {
 
-	{ "clipboard-copy",
+	{ "copy-clipboard",
 	  GTK_STOCK_COPY,
 	  NULL,
 	  NULL,
-	  N_("Copy the selection to the clipboard"),
-	  G_CALLBACK (action_clipboard_copy_cb) },
+	  N_("Copy the selection"),
+	  G_CALLBACK (action_copy_clipboard_cb) },
 };
 
 static GtkActionEntry standard_entries[] = {
@@ -859,6 +860,52 @@ web_view_update_actions (EWebView *web_view)
 }
 
 static void
+web_view_selectable_update_actions (ESelectable *selectable,
+                                    EFocusTracker *focus_tracker,
+                                    GdkAtom *clipboard_targets,
+                                    gint n_clipboard_targets)
+{
+	EWebView *web_view;
+	GtkAction *action;
+	const gchar *tooltip;
+	gboolean sensitive;
+
+	web_view = E_WEB_VIEW (selectable);
+
+	/* Copy Clipboard */
+
+	action = e_web_view_get_action (web_view, "copy-clipboard");
+	sensitive = gtk_action_get_sensitive (action);
+	tooltip = gtk_action_get_tooltip (action);
+
+	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+
+	/* Select All */
+
+	action = e_web_view_get_action (web_view, "select-all");
+	sensitive = gtk_action_get_sensitive (action);
+	tooltip = gtk_action_get_tooltip (action);
+
+	action = e_focus_tracker_get_select_all_action (focus_tracker);
+	gtk_action_set_sensitive (action, sensitive);
+	gtk_action_set_tooltip (action, tooltip);
+}
+
+static void
+web_view_selectable_copy_clipboard (ESelectable *selectable)
+{
+	e_web_view_copy_clipboard (E_WEB_VIEW (selectable));
+}
+
+static void
+web_view_selectable_select_all (ESelectable *selectable)
+{
+	e_web_view_select_all (E_WEB_VIEW (selectable));
+}
+
+static void
 web_view_class_init (EWebViewClass *class)
 {
 	GObjectClass *object_class;
@@ -1010,6 +1057,14 @@ web_view_class_init (EWebViewClass *class)
 }
 
 static void
+web_view_selectable_init (ESelectableInterface *interface)
+{
+	interface->update_actions = web_view_selectable_update_actions;
+	interface->copy_clipboard = web_view_selectable_copy_clipboard;
+	interface->select_all = web_view_selectable_select_all;
+}
+
+static void
 web_view_init (EWebView *web_view)
 {
 	GtkUIManager *ui_manager;
@@ -1140,8 +1195,17 @@ e_web_view_get_type (void)
 			NULL   /* value_table */
 		};
 
+		static const GInterfaceInfo selectable_info = {
+			(GInterfaceInitFunc) web_view_selectable_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL   /* interface_data */
+		};
+
 		type = g_type_register_static (
 			GTK_TYPE_HTML, "EWebView", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_SELECTABLE, &selectable_info);
 	}
 
 	return type;
@@ -1411,7 +1475,7 @@ e_web_view_extract_uri (EWebView *web_view,
 }
 
 void
-e_web_view_clipboard_copy (EWebView *web_view)
+e_web_view_copy_clipboard (EWebView *web_view)
 {
 	g_return_if_fail (E_IS_WEB_VIEW (web_view));
 
diff --git a/widgets/misc/e-web-view.h b/widgets/misc/e-web-view.h
index 4a30b4b..123965c 100644
--- a/widgets/misc/e-web-view.h
+++ b/widgets/misc/e-web-view.h
@@ -118,7 +118,7 @@ GtkActionGroup *e_web_view_get_action_group	(EWebView *web_view,
 gchar *		e_web_view_extract_uri		(EWebView *web_view,
 						 GdkEventButton *event,
 						 GtkHTML *frame);
-void		e_web_view_clipboard_copy	(EWebView *web_view);
+void		e_web_view_copy_clipboard	(EWebView *web_view);
 gboolean	e_web_view_is_selection_active	(EWebView *web_view);
 gboolean	e_web_view_scroll_forward	(EWebView *web_view);
 gboolean	e_web_view_scroll_backward	(EWebView *web_view);
diff --git a/widgets/table/e-table.c b/widgets/table/e-table.c
index f5cd6c0..2656f7f 100644
--- a/widgets/table/e-table.c
+++ b/widgets/table/e-table.c
@@ -55,8 +55,6 @@
 
 #define COLUMN_HEADER_HEIGHT 16
 
-G_DEFINE_TYPE (ETable, e_table, GTK_TYPE_TABLE)
-
 #define d(x)
 
 #if d(!)0
@@ -77,6 +75,11 @@ enum {
 	STATE_CHANGE,
 	WHITE_SPACE_EVENT,
 
+	CUT_CLIPBOARD,
+	COPY_CLIPBOARD,
+	PASTE_CLIPBOARD,
+	SELECT_ALL,
+
 	TABLE_DRAG_BEGIN,
 	TABLE_DRAG_END,
 	TABLE_DRAG_DATA_GET,
@@ -159,6 +162,8 @@ static gint et_focus (GtkWidget *container, GtkDirectionType direction);
 static void scroll_off (ETable *et);
 static void scroll_on (ETable *et, guint scroll_direction);
 
+G_DEFINE_TYPE (ETable, e_table, GTK_TYPE_TABLE)
+
 static void
 et_disconnect_model (ETable *et)
 {
@@ -3357,4 +3362,3 @@ e_table_class_init (ETableClass *class)
 
 	gal_a11y_e_table_init ();
 }
-
diff --git a/widgets/table/e-tree.c b/widgets/table/e-tree.c
index 38c7a51..ba50be0 100644
--- a/widgets/table/e-tree.c
+++ b/widgets/table/e-tree.c
@@ -54,8 +54,6 @@
 
 #define COLUMN_HEADER_HEIGHT 16
 
-G_DEFINE_TYPE (ETree, e_tree, GTK_TYPE_TABLE)
-
 #define d(x)
 
 #if d(!)0
@@ -76,6 +74,11 @@ enum {
 	STATE_CHANGE,
 	WHITE_SPACE_EVENT,
 
+	CUT_CLIPBOARD,
+	COPY_CLIPBOARD,
+	PASTE_CLIPBOARD,
+	SELECT_ALL,
+
 	TREE_DRAG_BEGIN,
 	TREE_DRAG_END,
 	TREE_DRAG_DATA_GET,
@@ -245,6 +248,8 @@ static void hover_off (ETree *et);
 static void hover_on (ETree *et, gint x, gint y);
 static void context_destroyed (gpointer data, GObject *ctx);
 
+G_DEFINE_TYPE (ETree, e_tree, GTK_TYPE_TABLE)
+
 static void
 et_disconnect_from_etta (ETree *et)
 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]