From 81f6e6db484ce6978d9e69be9ee43200f612ef97 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Mon, 17 May 2004 03:50:56 +0000 Subject: ** Bug #6556. 2004-05-17 Not Zed ** Bug #6556. * message-list.c (ml_drop_async_desc, ml_drop_async_drop) (ml_drop_async_done, ml_drop_async_free, ml_drag_data_action) (ml_drop_popup_copy, ml_drop_popup_move, ml_drop_popup_cancel) (ml_tree_drag_data_received): implement async drop operations and the ask drop option menu. 2004-05-14 Not Zed ** Bug #6556. * message-list.c (ml_selection_received_uidlist): removed, not needed anymore. (ml_selection_received): call get_uidlist to paste the selection. (ml_tree_drag_data_received): same here. * em-folder-tree.c (emft_drop_uid_list): removed, not needed because of below change. * em-utils.c (em_utils_selection_get_uidlist): actually do the copy now, don't just decode the data. * em-folder-tree.c (tree_drag_data_received): just copy the selection data data itself, dont decode yet. (emft_import_message_rfc822): removed, not needed, use em utils stuff instead. (emft_drop_message_rfc822): same. (emft_drop_text_uri_list): same. (emft_drop_async_free): simply free stuff. (emft_drop_async_drop): call em_utils stuff where they exist to do the drop. * message-list.c (ml_tree_drag_data_get): send x-mailbox instead of message/rfc822 for the mailbox. (ml_tree_drag_data_received): handle drop of x-mailbox differently to message/rfc822. (ml_tree_drag_motion): implement so proper options are setup whilst dragging. (message_list_construct): seutp the drag src/dest types for changes typs and with ASK action. * em-utils.c (em_utils_read_messages_from_stream): dont unref the stream when we get it. (em_utils_selection_get_mailbox): add an argument to scan from or not, for message/rfc822 vs x-mailbox drops. (em_utils_read_messages_from_stream): Same. * em-folder-tree.c (tree_drag_motion): default to move properly. * message-list.c (ml_selection_received_uidlist): take a move flag. (ml_tree_drag_data_received): handle move action. * em-folder-tree.c (em_folder_tree_new_with_model): got sick of this bloody warning. * em-format.c (default_headers[]): just remove x-mailer from the header list, if it isn't on by default. This is the default list. (em_format_default_headers): loop through everything. svn path=/trunk/; revision=25915 --- mail/ChangeLog | 62 +++++++++++ mail/em-folder-tree.c | 266 +++++++++++++++++++----------------------------- mail/em-format.c | 3 +- mail/em-utils.c | 103 +++++++++++++++---- mail/em-utils.h | 5 +- mail/message-list.c | 277 ++++++++++++++++++++++++++++++++++++++++---------- 6 files changed, 478 insertions(+), 238 deletions(-) diff --git a/mail/ChangeLog b/mail/ChangeLog index 9d9b74f4a9..ef85773d64 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,65 @@ +2004-05-17 Not Zed + + ** Bug #6556. + + * message-list.c (ml_drop_async_desc, ml_drop_async_drop) + (ml_drop_async_done, ml_drop_async_free, ml_drag_data_action) + (ml_drop_popup_copy, ml_drop_popup_move, ml_drop_popup_cancel) + (ml_tree_drag_data_received): implement async drop operations and + the ask drop option menu. + +2004-05-14 Not Zed + + ** Bug #6556. + + * message-list.c (ml_selection_received_uidlist): removed, not + needed anymore. + (ml_selection_received): call get_uidlist to paste the selection. + (ml_tree_drag_data_received): same here. + + * em-folder-tree.c (emft_drop_uid_list): removed, not needed + because of below change. + + * em-utils.c (em_utils_selection_get_uidlist): actually do the + copy now, don't just decode the data. + + * em-folder-tree.c (tree_drag_data_received): just copy the + selection data data itself, dont decode yet. + (emft_import_message_rfc822): removed, not needed, use em utils + stuff instead. + (emft_drop_message_rfc822): same. + (emft_drop_text_uri_list): same. + (emft_drop_async_free): simply free stuff. + (emft_drop_async_drop): call em_utils stuff where they exist to do + the drop. + + * message-list.c (ml_tree_drag_data_get): send x-mailbox instead + of message/rfc822 for the mailbox. + (ml_tree_drag_data_received): handle drop of x-mailbox differently + to message/rfc822. + (ml_tree_drag_motion): implement so proper options are setup + whilst dragging. + (message_list_construct): seutp the drag src/dest types for + changes typs and with ASK action. + + * em-utils.c (em_utils_read_messages_from_stream): dont unref the + stream when we get it. + (em_utils_selection_get_mailbox): add an argument to scan from or + not, for message/rfc822 vs x-mailbox drops. + (em_utils_read_messages_from_stream): Same. + + * em-folder-tree.c (tree_drag_motion): default to move properly. + + * message-list.c (ml_selection_received_uidlist): take a move flag. + (ml_tree_drag_data_received): handle move action. + + * em-folder-tree.c (em_folder_tree_new_with_model): got sick of + this bloody warning. + + * em-format.c (default_headers[]): just remove x-mailer from the + header list, if it isn't on by default. This is the default list. + (em_format_default_headers): loop through everything. + 2004-05-14 Jeffrey Stedfast * em-popup.h: s/RESEND/EDIT/ diff --git a/mail/em-folder-tree.c b/mail/em-folder-tree.c index bf814c1bb4..7248b363a9 100644 --- a/mail/em-folder-tree.c +++ b/mail/em-folder-tree.c @@ -597,7 +597,7 @@ em_folder_tree_new_with_model (EMFolderTreeModel *model) em_folder_tree_construct (emft, model); g_object_ref (model); - em_folder_tree_model_expand_foreach (model, emft_expand_node, emft); + em_folder_tree_model_expand_foreach (model, (EMFTModelExpandFunc)emft_expand_node, emft); emft->priv->loading_row_id = g_signal_connect (model, "loading-row", G_CALLBACK (emft_maybe_expand_row), emft); emft->priv->loaded_row_id = g_signal_connect (model, "loaded-row", G_CALLBACK (emft_maybe_expand_row), emft); @@ -717,6 +717,7 @@ fail: gtk_tree_path_free(src_path); } +/* TODO: Merge the drop handling code/menu's into one spot using a popup target for details */ /* Drop handling */ struct _DragDataReceivedAsync { struct _mail_msg msg; @@ -724,41 +725,19 @@ struct _DragDataReceivedAsync { /* input data */ GdkDragContext *context; - union { - CamelStreamMem *rfc822; - char *folder; - char **urilist; - struct { - char *uri; - GPtrArray *uids; - } uidlist; - } selection; + /* Only selection->data and selection->length are valid */ + GtkSelectionData *selection; CamelStore *store; char *full_name; - gboolean move; + guint32 action; guint info; - /* output data */ - gboolean moved; + unsigned int move:1; + unsigned int moved:1; + unsigned int aborted:1; }; -static void -emft_drop_uid_list(struct _DragDataReceivedAsync *m, CamelFolder *dest) -{ - CamelFolder *src; - - d(printf(" * drop uid list from '%s'\n", m->selection.uidlist.uri)); - - if (!(src = mail_tool_uri_to_folder(m->selection.uidlist.uri, 0, &m->msg.ex))) - return; - - camel_folder_transfer_messages_to(src, m->selection.uidlist.uids, dest, NULL, m->move, &m->msg.ex); - camel_object_unref(src); - - m->moved = m->move && !camel_exception_is_set(&m->msg.ex); -} - static void emft_drop_folder_rec (CamelStore *store, CamelFolderInfo *fi, const char *parent_name, CamelException *ex) { @@ -810,9 +789,9 @@ emft_drop_folder(struct _DragDataReceivedAsync *m) CamelFolder *src; char *new_name; - d(printf(" * Drop folder '%s' onto '%s'\n", m->selection.folder, m->full_name)); + d(printf(" * Drop folder '%s' onto '%s'\n", m->selection->data, m->full_name)); - if (!(src = mail_tool_uri_to_folder(m->selection.folder, 0, &m->msg.ex))) + if (!(src = mail_tool_uri_to_folder(m->selection->data, 0, &m->msg.ex))) return; /* handles dropping to the root properly */ @@ -846,83 +825,6 @@ emft_drop_folder(struct _DragDataReceivedAsync *m) camel_object_unref(src); } -static gboolean -emft_import_message_rfc822 (CamelFolder *dest, CamelStream *stream, gboolean scan_from, CamelException *ex) -{ - CamelMimeParser *mp; - - mp = camel_mime_parser_new (); - camel_mime_parser_scan_from (mp, scan_from); - camel_mime_parser_init_with_stream (mp, stream); - - while (camel_mime_parser_step (mp, 0, 0) == CAMEL_MIME_PARSER_STATE_FROM) { - CamelMessageInfo *info; - CamelMimeMessage *msg; - - msg = camel_mime_message_new (); - if (camel_mime_part_construct_from_parser (CAMEL_MIME_PART (msg), mp) == -1) { - camel_object_unref (msg); - camel_object_unref (mp); - return FALSE; - } - - /* append the message to the folder... */ - info = g_new0 (CamelMessageInfo, 1); - camel_folder_append_message (dest, msg, info, NULL, ex); - camel_object_unref (msg); - - if (camel_exception_is_set (ex)) { - camel_object_unref (mp); - return FALSE; - } - - /* skip over the FROM_END state */ - camel_mime_parser_step (mp, 0, 0); - } - - camel_object_unref (mp); - - return TRUE; -} - -static void -emft_drop_message_rfc822(struct _DragDataReceivedAsync *m, CamelFolder *dest) -{ - gboolean scan_from; - - d(printf(" * drop message/rfc822\n")); - - scan_from = m->selection.rfc822->buffer->len > 5 - && !strncmp(m->selection.rfc822->buffer->data, "From ", 5); - emft_import_message_rfc822(dest, (CamelStream *)m->selection.rfc822, scan_from, &m->msg.ex); -} - -static void -emft_drop_text_uri_list(struct _DragDataReceivedAsync *m, CamelFolder *dest) -{ - CamelStream *stream; - CamelURL *url; - int fd, i, go=1; - - d(printf(" * drop uri list\n")); - - for (i = 0; go && m->selection.urilist[i] != NULL; i++) { - d(printf(" - '%s'\n", (char *)m->selection.urilist[i])); - - url = camel_url_new(m->selection.urilist[i], NULL); - if (url == NULL) - continue; - - if (strcmp(url->protocol, "file") == 0 - && (fd = open(url->path, O_RDONLY)) != -1) { - stream = camel_stream_fs_new_with_fd(fd); - go = emft_import_message_rfc822(dest, stream, TRUE, &m->msg.ex); - camel_object_unref(stream); - } - camel_url_free(url); - } -} - static char * emft_drop_async_desc (struct _mail_msg *mm, int done) { @@ -931,7 +833,7 @@ emft_drop_async_desc (struct _mail_msg *mm, int done) char *buf; if (m->info == DND_DROP_TYPE_FOLDER) { - url = camel_url_new (m->selection.folder, NULL); + url = camel_url_new (m->selection->data, NULL); if (m->move) buf = g_strdup_printf (_("Moving folder %s"), url->fragment ? url->fragment : url->path + 1); @@ -966,15 +868,16 @@ emft_drop_async_drop (struct _mail_msg *mm) switch (m->info) { case DND_DROP_TYPE_UID_LIST: /* import a list of uids from another evo folder */ - emft_drop_uid_list(m, folder); + em_utils_selection_get_uidlist(m->selection, folder, m->move, &mm->ex); + m->moved = m->move && !camel_exception_is_set(&mm->ex); break; case DND_DROP_TYPE_MESSAGE_RFC822: /* import a message/rfc822 stream */ - emft_drop_message_rfc822(m, folder); + em_utils_selection_get_message(m->selection, folder); break; case DND_DROP_TYPE_TEXT_URI_LIST: /* import an mbox, maildir, or mh folder? */ - emft_drop_text_uri_list(m, folder); + em_utils_selection_get_mailbox(m->selection, folder); break; default: abort(); @@ -989,8 +892,14 @@ emft_drop_async_done (struct _mail_msg *mm) struct _DragDataReceivedAsync *m = (struct _DragDataReceivedAsync *) mm; gboolean success, delete; - success = !camel_exception_is_set (&mm->ex); - delete = success && m->move && !m->moved; + /* ?? */ + if (m->aborted) { + success = FALSE; + delete = FALSE; + } else { + success = !camel_exception_is_set (&mm->ex); + delete = success && m->move && !m->moved; + } gtk_drag_finish (m->context, success, delete, GDK_CURRENT_TIME); } @@ -1004,23 +913,8 @@ emft_drop_async_free (struct _mail_msg *mm) camel_object_unref(m->store); g_free(m->full_name); - switch (m->info) { - case DND_DROP_TYPE_FOLDER: - g_free(m->selection.folder); - break; - case DND_DROP_TYPE_UID_LIST: - g_free(m->selection.uidlist.uri); - em_utils_uids_free(m->selection.uidlist.uids); - break; - case DND_DROP_TYPE_MESSAGE_RFC822: - camel_object_unref(m->selection.rfc822); - break; - case DND_DROP_TYPE_TEXT_URI_LIST: - g_strfreev(m->selection.urilist); - break; - default: - abort(); - } + g_free(m->selection->data); + g_free(m->selection); } static struct _mail_msg_op emft_drop_async_op = { @@ -1030,6 +924,43 @@ static struct _mail_msg_op emft_drop_async_op = { emft_drop_async_free, }; +static void +tree_drag_data_action(struct _DragDataReceivedAsync *m) +{ + m->move = m->action == GDK_ACTION_MOVE; + e_thread_put (mail_thread_new, (EMsg *) m); +} + +static void +emft_drop_popup_copy(GtkWidget *item, struct _DragDataReceivedAsync *m) +{ + m->action = GDK_ACTION_COPY; + tree_drag_data_action(m); +} + +static void +emft_drop_popup_move(GtkWidget *item, struct _DragDataReceivedAsync *m) +{ + m->action = GDK_ACTION_MOVE; + tree_drag_data_action(m); +} + +static void +emft_drop_popup_cancel(GtkWidget *item, struct _DragDataReceivedAsync *m) +{ + m->aborted = TRUE; + mail_msg_free(&m->msg); +} + +static EMPopupItem emft_drop_popup_menu[] = { + { EM_POPUP_ITEM, "00.emc.00", N_("_Copy to Folder"), G_CALLBACK (emft_drop_popup_copy), NULL, NULL, 1 }, + { EM_POPUP_ITEM, "00.emc.01", N_("_Move to Folder"), G_CALLBACK (emft_drop_popup_move), NULL, NULL, 1 }, + { EM_POPUP_ITEM, "00.emc.02", N_("_Copy"), G_CALLBACK (emft_drop_popup_copy), NULL, "stock_folder-copy", 2 }, + { EM_POPUP_ITEM, "00.emc.03", N_("_Move"), G_CALLBACK (emft_drop_popup_move), NULL, "stock_folder-move", 2 }, + { EM_POPUP_BAR, "10.emc" }, + { EM_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), G_CALLBACK (emft_drop_popup_cancel), NULL, "stock_cancel", 0 }, +}; + static void tree_drag_data_received(GtkWidget *widget, GdkDragContext *context, int x, int y, GtkSelectionData *selection, guint info, guint time, EMFolderTree *emft) { @@ -1040,8 +971,10 @@ tree_drag_data_received(GtkWidget *widget, GdkDragContext *context, int x, int y const char *full_name; CamelStore *store; GtkTreeIter iter; - char *path, *tmp; + char *path; int i; + + printf("drag data received, action %d\n", context->action); if (!gtk_tree_view_get_dest_row_at_pos (priv->treeview, x, y, &dest_path, &pos)) return; @@ -1075,31 +1008,41 @@ tree_drag_data_received(GtkWidget *widget, GdkDragContext *context, int x, int y m->store = store; camel_object_ref(store); m->full_name = g_strdup (full_name); - m->move = context->action == GDK_ACTION_MOVE; + m->action = context->action; m->info = info; - switch (info) { - case DND_DROP_TYPE_FOLDER: - m->selection.folder = g_strdup(selection->data); - break; - case DND_DROP_TYPE_UID_LIST: - em_utils_selection_get_uidlist(selection, &m->selection.uidlist.uri, &m->selection.uidlist.uids); - break; - case DND_DROP_TYPE_MESSAGE_RFC822: - m->selection.rfc822 = (CamelStreamMem *)camel_stream_mem_new_with_buffer(selection->data, selection->length); - break; - case DND_DROP_TYPE_TEXT_URI_LIST: - tmp = g_strndup(selection->data, selection->length); - m->selection.urilist = g_strsplit(tmp, "\n", 0); - g_free(tmp); - for (i=0;m->selection.urilist[i];i++) - g_strstrip(m->selection.urilist[i]); - break; - default: - abort(); + /* need to copy, goes away once we exit */ + m->selection = g_malloc0(sizeof(*m->selection)); + m->selection->data = g_malloc(selection->length); + memcpy(m->selection->data, selection->data, selection->length); + m->selection->length = selection->length; + + if (context->action == GDK_ACTION_ASK) { + EMPopup *emp; + int mask; + GSList *menus = NULL; + GtkMenu *menu; + + emp = em_popup_new("com.ximian.mail.storageset.popup.drop"); + if (info != DND_DROP_TYPE_FOLDER) + mask = ~1; + else + mask = ~2; + + for (i=0;imask & mask) == 0) { + item->activate_data = m; + menus = g_slist_append(menus, item); + } + } + em_popup_add_items(emp, menus, (GDestroyNotify)g_slist_free); + menu = em_popup_create_menu_once(emp, NULL, mask, mask); + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); + } else { + tree_drag_data_action(m); } - - e_thread_put (mail_thread_new, (EMsg *) m); } static gboolean @@ -1448,21 +1391,16 @@ tree_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guin g_source_remove (priv->autoexpand_id); priv->autoexpand_id = 0; } - + target = emft_drop_target(emft, context, path); if (target != GDK_NONE) { for (i=0; isuggested_action; - if (context->actions & GDK_ACTION_MOVE) - action = GDK_ACTION_MOVE; - gtk_tree_view_set_drag_dest_row(priv->treeview, path, GTK_TREE_VIEW_DROP_INTO_OR_AFTER); - break; case DND_DROP_TYPE_UID_LIST: + case DND_DROP_TYPE_FOLDER: action = context->suggested_action; - if (context->actions & GDK_ACTION_MOVE) + if (action == GDK_ACTION_COPY && (context->actions & GDK_ACTION_MOVE)) action = GDK_ACTION_MOVE; gtk_tree_view_set_drag_dest_row(priv->treeview, path, GTK_TREE_VIEW_DROP_INTO_OR_AFTER); break; @@ -1503,8 +1441,8 @@ em_folder_tree_enable_drag_and_drop (EMFolderTree *emft) setup = 1; } - gtk_drag_source_set((GtkWidget *)priv->treeview, GDK_BUTTON1_MASK, drag_types, NUM_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE); - gtk_drag_dest_set((GtkWidget *)priv->treeview, GTK_DEST_DEFAULT_ALL, drop_types, NUM_DROP_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE); + gtk_drag_source_set((GtkWidget *)priv->treeview, GDK_BUTTON1_MASK, drag_types, NUM_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK); + gtk_drag_dest_set((GtkWidget *)priv->treeview, GTK_DEST_DEFAULT_ALL, drop_types, NUM_DROP_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK); g_signal_connect (priv->treeview, "drag-begin", G_CALLBACK (tree_drag_begin), emft); g_signal_connect (priv->treeview, "drag-data-delete", G_CALLBACK (tree_drag_data_delete), emft); diff --git a/mail/em-format.c b/mail/em-format.c index a93b63728c..2ba834b9d6 100644 --- a/mail/em-format.c +++ b/mail/em-format.c @@ -747,7 +747,6 @@ static const struct { { N_("Subject"), EM_FORMAT_HEADER_BOLD }, { N_("Date"), EM_FORMAT_HEADER_BOLD }, { N_("Newsgroups"), EM_FORMAT_HEADER_BOLD }, - { "x-evolution-mailer", 0 }, /* DO NOT translate */ }; /** @@ -764,7 +763,7 @@ em_format_default_headers(EMFormat *emf) int i; em_format_clear_headers(emf); - for (i = 0; i < G_N_ELEMENTS (default_headers) - 1; i++) + for (i=0; idata == NULL || data->length == -1) + return; + + ex = camel_exception_new(); + stream = camel_stream_mem_new_with_buffer(data->data, data->length); + msg = camel_mime_message_new(); + if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)msg, stream) == 0) + camel_folder_append_message(folder, msg, NULL, NULL, ex); + camel_object_unref(msg); + camel_object_unref(stream); + camel_exception_free(ex); +} + /** * em_utils_selection_set_uidlist: * @data: selection data @@ -901,27 +927,22 @@ em_utils_selection_set_uidlist(GtkSelectionData *data, const char *uri, GPtrArra /** * em_utils_selection_get_uidlist: * @data: selection data - * @urip: Pointer to uri string, to be free'd by caller - * @uidsp: Pointer to an array of uid's. + * @move: do we delete the messages. * - * Convert an x-uid-list type to a uri and a uid list. + * Convert a uid list into a copy/move operation. * - * Return value: The number of uid's found. If 0, then @urip and - * @uidsp will be empty. + * Warning: Could take some time to run. **/ -int -em_utils_selection_get_uidlist(GtkSelectionData *data, char **urip, GPtrArray **uidsp) +void +em_utils_selection_get_uidlist(GtkSelectionData *data, CamelFolder *dest, int move, CamelException *ex) { /* format: "uri\0uid1\0uid2\0uid3\0...\0uidn" */ char *inptr, *inend; GPtrArray *uids; - int res; - - *urip = NULL; - *uidsp = NULL; + CamelFolder *folder; if (data == NULL || data->data == NULL || data->length == -1) - return 0; + return; uids = g_ptr_array_new(); @@ -941,14 +962,16 @@ em_utils_selection_get_uidlist(GtkSelectionData *data, char **urip, GPtrArray ** if (uids->len == 0) { g_ptr_array_free(uids, TRUE); - res = 0; - } else { - *urip = g_strdup(data->data); - *uidsp = uids; - res = uids->len; + return; } - return res; + folder = mail_tool_uri_to_folder(data->data, 0, ex); + if (folder) { + camel_folder_transfer_messages_to(folder, uids, dest, NULL, move, ex); + camel_object_unref(folder); + } + + em_utils_uids_free(uids); } /** @@ -1011,6 +1034,48 @@ em_utils_selection_set_urilist(GtkSelectionData *data, CamelFolder *folder, GPtr } } +/** + * em_utils_selection_set_urilist: + * @data: + * @folder: + * @uids: + * + * Get the selection data @data from a uri list which points to a + * file, which is a berkely mailbox format mailbox. The file is + * automatically cleaned up when the application quits. + **/ +void +em_utils_selection_get_urilist(GtkSelectionData *data, CamelFolder *folder) +{ + CamelStream *stream; + CamelURL *url; + int fd, i, res = 0; + char *tmp, **uris; + + d(printf(" * drop uri list\n")); + + tmp = g_strndup(data->data, data->length); + uris = g_strsplit(tmp, "\n", 0); + g_free(tmp); + for (i=0;res == 0 && uris[i];i++) { + g_strstrip(uris[i]); + + url = camel_url_new(uris[i], NULL); + if (url == NULL) + continue; + + if (strcmp(url->protocol, "file") == 0 + && (fd = open(url->path, O_RDONLY)) != -1) { + stream = camel_stream_fs_new_with_fd(fd); + res = em_utils_read_messages_from_stream(folder, stream); + camel_object_unref(stream); + } + camel_url_free(url); + } + + g_strfreev(uris); +} + static void emu_save_part_done(CamelMimePart *part, char *name, int done, void *data) { diff --git a/mail/em-utils.h b/mail/em-utils.h index 368265ba69..4e433ab12d 100644 --- a/mail/em-utils.h +++ b/mail/em-utils.h @@ -39,6 +39,7 @@ struct _CamelMimeMessage; struct _CamelMimePart; struct _GtkSelectionData; struct _GtkAdjustment; +struct _CamelException; gboolean em_utils_prompt_user(struct _GtkWindow *parent, const char *promptkey, const char *tag, const char *arg0, ...); @@ -65,10 +66,12 @@ void em_utils_flag_for_followup_completed (struct _GtkWidget *parent, struct _Ca void em_utils_selection_set_mailbox(struct _GtkSelectionData *data, struct _CamelFolder *folder, GPtrArray *uids); void em_utils_selection_get_mailbox(struct _GtkSelectionData *data, struct _CamelFolder *folder); +void em_utils_selection_get_message(struct _GtkSelectionData *data, struct _CamelFolder *folder); /* FIXME: be nice if these also worked on struct _CamelFolder's, no easy way to get uri from folder yet tho */ void em_utils_selection_set_uidlist(struct _GtkSelectionData *data, const char *uri, GPtrArray *uids); -int em_utils_selection_get_uidlist(struct _GtkSelectionData *data, char **uri, GPtrArray **uidsp); +void em_utils_selection_get_uidlist(struct _GtkSelectionData *data, struct _CamelFolder *dest, int move, struct _CamelException *ex); void em_utils_selection_set_urilist(struct _GtkSelectionData *data, struct _CamelFolder *folder, GPtrArray *uids); +void em_utils_selection_get_urilist(struct _GtkSelectionData *data, struct _CamelFolder *folder); char *em_utils_temp_save_part(struct _GtkWidget *parent, struct _CamelMimePart *part); diff --git a/mail/message-list.c b/mail/message-list.c index 252957445e..0fd105c14e 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -65,6 +65,7 @@ #include "mail-mt.h" #include "mail-tools.h" #include "mail-ops.h" +#include "em-popup.h" #include "em-utils.h" #include @@ -91,6 +92,35 @@ struct _MessageListPrivate { struct _MLSelection clipboard; }; +static struct { + char *target; + GdkAtom atom; + guint32 actions; +} ml_drag_info[] = { + { "x-uid-list", 0, GDK_ACTION_ASK|GDK_ACTION_MOVE|GDK_ACTION_COPY }, + { "message/rfc822", 0, GDK_ACTION_COPY }, + { "text/uri-list", 0, GDK_ACTION_COPY }, +}; + +enum { + DND_X_UID_LIST, /* x-uid-list */ + DND_MESSAGE_RFC822, /* message/rfc822 */ + DND_TEXT_URI_LIST /* text/uri-list */ +}; + +/* What we send */ +static GtkTargetEntry ml_drag_types[] = { + { "x-uid-list", 0, DND_X_UID_LIST }, + { "text/uri-list", 0, DND_TEXT_URI_LIST }, +}; + +/* What we accept */ +static GtkTargetEntry ml_drop_types[] = { + { "x-uid-list", 0, DND_X_UID_LIST }, + { "message/rfc822", 0, DND_MESSAGE_RFC822 }, + { "text/uri-list", 0, DND_TEXT_URI_LIST }, +}; + /* * Default sizes for the ETable display * @@ -1461,29 +1491,6 @@ ml_selection_clear_event(GtkWidget *widget, GdkEventSelection *event, MessageLis return TRUE; } -static void -ml_selection_received_uidlist(MessageList *ml, GtkSelectionData *data) -{ - CamelFolder *folder; - GPtrArray *uids; - char *uri; - - if (em_utils_selection_get_uidlist(data, &uri, &uids) == 0) - return; - - folder = mail_tool_uri_to_folder(uri, 0, NULL); - if (folder) { - mail_transfer_messages(folder, uids, FALSE, ml->folder_uri, 0, NULL, NULL); - camel_object_unref(folder); - } else { - /* FIXME: error box? */ - g_warning("could not open paste source uri '%s'", uri); - em_utils_uids_free(uids); - } - - g_free(uri); -} - static void ml_selection_received(GtkWidget *widget, GtkSelectionData *data, guint time, MessageList *ml) { @@ -1493,16 +1500,9 @@ ml_selection_received(GtkWidget *widget, GtkSelectionData *data, guint time, Mes return; } - ml_selection_received_uidlist(ml, data); + em_utils_selection_get_uidlist(data, ml->folder, FALSE, NULL); } -static GtkTargetEntry ml_drag_types[] = { - { "x-uid-list", 0, 0 }, - { "message/rfc822", 0, 1 }, - /* not included in dest types */ - { "text/uri-list", 0, 2 }, -}; - static void ml_tree_drag_data_get (ETree *tree, int row, ETreePath path, int col, GdkDragContext *context, GtkSelectionData *data, @@ -1514,13 +1514,10 @@ ml_tree_drag_data_get (ETree *tree, int row, ETreePath path, int col, if (uids->len > 0) { switch (info) { - case 0 /* DND_TARGET_TYPE_X_UID_LIST */: + case DND_X_UID_LIST: em_utils_selection_set_uidlist(data, ml->folder_uri, uids); break; - case 1 /* DND_TARGET_TYPE_MESSAGE_RFC822 */: - em_utils_selection_set_mailbox(data, ml->folder, uids); - break; - case 2 /* DND_TARGET_TYPE_TEXT_URI_LIST */: + case DND_TEXT_URI_LIST: em_utils_selection_set_urilist(data, ml->folder, uids); break; } @@ -1529,29 +1526,200 @@ ml_tree_drag_data_get (ETree *tree, int row, ETreePath path, int col, message_list_free_uids(ml, uids); } +/* TODO: merge this with the folder tree stuff via empopup targets */ +/* Drop handling */ +struct _drop_msg { + struct _mail_msg msg; + + GdkDragContext *context; + + /* Only selection->data and selection->length are valid */ + GtkSelectionData *selection; + + CamelFolder *folder; + + guint32 action; + guint info; + + unsigned int move:1; + unsigned int moved:1; + unsigned int aborted:1; +}; + +static char * +ml_drop_async_desc (struct _mail_msg *mm, int done) +{ + struct _drop_msg *m = (struct _drop_msg *) mm; + + if (m->move) + return g_strdup_printf(_("Moving messages into folder %s"), m->folder->full_name); + else + return g_strdup_printf(_("Copying messages into folder %s"), m->folder->full_name); +} + +static void +ml_drop_async_drop(struct _mail_msg *mm) +{ + struct _drop_msg *m = (struct _drop_msg *)mm; + + switch (m->info) { + case DND_X_UID_LIST: + em_utils_selection_get_uidlist(m->selection, m->folder, m->action == GDK_ACTION_MOVE, &mm->ex); + break; + case DND_MESSAGE_RFC822: + em_utils_selection_get_message(m->selection, m->folder); + break; + case DND_TEXT_URI_LIST: + em_utils_selection_get_urilist(m->selection, m->folder); + break; + } +} + +static void +ml_drop_async_done(struct _mail_msg *mm) +{ + struct _drop_msg *m = (struct _drop_msg *)mm; + gboolean success, delete; + + /* ?? */ + if (m->aborted) { + success = FALSE; + delete = FALSE; + } else { + success = !camel_exception_is_set (&mm->ex); + delete = success && m->move && !m->moved; + } + + gtk_drag_finish(m->context, success, delete, GDK_CURRENT_TIME); +} + +static void +ml_drop_async_free(struct _mail_msg *mm) +{ + struct _drop_msg *m = (struct _drop_msg *)mm; + + g_object_unref(m->context); + camel_object_unref(m->folder); + + g_free(m->selection->data); + g_free(m->selection); +} + +static struct _mail_msg_op ml_drop_async_op = { + ml_drop_async_desc, + ml_drop_async_drop, + ml_drop_async_done, + ml_drop_async_free, +}; + +static void +ml_drop_action(struct _drop_msg *m) +{ + m->move = m->action == GDK_ACTION_MOVE; + e_thread_put (mail_thread_new, (EMsg *) m); +} + +static void +ml_drop_popup_copy(GtkWidget *item, struct _drop_msg *m) +{ + m->action = GDK_ACTION_COPY; + ml_drop_action(m); +} + +static void +ml_drop_popup_move(GtkWidget *item, struct _drop_msg *m) +{ + m->action = GDK_ACTION_MOVE; + ml_drop_action(m); +} + +static void +ml_drop_popup_cancel(GtkWidget *item, struct _drop_msg *m) +{ + m->aborted = TRUE; + mail_msg_free(&m->msg); +} + +static EMPopupItem ml_drop_popup_menu[] = { + { EM_POPUP_ITEM, "00.emc.02", N_("_Copy"), G_CALLBACK(ml_drop_popup_copy), NULL, "stock_folder-copy", 0 }, + { EM_POPUP_ITEM, "00.emc.03", N_("_Move"), G_CALLBACK(ml_drop_popup_move), NULL, "stock_folder-move", 0 }, + { EM_POPUP_BAR, "10.emc" }, + { EM_POPUP_ITEM, "99.emc.00", N_("Cancel _Drag"), G_CALLBACK(ml_drop_popup_cancel), NULL, NULL, 0 }, +}; + static void ml_tree_drag_data_received (ETree *tree, int row, ETreePath path, int col, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, MessageList *ml) { + struct _drop_msg *m; + /* this means we are receiving no data */ if (data->data == NULL || data->length == -1) return; - /* Note: we don't receive text/uri-list, since we have no - guarantee on what the content would be */ - - switch (info) { - case 1 /* DND_TARGET_TYPE_MESSAGE_RFC822 */: - em_utils_selection_get_mailbox(data, ml->folder); - break; - case 0 /* DND_TARGET_TYPE_X_UID_LIST */: - ml_selection_received_uidlist(ml, data); - break; + m = mail_msg_new(&ml_drop_async_op, NULL, sizeof(*m)); + m->context = context; + g_object_ref(context); + m->folder = ml->folder; + camel_object_ref(m->folder); + m->action = context->action; + m->info = info; + + /* need to copy, goes away once we exit */ + m->selection = g_malloc0(sizeof(*m->selection)); + m->selection->data = g_malloc(data->length); + memcpy(m->selection->data, data->data, data->length); + m->selection->length = data->length; + + if (context->action == GDK_ACTION_ASK) { + EMPopup *emp; + GSList *menus = NULL; + GtkMenu *menu; + int i; + + emp = em_popup_new("com.ximian.mail.messagelist.popup.drop"); + for (i=0;iactivate_data = m; + menus = g_slist_append(menus, item); + } + em_popup_add_items(emp, menus, (GDestroyNotify)g_slist_free); + menu = em_popup_create_menu_once(emp, NULL, 0, 0); + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); + } else { + ml_drop_action(m); } - - gtk_drag_finish(context, TRUE, TRUE, time); +} + +static gboolean +ml_tree_drag_motion(ETree *tree, GdkDragContext *context, gint x, gint y, guint time, MessageList *ml) +{ + GList *targets; + GdkDragAction action, actions = 0; + + for (targets = context->targets; targets; targets = targets->next) { + int i; + + printf("atom drop '%s'\n", gdk_atom_name(targets->data)); + for (i=0;idata == (void *)ml_drag_info[i].atom) + actions |= ml_drag_info[i].actions; + } + printf("\n"); + + actions &= context->actions; + action = context->suggested_action; + if (action == GDK_ACTION_COPY && (actions & GDK_ACTION_MOVE)) + action = GDK_ACTION_MOVE; + else if (action == GDK_ACTION_ASK && (actions & (GDK_ACTION_MOVE|GDK_ACTION_COPY)) != (GDK_ACTION_MOVE|GDK_ACTION_COPY)) + action = GDK_ACTION_MOVE; + + gdk_drag_status(context, action, time); + + return action != 0; } static void @@ -1709,8 +1877,13 @@ message_list_finalise (GObject *object) static void message_list_class_init (GObjectClass *object_class) { + int i; + message_list_parent_class = g_type_class_ref(PARENT_TYPE); + for (i=0;ifinalize = message_list_finalise; ((GtkObjectClass *)object_class)->destroy = message_list_destroy; @@ -1804,18 +1977,18 @@ message_list_construct (MessageList *message_list) e_tree_drag_source_set(message_list->tree, GDK_BUTTON1_MASK, ml_drag_types, sizeof(ml_drag_types)/sizeof(ml_drag_types[0]), - GDK_ACTION_MOVE|GDK_ACTION_COPY); + GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_ASK); g_signal_connect(message_list->tree, "tree_drag_data_get", G_CALLBACK(ml_tree_drag_data_get), message_list); - /* note, we only include 2 types, we don't include text/uri-list as a receiver */ e_tree_drag_dest_set(message_list->tree, GTK_DEST_DEFAULT_ALL, - ml_drag_types, 2, - GDK_ACTION_MOVE|GDK_ACTION_COPY); + ml_drop_types, sizeof(ml_drop_types)/sizeof(ml_drop_types[0]), + GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_ASK); g_signal_connect(message_list->tree, "tree_drag_data_received", G_CALLBACK(ml_tree_drag_data_received), message_list); + g_signal_connect(message_list->tree, "drag-motion", G_CALLBACK(ml_tree_drag_motion), message_list); } /** -- cgit v1.2.3