aboutsummaryrefslogtreecommitdiffstats
path: root/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'widgets')
-rw-r--r--widgets/misc/e-attachment-icon-view.c15
-rw-r--r--widgets/misc/e-attachment-paned.c62
-rw-r--r--widgets/misc/e-attachment-tree-view.c15
-rw-r--r--widgets/misc/e-attachment-view.c196
-rw-r--r--widgets/misc/e-attachment-view.h9
5 files changed, 212 insertions, 85 deletions
diff --git a/widgets/misc/e-attachment-icon-view.c b/widgets/misc/e-attachment-icon-view.c
index edfecb57d8..57301213dd 100644
--- a/widgets/misc/e-attachment-icon-view.c
+++ b/widgets/misc/e-attachment-icon-view.c
@@ -130,6 +130,20 @@ attachment_icon_view_button_release_event (GtkWidget *widget,
}
static gboolean
+attachment_icon_view_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_motion_notify_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's motion_notify_event() method. */
+ return GTK_WIDGET_CLASS (parent_class)->
+ motion_notify_event (widget, event);
+}
+
+static gboolean
attachment_icon_view_key_press_event (GtkWidget *widget,
GdkEventKey *event)
{
@@ -408,6 +422,7 @@ attachment_icon_view_class_init (EAttachmentIconViewClass *class)
widget_class = GTK_WIDGET_CLASS (class);
widget_class->button_press_event = attachment_icon_view_button_press_event;
widget_class->button_release_event = attachment_icon_view_button_release_event;
+ widget_class->motion_notify_event = attachment_icon_view_motion_notify_event;
widget_class->key_press_event = attachment_icon_view_key_press_event;
widget_class->drag_begin = attachment_icon_view_drag_begin;
widget_class->drag_end = attachment_icon_view_drag_end;
diff --git a/widgets/misc/e-attachment-paned.c b/widgets/misc/e-attachment-paned.c
index 8b4c2bfb96..d47fe9c8c7 100644
--- a/widgets/misc/e-attachment-paned.c
+++ b/widgets/misc/e-attachment-paned.c
@@ -92,36 +92,6 @@ attachment_paned_notify_cb (EAttachmentPaned *paned,
}
static void
-attachment_paned_sync_icon_view (EAttachmentPaned *paned)
-{
- EAttachmentView *source;
- EAttachmentView *target;
-
- source = E_ATTACHMENT_VIEW (paned->priv->tree_view);
- target = E_ATTACHMENT_VIEW (paned->priv->icon_view);
-
- /* Only sync if the tree view is active. This prevents the
- * two views from endlessly trying to sync with each other. */
- if (e_attachment_paned_get_active_view (paned) == 1)
- e_attachment_view_sync_selection (source, target);
-}
-
-static void
-attachment_paned_sync_tree_view (EAttachmentPaned *paned)
-{
- EAttachmentView *source;
- EAttachmentView *target;
-
- source = E_ATTACHMENT_VIEW (paned->priv->icon_view);
- target = E_ATTACHMENT_VIEW (paned->priv->tree_view);
-
- /* Only sync if the icon view is active. This prevents the
- * two views from endlessly trying to sync with each other. */
- if (e_attachment_paned_get_active_view (paned) == 0)
- e_attachment_view_sync_selection (source, target);
-}
-
-static void
attachment_paned_update_status (EAttachmentPaned *paned)
{
EAttachmentView *view;
@@ -509,7 +479,6 @@ static void
attachment_paned_init (EAttachmentPaned *paned)
{
EAttachmentView *view;
- GtkTreeSelection *selection;
GtkSizeGroup *size_group;
GtkWidget *container;
GtkWidget *widget;
@@ -662,17 +631,6 @@ attachment_paned_init (EAttachmentPaned *paned)
paned->priv->status_label = g_object_ref (widget);
gtk_widget_hide (widget);
- selection = gtk_tree_view_get_selection (
- GTK_TREE_VIEW (paned->priv->tree_view));
-
- g_signal_connect_swapped (
- selection, "changed",
- G_CALLBACK (attachment_paned_sync_icon_view), paned);
-
- g_signal_connect_swapped (
- paned->priv->icon_view, "selection-changed",
- G_CALLBACK (attachment_paned_sync_tree_view), paned);
-
g_signal_connect_swapped (
paned->priv->expander, "notify::expanded",
G_CALLBACK (attachment_paned_notify_cb), paned);
@@ -750,11 +708,31 @@ void
e_attachment_paned_set_active_view (EAttachmentPaned *paned,
gint active_view)
{
+ EAttachmentView *source;
+ EAttachmentView *target;
+
g_return_if_fail (E_IS_ATTACHMENT_PANED (paned));
g_return_if_fail (active_view >= 0 && active_view < NUM_VIEWS);
+ if (active_view == paned->priv->active_view)
+ return;
+
paned->priv->active_view = active_view;
+ /* Synchronize the item selection of the view we're
+ * switching TO with the view we're switching FROM. */
+ if (active_view == 0) {
+ /* from tree view to icon view */
+ source = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+ target = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+ } else {
+ /* from icon view to tree view */
+ source = E_ATTACHMENT_VIEW (paned->priv->icon_view);
+ target = E_ATTACHMENT_VIEW (paned->priv->tree_view);
+ }
+
+ e_attachment_view_sync_selection (source, target);
+
g_object_notify (G_OBJECT (paned), "active-view");
}
diff --git a/widgets/misc/e-attachment-tree-view.c b/widgets/misc/e-attachment-tree-view.c
index 5729a68069..09602ca186 100644
--- a/widgets/misc/e-attachment-tree-view.c
+++ b/widgets/misc/e-attachment-tree-view.c
@@ -144,6 +144,20 @@ attachment_tree_view_button_release_event (GtkWidget *widget,
}
static gboolean
+attachment_tree_view_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EAttachmentView *view = E_ATTACHMENT_VIEW (widget);
+
+ if (e_attachment_view_motion_notify_event (view, event))
+ return TRUE;
+
+ /* Chain up to parent's motion_notify_event() method. */
+ return GTK_WIDGET_CLASS (parent_class)->
+ motion_notify_event (widget, event);
+}
+
+static gboolean
attachment_tree_view_key_press_event (GtkWidget *widget,
GdkEventKey *event)
{
@@ -440,6 +454,7 @@ attachment_tree_view_class_init (EAttachmentTreeViewClass *class)
widget_class = GTK_WIDGET_CLASS (class);
widget_class->button_press_event = attachment_tree_view_button_press_event;
widget_class->button_release_event = attachment_tree_view_button_release_event;
+ widget_class->motion_notify_event = attachment_tree_view_motion_notify_event;
widget_class->key_press_event = attachment_tree_view_key_press_event;
widget_class->drag_begin = attachment_tree_view_drag_begin;
widget_class->drag_end = attachment_tree_view_drag_end;
diff --git a/widgets/misc/e-attachment-view.c b/widgets/misc/e-attachment-view.c
index 5a5db57e07..2ff6fb4dab 100644
--- a/widgets/misc/e-attachment-view.c
+++ b/widgets/misc/e-attachment-view.c
@@ -875,6 +875,12 @@ e_attachment_view_finalize (EAttachmentView *view)
priv = e_attachment_view_get_private (view);
g_ptr_array_free (priv->handlers, TRUE);
+
+ g_list_foreach (priv->event_list, (GFunc) gdk_event_free, NULL);
+ g_list_free (priv->event_list);
+
+ g_list_foreach (priv->selected, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->selected);
}
EAttachmentViewPrivate *
@@ -1074,80 +1080,149 @@ gboolean
e_attachment_view_button_press_event (EAttachmentView *view,
GdkEventButton *event)
{
+ EAttachmentViewPrivate *priv;
GtkTreePath *path;
gboolean editable;
- gboolean item_clicked;
+ gboolean handled = FALSE;
+ gboolean path_is_selected = FALSE;
g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
- editable = e_attachment_view_get_editable (view);
+ priv = e_attachment_view_get_private (view);
- /* If the user clicked on a selected item, retain the current
- * selection. If the user clicked on an unselected item, select
- * the clicked item only. If the user did not click on an item,
- * clear the current selection. */
- path = e_attachment_view_get_path_at_pos (view, event->x, event->y);
- if (path != NULL) {
- if (!e_attachment_view_path_is_selected (view, path)) {
- e_attachment_view_unselect_all (view);
- e_attachment_view_select_path (view, path);
- }
- gtk_tree_path_free (path);
- item_clicked = TRUE;
- } else {
- e_attachment_view_unselect_all (view);
- item_clicked = FALSE;
+ if (g_list_find (priv->event_list, event) != NULL)
+ return FALSE;
+
+ if (priv->event_list != NULL) {
+ /* Save the event to be propagated in order. */
+ priv->event_list = g_list_append (
+ priv->event_list,
+ gdk_event_copy ((GdkEvent *) event));
+ return TRUE;
}
- /* Cancel drag and drop if there are no selected items,
- * or if any of the selected items are loading or saving. */
+ editable = e_attachment_view_get_editable (view);
+ path = e_attachment_view_get_path_at_pos (view, event->x, event->y);
+ path_is_selected = e_attachment_view_path_is_selected (view, path);
+
if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
GList *selected, *iter;
gboolean busy = FALSE;
selected = e_attachment_view_get_selected_attachments (view);
+
for (iter = selected; iter != NULL; iter = iter->next) {
EAttachment *attachment = iter->data;
busy |= e_attachment_get_loading (attachment);
busy |= e_attachment_get_saving (attachment);
}
- if (selected == NULL || busy)
- e_attachment_view_drag_source_unset (view);
+
+ /* Prepare for dragging if the clicked item is selected
+ * and none of the selected items are loading or saving. */
+ if (path_is_selected && !busy) {
+ priv->start_x = event->x;
+ priv->start_y = event->y;
+ priv->event_list = g_list_append (
+ priv->event_list,
+ gdk_event_copy ((GdkEvent *) event));
+ handled = TRUE;
+ }
+
g_list_foreach (selected, (GFunc) g_object_unref, NULL);
g_list_free (selected);
}
if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+ /* If the user clicked on a selected item, retain the
+ * current selection. If the user clicked on an unselected
+ * item, select the clicked item only. If the user did not
+ * click on an item, clear the current selection. */
+ if (path == NULL)
+ e_attachment_view_unselect_all (view);
+ else if (!path_is_selected) {
+ e_attachment_view_unselect_all (view);
+ e_attachment_view_select_path (view, path);
+ }
+
/* Non-editable attachment views should only show a
* popup menu when right-clicking on an attachment,
* but editable views can show the menu any time. */
- if (item_clicked || editable) {
+ if (path != NULL || editable) {
e_attachment_view_show_popup_menu (
view, event, NULL, NULL);
- return TRUE;
+ handled = TRUE;
}
}
- return FALSE;
+ if (path != NULL)
+ gtk_tree_path_free (path);
+
+ return handled;
}
gboolean
e_attachment_view_button_release_event (EAttachmentView *view,
GdkEventButton *event)
{
+ EAttachmentViewPrivate *priv;
+ GtkWidget *widget = GTK_WIDGET (view);
+ GList *iter;
+
g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
- /* Restore the attachment view as a drag source, in case
- * we had to cancel during a button press event. */
- if (event->button == 1)
- e_attachment_view_drag_source_set (view);
+ priv = e_attachment_view_get_private (view);
+
+ for (iter = priv->event_list; iter != NULL; iter = iter->next) {
+ GdkEvent *event = iter->data;
+
+ gtk_propagate_event (widget, event);
+ gdk_event_free (event);
+ }
+
+ g_list_free (priv->event_list);
+ priv->event_list = NULL;
return FALSE;
}
gboolean
+e_attachment_view_motion_notify_event (EAttachmentView *view,
+ GdkEventMotion *event)
+{
+ EAttachmentViewPrivate *priv;
+ GtkWidget *widget = GTK_WIDGET (view);
+ GdkDragContext *context;
+ GtkTargetList *targets;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ priv = e_attachment_view_get_private (view);
+
+ if (priv->event_list == NULL)
+ return FALSE;
+
+ if (!gtk_drag_check_threshold (
+ widget, priv->start_x, priv->start_y, event->x, event->y))
+ return TRUE;
+
+ g_list_foreach (priv->event_list, (GFunc) gdk_event_free, NULL);
+ g_list_free (priv->event_list);
+ priv->event_list = NULL;
+
+ targets = gtk_drag_source_get_target_list (widget);
+
+ context = gtk_drag_begin (
+ widget, targets, GDK_ACTION_COPY, 1, (GdkEvent *) event);
+
+ gtk_drag_set_icon_default (context);
+
+ return TRUE;
+}
+
+gboolean
e_attachment_view_key_press_event (EAttachmentView *view,
GdkEventKey *event)
{
@@ -1201,7 +1276,10 @@ e_attachment_view_path_is_selected (EAttachmentView *view,
EAttachmentViewIface *iface;
g_return_val_if_fail (E_IS_ATTACHMENT_VIEW (view), FALSE);
- g_return_val_if_fail (path != NULL, FALSE);
+
+ /* Handle NULL paths gracefully. */
+ if (path == NULL)
+ return FALSE;
iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
g_return_val_if_fail (iface->path_is_selected != NULL, FALSE);
@@ -1287,18 +1365,23 @@ e_attachment_view_sync_selection (EAttachmentView *view,
void
e_attachment_view_drag_source_set (EAttachmentView *view)
{
+ EAttachmentViewIface *iface;
GtkTargetEntry *targets;
GtkTargetList *list;
gint n_targets;
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ if (iface->drag_source_set == NULL)
+ return;
+
list = gtk_target_list_new (NULL, 0);
gtk_target_list_add_uri_targets (list, 0);
targets = gtk_target_table_new_from_list (list, &n_targets);
- gtk_drag_source_set (
- GTK_WIDGET (view), GDK_BUTTON1_MASK,
+ iface->drag_source_set (
+ view, GDK_BUTTON1_MASK,
targets, n_targets, GDK_ACTION_COPY);
gtk_target_table_free (targets, n_targets);
@@ -1308,34 +1391,55 @@ e_attachment_view_drag_source_set (EAttachmentView *view)
void
e_attachment_view_drag_source_unset (EAttachmentView *view)
{
+ EAttachmentViewIface *iface;
+
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
- gtk_drag_source_unset (GTK_WIDGET (view));
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ if (iface->drag_source_unset == NULL)
+ return;
+
+ iface->drag_source_unset (view);
}
void
e_attachment_view_drag_begin (EAttachmentView *view,
GdkDragContext *context)
{
+ EAttachmentViewPrivate *priv;
+
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+ priv = e_attachment_view_get_private (view);
+
/* Prevent the user from dragging and dropping to
* the same attachment view, which would duplicate
* the attachment. */
e_attachment_view_drag_dest_unset (view);
+
+ g_warn_if_fail (priv->selected == NULL);
+ priv->selected = e_attachment_view_get_selected_attachments (view);
}
void
e_attachment_view_drag_end (EAttachmentView *view,
GdkDragContext *context)
{
+ EAttachmentViewPrivate *priv;
+
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+ priv = e_attachment_view_get_private (view);
+
/* Restore the previous drag destination state. */
if (e_attachment_view_get_editable (view))
e_attachment_view_drag_dest_set (view);
+
+ g_list_foreach (priv->selected, (GFunc) g_object_unref, NULL);
+ g_list_free (priv->selected);
+ priv->selected = NULL;
}
static void
@@ -1363,8 +1467,8 @@ e_attachment_view_drag_data_get (EAttachmentView *view,
guint info,
guint time)
{
+ EAttachmentViewPrivate *priv;
EAttachmentStore *store;
- GList *selected;
struct {
gchar **uris;
@@ -1378,19 +1482,16 @@ e_attachment_view_drag_data_get (EAttachmentView *view,
status.uris = NULL;
status.done = FALSE;
+ priv = e_attachment_view_get_private (view);
store = e_attachment_view_get_store (view);
- selected = e_attachment_view_get_selected_attachments (view);
- if (selected == NULL)
+ if (priv->selected == NULL)
return;
e_attachment_store_get_uris_async (
- store, selected, (GAsyncReadyCallback)
+ store, priv->selected, (GAsyncReadyCallback)
attachment_view_got_uris_cb, &status);
- g_list_foreach (selected, (GFunc) g_object_unref, NULL);
- g_list_free (selected);
-
/* We can't return until we have results, so crank
* the main loop until the callback gets triggered. */
while (!status.done)
@@ -1407,19 +1508,22 @@ void
e_attachment_view_drag_dest_set (EAttachmentView *view)
{
EAttachmentViewPrivate *priv;
+ EAttachmentViewIface *iface;
GtkTargetEntry *targets;
gint n_targets;
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ if (iface->drag_dest_set == NULL)
+ return;
+
priv = e_attachment_view_get_private (view);
targets = gtk_target_table_new_from_list (
priv->target_list, &n_targets);
- gtk_drag_dest_set (
- GTK_WIDGET (view), GTK_DEST_DEFAULT_ALL,
- targets, n_targets, priv->drag_actions);
+ iface->drag_dest_set (view, targets, n_targets, priv->drag_actions);
gtk_target_table_free (targets, n_targets);
}
@@ -1427,9 +1531,15 @@ e_attachment_view_drag_dest_set (EAttachmentView *view)
void
e_attachment_view_drag_dest_unset (EAttachmentView *view)
{
+ EAttachmentViewIface *iface;
+
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
- gtk_drag_dest_unset (GTK_WIDGET (view));
+ iface = E_ATTACHMENT_VIEW_GET_IFACE (view);
+ if (iface->drag_dest_unset == NULL)
+ return;
+
+ iface->drag_dest_unset (view);
}
gboolean
diff --git a/widgets/misc/e-attachment-view.h b/widgets/misc/e-attachment-view.h
index 89d2d28664..071de0705a 100644
--- a/widgets/misc/e-attachment-view.h
+++ b/widgets/misc/e-attachment-view.h
@@ -103,6 +103,12 @@ struct _EAttachmentViewPrivate {
GtkUIManager *ui_manager;
guint merge_id;
+ /* Multi-DnD State */
+ GList *event_list;
+ GList *selected;
+ gint start_x;
+ gint start_y;
+
guint editable : 1;
};
@@ -139,6 +145,9 @@ gboolean e_attachment_view_button_press_event
gboolean e_attachment_view_button_release_event
(EAttachmentView *view,
GdkEventButton *event);
+gboolean e_attachment_view_motion_notify_event
+ (EAttachmentView *view,
+ GdkEventMotion *event);
gboolean e_attachment_view_key_press_event
(EAttachmentView *view,
GdkEventKey *event);