diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2010-01-17 02:34:32 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2010-01-18 01:11:08 +0800 |
commit | 3e7c7808cc65c22bc40a7d1d30ffa0044097a6ff (patch) | |
tree | d74cd0017e310c79b796eba3df7bfb8947a673d7 | |
parent | 2cf0c27e2ece428dd2af1945f6fececbe9dbcc15 (diff) | |
download | gsoc2013-evolution-3e7c7808cc65c22bc40a7d1d30ffa0044097a6ff.tar.gz gsoc2013-evolution-3e7c7808cc65c22bc40a7d1d30ffa0044097a6ff.tar.zst gsoc2013-evolution-3e7c7808cc65c22bc40a7d1d30ffa0044097a6ff.zip |
Improve clipboard behavior.
Add "copy-target-list" and "paste-target-list" to the ESelectable
interface. These are underutilized for the moment, but will eventually
be used to help integrate drag-and-drop support into ESelectable.
Add cut and paste support to EWebView, along with a new "editable"
property and new clipboard signals "copy-clipboard", "cut-clipboard" and
"paste-clipboard".
In EFocusTracker, listen for "owner-changed" signals from the default
clipboard as another trigger to update actions, particularly the Paste
action. (Unfortunately this doesn't work for EWebView since GtkHtml
implements its own clipboard.)
In EMsgComposer, convert GtkhtmlEditor's clipboard methods to empty
stubs, since EFocusTracker will now trigger EWebView's clipboard
actions. Also, intercept EWebView::paste-clipboard signals and improve
the interaction between the HTML editor and the attachment bar based on
use cases in bug #603715.
-rw-r--r-- | addressbook/gui/widgets/e-addressbook-view.c | 90 | ||||
-rw-r--r-- | addressbook/gui/widgets/e-addressbook-view.h | 4 | ||||
-rw-r--r-- | calendar/gui/e-calendar-view.c | 90 | ||||
-rw-r--r-- | calendar/gui/e-calendar-view.h | 4 | ||||
-rw-r--r-- | calendar/gui/e-memo-table.c | 78 | ||||
-rw-r--r-- | calendar/gui/e-memo-table.h | 4 | ||||
-rw-r--r-- | calendar/gui/e-task-table.c | 84 | ||||
-rw-r--r-- | calendar/gui/e-task-table.h | 8 | ||||
-rw-r--r-- | composer/e-composer-private.c | 42 | ||||
-rw-r--r-- | composer/e-composer-private.h | 5 | ||||
-rw-r--r-- | composer/e-msg-composer.c | 134 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | mail/message-list.c | 64 | ||||
-rw-r--r-- | mail/message-list.h | 4 | ||||
-rw-r--r-- | widgets/misc/e-focus-tracker.c | 14 | ||||
-rw-r--r-- | widgets/misc/e-selectable.c | 58 | ||||
-rw-r--r-- | widgets/misc/e-selectable.h | 4 | ||||
-rw-r--r-- | widgets/misc/e-web-view.c | 243 | ||||
-rw-r--r-- | widgets/misc/e-web-view.h | 10 |
19 files changed, 823 insertions, 119 deletions
diff --git a/addressbook/gui/widgets/e-addressbook-view.c b/addressbook/gui/widgets/e-addressbook-view.c index cc540c14c3..48058ad415 100644 --- a/addressbook/gui/widgets/e-addressbook-view.c +++ b/addressbook/gui/widgets/e-addressbook-view.c @@ -36,6 +36,7 @@ #include "addressbook/printing/e-contact-print.h" #include "ea-addressbook.h" +#include "e-util/e-binding.h" #include "e-util/e-print.h" #include "e-util/e-selection.h" #include "e-util/e-util.h" @@ -90,11 +91,16 @@ struct _EAddressbookViewPrivate { gchar *search_text; gint search_id; EFilterRule *advanced_search; + + GtkTargetList *copy_target_list; + GtkTargetList *paste_target_list; }; enum { PROP_0, + PROP_COPY_TARGET_LIST, PROP_MODEL, + PROP_PASTE_TARGET_LIST, PROP_SHELL_VIEW, PROP_SOURCE }; @@ -437,21 +443,38 @@ addressbook_view_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_COPY_TARGET_LIST: + g_value_set_boxed ( + value, + e_addressbook_view_get_copy_target_list ( + E_ADDRESSBOOK_VIEW (object))); + return; + case PROP_MODEL: g_value_set_object ( - value, e_addressbook_view_get_model ( + value, + e_addressbook_view_get_model ( + E_ADDRESSBOOK_VIEW (object))); + return; + + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed ( + value, + e_addressbook_view_get_paste_target_list ( E_ADDRESSBOOK_VIEW (object))); return; case PROP_SHELL_VIEW: g_value_set_object ( - value, e_addressbook_view_get_shell_view ( + value, + e_addressbook_view_get_shell_view ( E_ADDRESSBOOK_VIEW (object))); return; case PROP_SOURCE: g_value_set_object ( - value, e_addressbook_view_get_source ( + value, + e_addressbook_view_get_source ( E_ADDRESSBOOK_VIEW (object))); return; } @@ -511,6 +534,16 @@ addressbook_view_dispose (GObject *object) priv->advanced_search = NULL; } + if (priv->copy_target_list != NULL) { + gtk_target_list_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + } + + if (priv->paste_target_list != NULL) { + gtk_target_list_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -548,12 +581,14 @@ addressbook_view_update_actions (ESelectable *selectable, EAddressbookModel *model; ESelectionModel *selection_model; GtkAction *action; - gboolean clipboard_has_directory; + GtkTargetList *target_list; + gboolean can_paste = FALSE; gboolean source_is_editable; gboolean sensitive; const gchar *tooltip; gint n_contacts; gint n_selected; + gint ii; view = E_ADDRESSBOOK_VIEW (selectable); model = e_addressbook_view_get_model (view); @@ -565,9 +600,10 @@ addressbook_view_update_actions (ESelectable *selectable, 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); + target_list = e_selectable_get_paste_target_list (selectable); + for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++) + can_paste = gtk_target_list_find ( + target_list, clipboard_targets[ii], NULL); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); sensitive = source_is_editable && (n_selected > 0); @@ -582,7 +618,7 @@ addressbook_view_update_actions (ESelectable *selectable, gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); - sensitive = source_is_editable && clipboard_has_directory; + sensitive = source_is_editable && can_paste; tooltip = _("Paste contacts from the clipboard"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); @@ -702,6 +738,12 @@ addressbook_view_class_init (EAddressbookViewClass *class) object_class->dispose = addressbook_view_dispose; object_class->constructed = addressbook_view_constructed; + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + g_object_class_install_property ( object_class, PROP_MODEL, @@ -712,6 +754,12 @@ addressbook_view_class_init (EAddressbookViewClass *class) E_TYPE_ADDRESSBOOK_MODEL, G_PARAM_READABLE)); + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + g_object_class_install_property ( object_class, PROP_SHELL_VIEW, @@ -780,10 +828,20 @@ addressbook_view_class_init (EAddressbookViewClass *class) static void addressbook_view_init (EAddressbookView *view) { + GtkTargetList *target_list; + view->priv = E_ADDRESSBOOK_VIEW_GET_PRIVATE (view); view->priv->model = e_addressbook_model_new (); + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_directory_targets (target_list, 0); + view->priv->copy_target_list = target_list; + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_directory_targets (target_list, 0); + view->priv->paste_target_list = target_list; + gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); @@ -972,6 +1030,22 @@ e_addressbook_view_get_source (EAddressbookView *view) return view->priv->source; } +GtkTargetList * +e_addressbook_view_get_copy_target_list (EAddressbookView *view) +{ + g_return_val_if_fail (E_IS_ADDRESSBOOK_VIEW (view), NULL); + + return view->priv->copy_target_list; +} + +GtkTargetList * +e_addressbook_view_get_paste_target_list (EAddressbookView *view) +{ + g_return_val_if_fail (E_IS_ADDRESSBOOK_VIEW (view), NULL); + + return view->priv->paste_target_list; +} + static void status_message (EAddressbookView *view, const gchar *status) diff --git a/addressbook/gui/widgets/e-addressbook-view.h b/addressbook/gui/widgets/e-addressbook-view.h index 55f114a538..4b85293cd5 100644 --- a/addressbook/gui/widgets/e-addressbook-view.h +++ b/addressbook/gui/widgets/e-addressbook-view.h @@ -96,6 +96,10 @@ ESelectionModel * EShellView * e_addressbook_view_get_shell_view (EAddressbookView *view); ESource * e_addressbook_view_get_source (EAddressbookView *view); +GtkTargetList * e_addressbook_view_get_copy_target_list + (EAddressbookView *view); +GtkTargetList * e_addressbook_view_get_paste_target_list + (EAddressbookView *view); void e_addressbook_view_view (EAddressbookView *view); void e_addressbook_view_print (EAddressbookView *view, gboolean selection_only, diff --git a/calendar/gui/e-calendar-view.c b/calendar/gui/e-calendar-view.c index 57973828a3..fd8650edad 100644 --- a/calendar/gui/e-calendar-view.c +++ b/calendar/gui/e-calendar-view.c @@ -74,11 +74,16 @@ struct _ECalendarViewPrivate { /* The default category */ gchar *default_category; + + GtkTargetList *copy_target_list; + GtkTargetList *paste_target_list; }; enum { PROP_0, - PROP_MODEL + PROP_COPY_TARGET_LIST, + PROP_MODEL, + PROP_PASTE_TARGET_LIST }; /* FIXME Why are we emitting these event signals here? Can't the model just be listened to? */ @@ -268,11 +273,23 @@ calendar_view_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_COPY_TARGET_LIST: + g_value_set_boxed ( + value, e_calendar_view_get_copy_target_list ( + E_CALENDAR_VIEW (object))); + return; + case PROP_MODEL: g_value_set_object ( value, e_calendar_view_get_model ( E_CALENDAR_VIEW (object))); return; + + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed ( + value, e_calendar_view_get_paste_target_list ( + E_CALENDAR_VIEW (object))); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -293,6 +310,16 @@ calendar_view_dispose (GObject *object) priv->model = NULL; } + if (priv->copy_target_list != NULL) { + gtk_target_list_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + } + + if (priv->paste_target_list != NULL) { + gtk_target_list_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_calendar_view_parent_class)->dispose (object); } @@ -318,13 +345,15 @@ calendar_view_update_actions (ESelectable *selectable, { ECalendarView *view; GtkAction *action; + GtkTargetList *target_list; GList *list, *iter; + gboolean can_paste = FALSE; gboolean sources_are_editable = TRUE; - gboolean clipboard_has_calendar; gboolean recurring = FALSE; gboolean sensitive; const gchar *tooltip; gint n_selected; + gint ii; view = E_CALENDAR_VIEW (selectable); @@ -353,9 +382,10 @@ calendar_view_update_actions (ESelectable *selectable, g_list_free (list); - clipboard_has_calendar = (clipboard_targets != NULL) && - e_targets_include_calendar ( - clipboard_targets, n_clipboard_targets); + target_list = e_selectable_get_paste_target_list (selectable); + for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++) + can_paste = gtk_target_list_find ( + target_list, clipboard_targets[ii], NULL); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); sensitive = (n_selected > 0) && sources_are_editable; @@ -370,7 +400,7 @@ calendar_view_update_actions (ESelectable *selectable, gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); - sensitive = sources_are_editable && clipboard_has_calendar; + sensitive = sources_are_editable && can_paste; tooltip = _("Paste events from the clipboard"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); @@ -718,6 +748,12 @@ e_calendar_view_class_init (ECalendarViewClass *class) class->open_event = e_calendar_view_open_event; class->paste_text = NULL; + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + g_object_class_install_property ( object_class, PROP_MODEL, @@ -729,6 +765,12 @@ e_calendar_view_class_init (ECalendarViewClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + signals[POPUP_EVENT] = g_signal_new ( "popup-event", G_TYPE_FROM_CLASS (class), @@ -820,7 +862,17 @@ e_calendar_view_class_init (ECalendarViewClass *class) static void e_calendar_view_init (ECalendarView *calendar_view) { + GtkTargetList *target_list; + calendar_view->priv = E_CALENDAR_VIEW_GET_PRIVATE (calendar_view); + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_calendar_targets (target_list, 0); + calendar_view->priv->copy_target_list = target_list; + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_calendar_targets (target_list, 0); + calendar_view->priv->paste_target_list = target_list; } static void @@ -1017,7 +1069,8 @@ const gchar * e_calendar_view_get_default_category (ECalendarView *cal_view) { g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); - return (const gchar *) cal_view->priv->default_category; + + return cal_view->priv->default_category; } /** @@ -1029,16 +1082,31 @@ e_calendar_view_get_default_category (ECalendarView *cal_view) * components from the given calendar view. */ void -e_calendar_view_set_default_category (ECalendarView *cal_view, const gchar *category) +e_calendar_view_set_default_category (ECalendarView *cal_view, + const gchar *category) { g_return_if_fail (E_IS_CALENDAR_VIEW (cal_view)); - if (cal_view->priv->default_category) - g_free (cal_view->priv->default_category); - + g_free (cal_view->priv->default_category); cal_view->priv->default_category = g_strdup (category); } +GtkTargetList * +e_calendar_view_get_copy_target_list (ECalendarView *cal_view) +{ + g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); + + return cal_view->priv->copy_target_list; +} + +GtkTargetList * +e_calendar_view_get_paste_target_list (ECalendarView *cal_view) +{ + g_return_val_if_fail (E_IS_CALENDAR_VIEW (cal_view), NULL); + + return cal_view->priv->paste_target_list; +} + GList * e_calendar_view_get_selected_events (ECalendarView *cal_view) { diff --git a/calendar/gui/e-calendar-view.h b/calendar/gui/e-calendar-view.h index d79a6fdc9d..62fcabcc37 100644 --- a/calendar/gui/e-calendar-view.h +++ b/calendar/gui/e-calendar-view.h @@ -159,6 +159,10 @@ void e_calendar_view_set_status_message (ECalendarView *cal_view, const gchar *message, gint percent); +GtkTargetList * e_calendar_view_get_copy_target_list + (ECalendarView *cal_view); +GtkTargetList * e_calendar_view_get_paste_target_list + (ECalendarView *cal_view); GList * e_calendar_view_get_selected_events (ECalendarView *cal_view); diff --git a/calendar/gui/e-memo-table.c b/calendar/gui/e-memo-table.c index 5f2cd26baf..11c74820a6 100644 --- a/calendar/gui/e-memo-table.c +++ b/calendar/gui/e-memo-table.c @@ -67,11 +67,16 @@ struct _EMemoTablePrivate { gpointer shell_view; /* weak pointer */ ECalModel *model; + + GtkTargetList *copy_target_list; + GtkTargetList *paste_target_list; }; enum { PROP_0, + PROP_COPY_TARGET_LIST, PROP_MODEL, + PROP_PASTE_TARGET_LIST, PROP_SHELL_VIEW }; @@ -300,12 +305,24 @@ memo_table_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_COPY_TARGET_LIST: + g_value_set_boxed ( + value, e_memo_table_get_copy_target_list ( + E_MEMO_TABLE (object))); + return; + case PROP_MODEL: g_value_set_object ( value, e_memo_table_get_model ( E_MEMO_TABLE (object))); return; + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed ( + value, e_memo_table_get_paste_target_list ( + E_MEMO_TABLE (object))); + return; + case PROP_SHELL_VIEW: g_value_set_object ( value, e_memo_table_get_shell_view ( @@ -334,6 +351,16 @@ memo_table_dispose (GObject *object) priv->model = NULL; } + if (priv->copy_target_list != NULL) { + gtk_target_list_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + } + + if (priv->paste_target_list != NULL) { + gtk_target_list_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -689,12 +716,14 @@ memo_table_update_actions (ESelectable *selectable, { EMemoTable *memo_table; GtkAction *action; + GtkTargetList *target_list; GSList *list, *iter; + gboolean can_paste = FALSE; gboolean sources_are_editable = TRUE; - gboolean clipboard_has_calendar; gboolean sensitive; const gchar *tooltip; gint n_selected; + gint ii; memo_table = E_MEMO_TABLE (selectable); n_selected = e_table_selected_count (E_TABLE (memo_table)); @@ -709,9 +738,10 @@ memo_table_update_actions (ESelectable *selectable, } g_slist_free (list); - clipboard_has_calendar = (clipboard_targets != NULL) && - e_targets_include_calendar ( - clipboard_targets, n_clipboard_targets); + target_list = e_selectable_get_paste_target_list (selectable); + for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++) + can_paste = gtk_target_list_find ( + target_list, clipboard_targets[ii], NULL); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); sensitive = (n_selected > 0) && sources_are_editable; @@ -726,7 +756,7 @@ memo_table_update_actions (ESelectable *selectable, gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); - sensitive = sources_are_editable && clipboard_has_calendar; + sensitive = sources_are_editable && can_paste; tooltip = _("Paste memos from the clipboard"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); @@ -1046,6 +1076,12 @@ memo_table_class_init (EMemoTableClass *class) table_class->double_click = memo_table_double_click; table_class->right_click = memo_table_right_click; + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + g_object_class_install_property ( object_class, PROP_MODEL, @@ -1057,6 +1093,12 @@ memo_table_class_init (EMemoTableClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + g_object_class_install_property ( object_class, PROP_SHELL_VIEW, @@ -1111,7 +1153,17 @@ memo_table_class_init (EMemoTableClass *class) static void memo_table_init (EMemoTable *memo_table) { + GtkTargetList *target_list; + memo_table->priv = E_MEMO_TABLE_GET_PRIVATE (memo_table); + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_calendar_targets (target_list, 0); + memo_table->priv->copy_target_list = target_list; + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_calendar_targets (target_list, 0); + memo_table->priv->paste_target_list = target_list; } static void @@ -1249,3 +1301,19 @@ e_memo_table_get_selected (EMemoTable *memo_table) return closure.objects; } + +GtkTargetList * +e_memo_table_get_copy_target_list (EMemoTable *memo_table) +{ + g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL); + + return memo_table->priv->copy_target_list; +} + +GtkTargetList * +e_memo_table_get_paste_target_list (EMemoTable *memo_table) +{ + g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL); + + return memo_table->priv->paste_target_list; +} diff --git a/calendar/gui/e-memo-table.h b/calendar/gui/e-memo-table.h index c891117203..b0e4ba6a6a 100644 --- a/calendar/gui/e-memo-table.h +++ b/calendar/gui/e-memo-table.h @@ -103,6 +103,10 @@ void e_memo_table_set_use_24_hour_format (EMemoTable *memo_table, gboolean use_24_hour_format); GSList * e_memo_table_get_selected (EMemoTable *memo_table); +GtkTargetList * e_memo_table_get_copy_target_list + (EMemoTable *memo_table); +GtkTargetList * e_memo_table_get_paste_target_list + (EMemoTable *memo_table); G_END_DECLS diff --git a/calendar/gui/e-task-table.c b/calendar/gui/e-task-table.c index 9f0962dcb8..4a378f2daf 100644 --- a/calendar/gui/e-task-table.c +++ b/calendar/gui/e-task-table.c @@ -69,11 +69,16 @@ struct _ETaskTablePrivate { gpointer shell_view; /* weak pointer */ ECalModel *model; + + GtkTargetList *copy_target_list; + GtkTargetList *paste_target_list; }; enum { PROP_0, + PROP_COPY_TARGET_LIST, PROP_MODEL, + PROP_PASTE_TARGET_LIST, PROP_SHELL_VIEW }; @@ -338,12 +343,24 @@ task_table_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_COPY_TARGET_LIST: + g_value_set_boxed ( + value, e_task_table_get_copy_target_list ( + E_TASK_TABLE (object))); + return; + case PROP_MODEL: g_value_set_object ( value, e_task_table_get_model ( E_TASK_TABLE (object))); return; + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed ( + value, e_task_table_get_paste_target_list ( + E_TASK_TABLE (object))); + return; + case PROP_SHELL_VIEW: g_value_set_object ( value, e_task_table_get_shell_view ( @@ -372,6 +389,16 @@ task_table_dispose (GObject *object) priv->model = NULL; } + if (priv->copy_target_list != NULL) { + gtk_target_list_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + } + + if (priv->paste_target_list != NULL) { + gtk_target_list_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -870,12 +897,14 @@ task_table_update_actions (ESelectable *selectable, { ETaskTable *task_table; GtkAction *action; + GtkTargetList *target_list; GSList *list, *iter; + gboolean can_paste = FALSE; gboolean sources_are_editable = TRUE; - gboolean clipboard_has_calendar; gboolean sensitive; const gchar *tooltip; gint n_selected; + gint ii; task_table = E_TASK_TABLE (selectable); n_selected = e_table_selected_count (E_TABLE (task_table)); @@ -890,9 +919,10 @@ task_table_update_actions (ESelectable *selectable, } g_slist_free (list); - clipboard_has_calendar = (clipboard_targets != NULL) && - e_targets_include_calendar ( - clipboard_targets, n_clipboard_targets); + target_list = e_selectable_get_paste_target_list (selectable); + for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++) + can_paste = gtk_target_list_find ( + target_list, clipboard_targets[ii], NULL); action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); sensitive = (n_selected > 0) && sources_are_editable; @@ -907,7 +937,7 @@ task_table_update_actions (ESelectable *selectable, gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_paste_clipboard_action (focus_tracker); - sensitive = sources_are_editable && clipboard_has_calendar; + sensitive = sources_are_editable && can_paste; tooltip = _("Paste tasks from the clipboard"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); @@ -1010,7 +1040,7 @@ clipboard_get_calendar_data (ETaskTable *task_table, icalcomponent_kind kind; const gchar *status_message; - g_return_if_fail (E_IS_CALENDAR_TABLE (task_table)); + g_return_if_fail (E_IS_TASK_TABLE (task_table)); if (!text || !*text) return; @@ -1305,6 +1335,12 @@ task_table_class_init (ETaskTableClass *class) table_class->double_click = task_table_double_click; table_class->right_click = task_table_right_click; + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + g_object_class_install_property ( object_class, PROP_MODEL, @@ -1316,6 +1352,12 @@ task_table_class_init (ETaskTableClass *class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + g_object_class_install_property ( object_class, PROP_SHELL_VIEW, @@ -1370,7 +1412,17 @@ task_table_class_init (ETaskTableClass *class) static void task_table_init (ETaskTable *task_table) { + GtkTargetList *target_list; + task_table->priv = E_TASK_TABLE_GET_PRIVATE (task_table); + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_calendar_targets (target_list, 0); + task_table->priv->copy_target_list = target_list; + + target_list = gtk_target_list_new (NULL, 0); + e_target_list_add_calendar_targets (target_list, 0); + task_table->priv->paste_target_list = target_list; } static void @@ -1451,7 +1503,7 @@ e_task_table_new (EShellView *shell_view, ECalModel * e_task_table_get_model (ETaskTable *task_table) { - g_return_val_if_fail (E_IS_CALENDAR_TABLE (task_table), NULL); + g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL); return task_table->priv->model; } @@ -1459,7 +1511,7 @@ e_task_table_get_model (ETaskTable *task_table) EShellView * e_task_table_get_shell_view (ETaskTable *task_table) { - g_return_val_if_fail (E_IS_CALENDAR_TABLE (task_table), NULL); + g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL); return task_table->priv->shell_view; } @@ -1506,6 +1558,22 @@ e_task_table_get_selected (ETaskTable *task_table) return closure.objects; } +GtkTargetList * +e_task_table_get_copy_target_list (ETaskTable *task_table) +{ + g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL); + + return task_table->priv->copy_target_list; +} + +GtkTargetList * +e_task_table_get_paste_target_list (ETaskTable *task_table) +{ + g_return_val_if_fail (E_IS_TASK_TABLE (task_table), NULL); + + return task_table->priv->paste_target_list; +} + static void hide_completed_rows (ECalModel *model, GList *clients_list, diff --git a/calendar/gui/e-task-table.h b/calendar/gui/e-task-table.h index b23f128b87..a112e222f8 100644 --- a/calendar/gui/e-task-table.h +++ b/calendar/gui/e-task-table.h @@ -45,10 +45,10 @@ #define E_TASK_TABLE_CLASS(cls) \ (G_TYPE_CHECK_CLASS_CAST \ ((cls), E_TYPE_TASK_TABLE, ETaskTableClass)) -#define E_IS_CALENDAR_TABLE(obj) \ +#define E_IS_TASK_TABLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE \ ((obj), E_TYPE_TASK_TABLE)) -#define E_IS_CALENDAR_TABLE_CLASS(cls) \ +#define E_IS_TASK_TABLE_CLASS(cls) \ (G_TYPE_CHECK_CLASS_TYPE \ ((cls), E_TYPE_TASK_TABLE)) #define E_TASK_TABLE_GET_CLASS(obj) \ @@ -93,6 +93,10 @@ GtkWidget * e_task_table_new (EShellView *shell_view, ECalModel * e_task_table_get_model (ETaskTable *task_table); EShellView * e_task_table_get_shell_view (ETaskTable *task_table); GSList * e_task_table_get_selected (ETaskTable *task_table); +GtkTargetList * e_task_table_get_copy_target_list + (ETaskTable *task_table); +GtkTargetList * e_task_table_get_paste_target_list + (ETaskTable *task_table); ECalModelComponent * e_task_table_get_selected_comp (ETaskTable *task_table); diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c index b0bc0c999d..0f7ffbb64c 100644 --- a/composer/e-composer-private.c +++ b/composer/e-composer-private.c @@ -509,6 +509,27 @@ e_composer_get_default_charset (void) } gboolean +e_composer_paste_html (EMsgComposer *composer, + GtkClipboard *clipboard) +{ + GtkhtmlEditor *editor; + gchar *html; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); + + html = e_clipboard_wait_for_html (clipboard); + g_return_val_if_fail (html != NULL, FALSE); + + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_insert_html (editor, html); + + g_free (html); + + return TRUE; +} + +gboolean e_composer_paste_image (EMsgComposer *composer, GtkClipboard *clipboard) { @@ -583,6 +604,27 @@ exit: } gboolean +e_composer_paste_text (EMsgComposer *composer, + GtkClipboard *clipboard) +{ + GtkhtmlEditor *editor; + gchar *text; + + g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE); + g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE); + + text = gtk_clipboard_wait_for_text (clipboard); + g_return_val_if_fail (text != NULL, FALSE); + + editor = GTKHTML_EDITOR (composer); + gtkhtml_editor_insert_text (editor, text); + + g_free (text); + + return TRUE; +} + +gboolean e_composer_paste_uris (EMsgComposer *composer, GtkClipboard *clipboard) { diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h index 1711e0c17b..cac41aabe4 100644 --- a/composer/e-composer-private.h +++ b/composer/e-composer-private.h @@ -52,6 +52,7 @@ #include "e-util/e-binding.h" #include "e-util/e-charset.h" #include "e-util/e-mktemp.h" +#include "e-util/e-selection.h" #include "e-util/e-util.h" #include "e-util/gconf-bridge.h" #include "widgets/misc/e-attachment-icon-view.h" @@ -157,8 +158,12 @@ void e_composer_private_finalize (EMsgComposer *composer); void e_composer_actions_init (EMsgComposer *composer); gchar * e_composer_find_data_file (const gchar *basename); gchar * e_composer_get_default_charset (void); +gboolean e_composer_paste_html (EMsgComposer *composer, + GtkClipboard *clipboard); gboolean e_composer_paste_image (EMsgComposer *composer, GtkClipboard *clipboard); +gboolean e_composer_paste_text (EMsgComposer *composer, + GtkClipboard *clipboard); gboolean e_composer_paste_uris (EMsgComposer *composer, GtkClipboard *clipboard); diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c index bdac5d44ab..b57bd156a5 100644 --- a/composer/e-msg-composer.c +++ b/composer/e-msg-composer.c @@ -1488,6 +1488,60 @@ msg_composer_account_changed_cb (EMsgComposer *composer) e_msg_composer_show_sig_file (composer); } +static void +msg_composer_paste_clipboard_targets_cb (GtkClipboard *clipboard, + GdkAtom *targets, + gint n_targets, + EMsgComposer *composer) +{ + GtkhtmlEditor *editor; + gboolean html_mode; + + editor = GTKHTML_EDITOR (composer); + html_mode = gtkhtml_editor_get_html_mode (editor); + + /* Order is important here to ensure common use cases are + * handled correctly. See GNOME bug #603715 for details. */ + + if (gtk_targets_include_uri (targets, n_targets)) { + e_composer_paste_uris (composer, clipboard); + return; + } + + /* Only paste HTML content in HTML mode. */ + if (html_mode) { + if (e_targets_include_html (targets, n_targets)) { + e_composer_paste_html (composer, clipboard); + return; + } + } + + if (gtk_targets_include_text (targets, n_targets)) { + e_composer_paste_text (composer, clipboard); + return; + } + + if (gtk_targets_include_image (targets, n_targets, TRUE)) { + e_composer_paste_image (composer, clipboard); + return; + } +} + +static void +msg_composer_paste_clipboard_cb (EWebView *web_view, + EMsgComposer *composer) +{ + GtkClipboard *clipboard; + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + gtk_clipboard_request_targets ( + clipboard, (GtkClipboardTargetsReceivedFunc) + msg_composer_paste_clipboard_targets_cb, composer); + + g_signal_stop_emission_by_name (web_view, "paste-clipboard"); +} + struct _drop_data { EMsgComposer *composer; @@ -1616,6 +1670,12 @@ msg_composer_constructed (GObject *object) shell_settings, "composer-request-receipt"); gtk_toggle_action_set_active (action, active); + /* Clipboard Support */ + + g_signal_connect ( + html, "paste-clipboard", + G_CALLBACK (msg_composer_paste_clipboard_cb), composer); + /* Drag-and-Drop Support */ target_list = e_attachment_view_get_target_list (view); @@ -1840,91 +1900,25 @@ msg_composer_drag_data_received (GtkWidget *widget, static void msg_composer_cut_clipboard (GtkhtmlEditor *editor) { - EMsgComposer *composer; - GtkWidget *parent; - GtkWidget *widget; - - composer = E_MSG_COMPOSER (editor); - widget = gtk_window_get_focus (GTK_WINDOW (editor)); - parent = gtk_widget_get_parent (widget); - - /* EFocusTracker handles the header widgets. */ - if (parent == composer->priv->header_table) - return; - - /* Chain up to parent's cut_clipboard() method. */ - GTKHTML_EDITOR_CLASS (parent_class)->cut_clipboard (editor); + /* Do nothing. EFocusTracker handles this. */ } static void msg_composer_copy_clipboard (GtkhtmlEditor *editor) { - EMsgComposer *composer; - GtkWidget *parent; - GtkWidget *widget; - - composer = E_MSG_COMPOSER (editor); - widget = gtk_window_get_focus (GTK_WINDOW (editor)); - parent = gtk_widget_get_parent (widget); - - /* EFocusTracker handles the header widgets. */ - if (parent == composer->priv->header_table) - return; - - /* Chain up to parent's copy_clipboard() method. */ - GTKHTML_EDITOR_CLASS (parent_class)->copy_clipboard (editor); + /* Do nothing. EFocusTracker handles this. */ } static void msg_composer_paste_clipboard (GtkhtmlEditor *editor) { - EMsgComposer *composer; - GtkClipboard *clipboard; - GtkWidget *parent; - GtkWidget *widget; - - composer = E_MSG_COMPOSER (editor); - - widget = gtk_window_get_focus (GTK_WINDOW (editor)); - parent = gtk_widget_get_parent (widget); - - /* EFocusTracker handles the header widgets. */ - if (parent == composer->priv->header_table) - return; - - clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD); - - if (gtk_clipboard_wait_is_image_available (clipboard)) { - e_composer_paste_image (composer, clipboard); - return; - } - - if (gtk_clipboard_wait_is_uris_available (clipboard)) { - e_composer_paste_uris (composer, clipboard); - return; - } - - /* Chain up to parent's paste_clipboard() method. */ - GTKHTML_EDITOR_CLASS (parent_class)->paste_clipboard (editor); + /* Do nothing. EFocusTracker handles this. */ } static void msg_composer_select_all (GtkhtmlEditor *editor) { - EMsgComposer *composer; - GtkWidget *parent; - GtkWidget *widget; - - composer = E_MSG_COMPOSER (editor); - widget = gtk_window_get_focus (GTK_WINDOW (editor)); - parent = gtk_widget_get_parent (widget); - - /* EFocusTracker handles the header widgets. */ - if (parent == composer->priv->header_table) - return; - - /* Chain up to the parent's select_all() method. */ - GTKHTML_EDITOR_CLASS (parent_class)->select_all (editor); + /* Do nothing. EFocusTracker handles this. */ } static void diff --git a/configure.ac b/configure.ac index e32b4295e3..826ac56b85 100644 --- a/configure.ac +++ b/configure.ac @@ -40,7 +40,7 @@ m4_define([gtk_minimum_version], [2.18.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.5]) +m4_define([libgtkhtml_minimum_version], [3.29.6]) m4_define([gconf_minimum_version], [2.0.0]) dnl XXX Just a Guess m4_define([libsoup_minimum_version], [2.4.0]) dnl XXX Just a Guess m4_define([libgnomecanvas_minimum_version], [2.0.0]) dnl XXX Just a Guess diff --git a/mail/message-list.c b/mail/message-list.c index 288b4a5420..9046b69894 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -117,10 +117,15 @@ struct _MessageListPrivate { gboolean thread_latest; gboolean any_row_changed; /* save state before regen list when this is set to true */ + + GtkTargetList *copy_target_list; + GtkTargetList *paste_target_list; }; enum { PROP_0, + PROP_COPY_TARGET_LIST, + PROP_PASTE_TARGET_LIST, PROP_SHELL_BACKEND }; @@ -2295,6 +2300,7 @@ static void message_list_init (MessageList *message_list) { MessageListPrivate *p; + GtkTargetList *target_list; GdkAtom matom; message_list->priv = MESSAGE_LIST_GET_PRIVATE (message_list); @@ -2336,6 +2342,14 @@ message_list_init (MessageList *message_list) g_signal_connect(p->invisible, "selection_get", G_CALLBACK(ml_selection_get), message_list); g_signal_connect(p->invisible, "selection_clear_event", G_CALLBACK(ml_selection_clear_event), message_list); g_signal_connect(p->invisible, "selection_received", G_CALLBACK(ml_selection_received), message_list); + + /* FIXME This is currently unused. */ + target_list = gtk_target_list_new (NULL, 0); + message_list->priv->copy_target_list = target_list; + + /* FIXME This is currently unused. */ + target_list = gtk_target_list_new (NULL, 0); + message_list->priv->paste_target_list = target_list; } static void @@ -2418,6 +2432,18 @@ message_list_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_COPY_TARGET_LIST: + g_value_set_boxed ( + value, message_list_get_copy_target_list ( + MESSAGE_LIST (object))); + return; + + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed ( + value, message_list_get_paste_target_list ( + MESSAGE_LIST (object))); + return; + case PROP_SHELL_BACKEND: g_value_set_object ( value, message_list_get_shell_backend ( @@ -2440,6 +2466,16 @@ message_list_dispose (GObject *object) priv->shell_backend = NULL; } + if (priv->copy_target_list != NULL) { + g_object_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + } + + if (priv->paste_target_list != NULL) { + g_object_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -2533,6 +2569,18 @@ message_list_class_init (MessageListClass *class) class->message_list_built = message_list_built; + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + g_object_class_install_property ( object_class, PROP_SHELL_BACKEND, @@ -3614,6 +3662,22 @@ message_list_set_folder (MessageList *message_list, CamelFolder *folder, const g } } +GtkTargetList * +message_list_get_copy_target_list (MessageList *message_list) +{ + g_return_val_if_fail (IS_MESSAGE_LIST (message_list), NULL); + + return message_list->priv->copy_target_list; +} + +GtkTargetList * +message_list_get_paste_target_list (MessageList *message_list) +{ + g_return_val_if_fail (IS_MESSAGE_LIST (message_list), NULL); + + return message_list->priv->paste_target_list; +} + static gboolean on_cursor_activated_idle (gpointer data) { diff --git a/mail/message-list.h b/mail/message-list.h index 953a444e11..c7efc5dc14 100644 --- a/mail/message-list.h +++ b/mail/message-list.h @@ -189,6 +189,10 @@ void message_list_set_folder (MessageList *message_list, CamelFolder *camel_folder, const gchar *uri, gboolean outgoing); +GtkTargetList * message_list_get_copy_target_list + (MessageList *message_list); +GtkTargetList * message_list_get_paste_target_list + (MessageList *message_list); void message_list_freeze (MessageList *message_list); void message_list_thaw (MessageList *message_list); GPtrArray * message_list_get_uids (MessageList *message_list); diff --git a/widgets/misc/e-focus-tracker.c b/widgets/misc/e-focus-tracker.c index d1cb32adde..8eb1b25d56 100644 --- a/widgets/misc/e-focus-tracker.c +++ b/widgets/misc/e-focus-tracker.c @@ -360,6 +360,10 @@ focus_tracker_dispose (GObject *object) gtk_clipboard_get (GDK_SELECTION_PRIMARY), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); + g_signal_handlers_disconnect_matched ( + gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), + G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); + if (priv->window != NULL) { g_signal_handlers_disconnect_matched ( priv->window, G_SIGNAL_MATCH_DATA, @@ -427,6 +431,16 @@ focus_tracker_constructed (GObject *object) g_signal_connect_swapped ( clipboard, "owner-change", G_CALLBACK (e_focus_tracker_update_actions), object); + + /* Listen for "owner-change" signals from the default clipboard + * so we can update the paste action when the user cuts or copies + * something. This is how GEdit does it. */ + + clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD); + + g_signal_connect_swapped ( + clipboard, "owner-change", + G_CALLBACK (e_focus_tracker_update_actions), object); } static void diff --git a/widgets/misc/e-selectable.c b/widgets/misc/e-selectable.c index bb5948cc13..da998d320d 100644 --- a/widgets/misc/e-selectable.c +++ b/widgets/misc/e-selectable.c @@ -21,6 +21,28 @@ #include "e-selectable.h" +static void +selectable_class_init (ESelectableInterface *interface) +{ + g_object_interface_install_property ( + interface, + g_param_spec_boxed ( + "copy-target-list", + "Copy Target List", + NULL, + GTK_TYPE_TARGET_LIST, + G_PARAM_READABLE)); + + g_object_interface_install_property ( + interface, + g_param_spec_boxed ( + "paste-target-list", + "Paste Target List", + NULL, + GTK_TYPE_TARGET_LIST, + G_PARAM_READABLE)); +} + GType e_selectable_get_type (void) { @@ -31,7 +53,7 @@ e_selectable_get_type (void) sizeof (ESelectableInterface), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, - (GClassInitFunc) NULL, + (GClassInitFunc) selectable_class_init, (GClassFinalizeFunc) NULL, NULL, /* class_data */ 0, /* instance_size */ @@ -43,7 +65,7 @@ e_selectable_get_type (void) type = g_type_register_static ( G_TYPE_INTERFACE, "ESelectable", &type_info, 0); - g_type_interface_add_prerequisite (type, G_TYPE_OBJECT); + g_type_interface_add_prerequisite (type, GTK_TYPE_WIDGET); } return type; @@ -131,3 +153,35 @@ e_selectable_select_all (ESelectable *selectable) if (interface->select_all != NULL) interface->select_all (selectable); } + +GtkTargetList * +e_selectable_get_copy_target_list (ESelectable *selectable) +{ + GtkTargetList *target_list; + + g_return_val_if_fail (E_IS_SELECTABLE (selectable), NULL); + + g_object_get (selectable, "copy-target-list", &target_list, NULL); + + /* We want to return a borrowed reference to the target + * list, so undo the reference that g_object_get() added. */ + gtk_target_list_unref (target_list); + + return target_list; +} + +GtkTargetList * +e_selectable_get_paste_target_list (ESelectable *selectable) +{ + GtkTargetList *target_list; + + g_return_val_if_fail (E_IS_SELECTABLE (selectable), NULL); + + g_object_get (selectable, "paste-target-list", &target_list, NULL); + + /* We want to return a borrowed reference to the target + * list, so undo the reference that g_object_get() added. */ + gtk_target_list_unref (target_list); + + return target_list; +} diff --git a/widgets/misc/e-selectable.h b/widgets/misc/e-selectable.h index fca400ce65..c9a0b6dded 100644 --- a/widgets/misc/e-selectable.h +++ b/widgets/misc/e-selectable.h @@ -70,6 +70,10 @@ 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); +GtkTargetList * e_selectable_get_copy_target_list + (ESelectable *selectable); +GtkTargetList * e_selectable_get_paste_target_list + (ESelectable *selectable); G_END_DECLS diff --git a/widgets/misc/e-web-view.c b/widgets/misc/e-web-view.c index 31516bf04d..a5f98a4edc 100644 --- a/widgets/misc/e-web-view.c +++ b/widgets/misc/e-web-view.c @@ -50,6 +50,9 @@ struct _EWebViewPrivate { GtkAction *print_proxy; GtkAction *save_as_proxy; + GtkTargetList *copy_target_list; + GtkTargetList *paste_target_list; + /* Lockdown Options */ guint disable_printing : 1; guint disable_save_to_disk : 1; @@ -68,15 +71,21 @@ enum { PROP_0, PROP_ANIMATE, PROP_CARET_MODE, + PROP_COPY_TARGET_LIST, PROP_DISABLE_PRINTING, PROP_DISABLE_SAVE_TO_DISK, + PROP_EDITABLE, PROP_OPEN_PROXY, + PROP_PASTE_TARGET_LIST, PROP_PRINT_PROXY, PROP_SAVE_AS_PROXY, PROP_SELECTED_URI }; enum { + COPY_CLIPBOARD, + CUT_CLIPBOARD, + PASTE_CLIPBOARD, POPUP_EVENT, STATUS_MESSAGE, STOP_LOADING, @@ -488,6 +497,12 @@ web_view_set_property (GObject *object, g_value_get_boolean (value)); return; + case PROP_EDITABLE: + e_web_view_set_editable ( + E_WEB_VIEW (object), + g_value_get_boolean (value)); + return; + case PROP_OPEN_PROXY: e_web_view_set_open_proxy ( E_WEB_VIEW (object), @@ -535,6 +550,12 @@ web_view_get_property (GObject *object, E_WEB_VIEW (object))); return; + case PROP_COPY_TARGET_LIST: + g_value_set_boxed ( + value, e_web_view_get_copy_target_list ( + E_WEB_VIEW (object))); + return; + case PROP_DISABLE_PRINTING: g_value_set_boolean ( value, e_web_view_get_disable_printing ( @@ -547,12 +568,24 @@ web_view_get_property (GObject *object, E_WEB_VIEW (object))); return; + case PROP_EDITABLE: + g_value_set_boolean ( + value, e_web_view_get_editable ( + E_WEB_VIEW (object))); + return; + case PROP_OPEN_PROXY: g_value_set_object ( value, e_web_view_get_open_proxy ( E_WEB_VIEW (object))); return; + case PROP_PASTE_TARGET_LIST: + g_value_set_boxed ( + value, e_web_view_get_paste_target_list ( + E_WEB_VIEW (object))); + return; + case PROP_PRINT_PROXY: g_value_set_object ( value, e_web_view_get_print_proxy ( @@ -602,6 +635,16 @@ web_view_dispose (GObject *object) priv->save_as_proxy = NULL; } + if (priv->copy_target_list != NULL) { + gtk_target_list_unref (priv->copy_target_list); + priv->copy_target_list = NULL; + } + + if (priv->paste_target_list != NULL) { + gtk_target_list_unref (priv->paste_target_list); + priv->paste_target_list = NULL; + } + /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -765,6 +808,26 @@ web_view_extract_uri (EWebView *web_view, return uri; } +static void +web_view_copy_clipboard (EWebView *web_view) +{ + gtk_html_command (GTK_HTML (web_view), "copy"); +} + +static void +web_view_cut_clipboard (EWebView *web_view) +{ + if (e_web_view_get_editable (web_view)) + gtk_html_command (GTK_HTML (web_view), "cut"); +} + +static void +web_view_paste_clipboard (EWebView *web_view) +{ + if (e_web_view_get_editable (web_view)) + gtk_html_command (GTK_HTML (web_view), "paste"); +} + static gboolean web_view_popup_event (EWebView *web_view, GdkEventButton *event, @@ -865,39 +928,76 @@ web_view_selectable_update_actions (ESelectable *selectable, { EWebView *web_view; GtkAction *action; - const gchar *tooltip; + /*GtkTargetList *target_list;*/ + gboolean can_paste = FALSE; + gboolean editable; + gboolean have_selection; gboolean sensitive; + const gchar *tooltip; + /*gint ii;*/ web_view = E_WEB_VIEW (selectable); + editable = e_web_view_get_editable (web_view); + have_selection = e_web_view_is_selection_active (web_view); - /* Copy Clipboard */ - - action = e_web_view_get_action (web_view, "copy-clipboard"); - sensitive = gtk_action_get_sensitive (action); - tooltip = gtk_action_get_tooltip (action); + /* XXX GtkHtml implements its own clipboard instead of using + * GDK_SELECTION_CLIPBOARD, so we don't get notifications + * when the clipboard contents change. The logic below + * is what we would do if GtkHtml worked properly. + * Instead, we need to keep the Paste action sensitive so + * its accelerator overrides GtkHtml's key binding. */ +#if 0 + target_list = e_selectable_get_paste_target_list (selectable); + for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++) + can_paste = gtk_target_list_find ( + target_list, clipboard_targets[ii], NULL); +#endif + can_paste = TRUE; + + action = e_focus_tracker_get_cut_clipboard_action (focus_tracker); + sensitive = editable && have_selection; + tooltip = _("Cut the selection"); + gtk_action_set_sensitive (action, sensitive); + gtk_action_set_tooltip (action, tooltip); action = e_focus_tracker_get_copy_clipboard_action (focus_tracker); + sensitive = have_selection; + tooltip = _("Copy the selection"); 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_paste_clipboard_action (focus_tracker); + sensitive = editable && can_paste; + tooltip = _("Paste 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 text and images"); gtk_action_set_sensitive (action, sensitive); gtk_action_set_tooltip (action, tooltip); } static void +web_view_selectable_cut_clipboard (ESelectable *selectable) +{ + e_web_view_cut_clipboard (E_WEB_VIEW (selectable)); +} + +static void web_view_selectable_copy_clipboard (ESelectable *selectable) { e_web_view_copy_clipboard (E_WEB_VIEW (selectable)); } static void +web_view_selectable_paste_clipboard (ESelectable *selectable) +{ + e_web_view_paste_clipboard (E_WEB_VIEW (selectable)); +} + +static void web_view_selectable_select_all (ESelectable *selectable) { e_web_view_select_all (E_WEB_VIEW (selectable)); @@ -930,6 +1030,9 @@ web_view_class_init (EWebViewClass *class) html_class->iframe_created = web_view_iframe_created; class->extract_uri = web_view_extract_uri; + class->copy_clipboard = web_view_copy_clipboard; + class->cut_clipboard = web_view_cut_clipboard; + class->paste_clipboard = web_view_paste_clipboard; class->popup_event = web_view_popup_event; class->stop_loading = web_view_stop_loading; class->update_actions = web_view_update_actions; @@ -954,6 +1057,12 @@ web_view_class_init (EWebViewClass *class) FALSE, G_PARAM_READWRITE)); + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_COPY_TARGET_LIST, + "copy-target-list"); + g_object_class_install_property ( object_class, PROP_DISABLE_PRINTING, @@ -976,6 +1085,16 @@ web_view_class_init (EWebViewClass *class) g_object_class_install_property ( object_class, + PROP_EDITABLE, + g_param_spec_boolean ( + "editable", + "Editable", + NULL, + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, PROP_OPEN_PROXY, g_param_spec_object ( "open-proxy", @@ -984,6 +1103,12 @@ web_view_class_init (EWebViewClass *class) GTK_TYPE_ACTION, G_PARAM_READWRITE)); + /* Inherited from ESelectableInterface */ + g_object_class_override_property ( + object_class, + PROP_PASTE_TARGET_LIST, + "paste-target-list"); + g_object_class_install_property ( object_class, PROP_PRINT_PROXY, @@ -1014,6 +1139,33 @@ web_view_class_init (EWebViewClass *class) NULL, G_PARAM_READWRITE)); + signals[COPY_CLIPBOARD] = g_signal_new ( + "copy-clipboard", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EWebViewClass, copy_clipboard), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[CUT_CLIPBOARD] = g_signal_new ( + "cut-clipboard", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EWebViewClass, cut_clipboard), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[PASTE_CLIPBOARD] = g_signal_new ( + "paste-clipboard", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EWebViewClass, paste_clipboard), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[POPUP_EVENT] = g_signal_new ( "popup-event", G_TYPE_FROM_CLASS (class), @@ -1058,7 +1210,9 @@ static void web_view_selectable_init (ESelectableInterface *interface) { interface->update_actions = web_view_selectable_update_actions; + interface->cut_clipboard = web_view_selectable_cut_clipboard; interface->copy_clipboard = web_view_selectable_copy_clipboard; + interface->paste_clipboard = web_view_selectable_paste_clipboard; interface->select_all = web_view_selectable_select_all; } @@ -1067,6 +1221,7 @@ web_view_init (EWebView *web_view) { GtkUIManager *ui_manager; GtkActionGroup *action_group; + GtkTargetList *target_list; EPopupAction *popup_action; const gchar *domain = GETTEXT_PACKAGE; const gchar *id; @@ -1081,6 +1236,12 @@ web_view_init (EWebView *web_view) ui_manager, "connect-proxy", G_CALLBACK (web_view_connect_proxy_cb), web_view); + target_list = gtk_target_list_new (NULL, 0); + web_view->priv->copy_target_list = target_list; + + target_list = gtk_target_list_new (NULL, 0); + web_view->priv->paste_target_list = target_list; + action_group = gtk_action_group_new ("uri"); gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); @@ -1287,6 +1448,14 @@ e_web_view_set_caret_mode (EWebView *web_view, g_object_notify (G_OBJECT (web_view), "caret-mode"); } +GtkTargetList * +e_web_view_get_copy_target_list (EWebView *web_view) +{ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + return web_view->priv->copy_target_list; +} + gboolean e_web_view_get_disable_printing (EWebView *web_view) { @@ -1325,6 +1494,32 @@ e_web_view_set_disable_save_to_disk (EWebView *web_view, g_object_notify (G_OBJECT (web_view), "disable-save-to-disk"); } +gboolean +e_web_view_get_editable (EWebView *web_view) +{ + /* XXX This is just here to maintain symmetry + * with e_web_view_set_editable(). */ + + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE); + + return gtk_html_get_editable (GTK_HTML (web_view)); +} + +void +e_web_view_set_editable (EWebView *web_view, + gboolean editable) +{ + /* XXX GtkHTML does not utilize GObject properties as well + * as it could. This just wraps gtk_html_set_editable() + * so we can get a "notify::editable" signal. */ + + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + gtk_html_set_editable (GTK_HTML (web_view), editable); + + g_object_notify (G_OBJECT (web_view), "editable"); +} + const gchar * e_web_view_get_selected_uri (EWebView *web_view) { @@ -1372,6 +1567,14 @@ e_web_view_set_open_proxy (EWebView *web_view, g_object_notify (G_OBJECT (web_view), "open-proxy"); } +GtkTargetList * +e_web_view_get_paste_target_list (EWebView *web_view) +{ + g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); + + return web_view->priv->paste_target_list; +} + GtkAction * e_web_view_get_print_proxy (EWebView *web_view) { @@ -1477,7 +1680,15 @@ e_web_view_copy_clipboard (EWebView *web_view) { g_return_if_fail (E_IS_WEB_VIEW (web_view)); - gtk_html_command (GTK_HTML (web_view), "copy"); + g_signal_emit (web_view, signals[COPY_CLIPBOARD], 0); +} + +void +e_web_view_cut_clipboard (EWebView *web_view) +{ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + g_signal_emit (web_view, signals[CUT_CLIPBOARD], 0); } gboolean @@ -1488,6 +1699,14 @@ e_web_view_is_selection_active (EWebView *web_view) return gtk_html_command (GTK_HTML (web_view), "is-selection-active"); } +void +e_web_view_paste_clipboard (EWebView *web_view) +{ + g_return_if_fail (E_IS_WEB_VIEW (web_view)); + + g_signal_emit (web_view, signals[PASTE_CLIPBOARD], 0); +} + gboolean e_web_view_scroll_forward (EWebView *web_view) { diff --git a/widgets/misc/e-web-view.h b/widgets/misc/e-web-view.h index 123965c7d0..788eadb1b7 100644 --- a/widgets/misc/e-web-view.h +++ b/widgets/misc/e-web-view.h @@ -71,6 +71,9 @@ struct _EWebViewClass { GtkHTML *frame); /* Signals */ + void (*copy_clipboard) (EWebView *web_view); + void (*cut_clipboard) (EWebView *web_view); + void (*paste_clipboard) (EWebView *web_view); gboolean (*popup_event) (EWebView *web_view, GdkEventButton *event, const gchar *uri); @@ -91,6 +94,7 @@ void e_web_view_set_animate (EWebView *web_view, gboolean e_web_view_get_caret_mode (EWebView *web_view); void e_web_view_set_caret_mode (EWebView *web_view, gboolean caret_mode); +GtkTargetList * e_web_view_get_copy_target_list (EWebView *web_view); gboolean e_web_view_get_disable_printing (EWebView *web_view); void e_web_view_set_disable_printing (EWebView *web_view, gboolean disable_printing); @@ -99,12 +103,16 @@ gboolean e_web_view_get_disable_save_to_disk void e_web_view_set_disable_save_to_disk (EWebView *web_view, gboolean disable_save_to_disk); +gboolean e_web_view_get_editable (EWebView *web_view); +void e_web_view_set_editable (EWebView *web_view, + gboolean editable); const gchar * e_web_view_get_selected_uri (EWebView *web_view); void e_web_view_set_selected_uri (EWebView *web_view, const gchar *selected_uri); GtkAction * e_web_view_get_open_proxy (EWebView *web_view); void e_web_view_set_open_proxy (EWebView *web_view, GtkAction *open_proxy); +GtkTargetList * e_web_view_get_paste_target_list(EWebView *web_view); GtkAction * e_web_view_get_print_proxy (EWebView *web_view); void e_web_view_set_print_proxy (EWebView *web_view, GtkAction *print_proxy); @@ -119,7 +127,9 @@ gchar * e_web_view_extract_uri (EWebView *web_view, GdkEventButton *event, GtkHTML *frame); void e_web_view_copy_clipboard (EWebView *web_view); +void e_web_view_cut_clipboard (EWebView *web_view); gboolean e_web_view_is_selection_active (EWebView *web_view); +void e_web_view_paste_clipboard (EWebView *web_view); gboolean e_web_view_scroll_forward (EWebView *web_view); gboolean e_web_view_scroll_backward (EWebView *web_view); void e_web_view_select_all (EWebView *web_view); |