source: trunk/third/evolution/shell/e-folder-dnd-bridge.c @ 18142

Revision 18142, 13.8 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18141, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* e-folder-dnd-bridge.c - Utility functions for handling dnd to Evolution
3 * folders using the ShellComponentDnd interface.
4 *
5 * Copyright (C) 2002 Ximian, Inc.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU General Public
9 * License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
21 * Author: Ettore Perazzoli <ettore@ximian.com>
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include "e-folder-dnd-bridge.h"
29
30#include "Evolution.h"
31#include "e-storage-set-view.h"
32#include "e-shell-constants.h"
33
34#include <gal/widgets/e-gui-utils.h>
35
36#include <libgnome/gnome-i18n.h>
37#include <libgnome/gnome-util.h>
38
39
40/* Callbacks for folder operations.  */
41
42static void
43folder_xfer_callback (EStorageSet *storage_set,
44                      EStorageResult result,
45                      void *data)
46{
47        GtkWindow *parent;
48
49        if (result == E_STORAGE_OK)
50                return;
51
52        parent = GTK_WINDOW (data);
53        e_notice (parent, GNOME_MESSAGE_BOX_ERROR, _("Cannot transfer folder:\n%s"),
54                  e_storage_result_to_string (result));
55}
56
57
58/* Utility functions.  */
59
60static GNOME_Evolution_ShellComponentDnd_ActionSet
61convert_gdk_drag_action_set_to_corba (GdkDragAction action)
62{
63        GNOME_Evolution_ShellComponentDnd_Action retval;
64
65        retval = GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT;
66
67        if (action & GDK_ACTION_COPY)
68                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_COPY;
69        if (action & GDK_ACTION_MOVE)
70                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_MOVE;
71        if (action & GDK_ACTION_LINK)
72                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_LINK;
73        if (action & GDK_ACTION_ASK)
74                retval |= GNOME_Evolution_ShellComponentDnd_ACTION_ASK;
75
76        return retval;
77}
78
79static GNOME_Evolution_ShellComponentDnd_ActionSet
80convert_gdk_drag_action_to_corba (GdkDragAction action)
81{
82        if (action == GDK_ACTION_COPY)
83                return GNOME_Evolution_ShellComponentDnd_ACTION_COPY;
84        else if (action == GDK_ACTION_MOVE)
85                return GNOME_Evolution_ShellComponentDnd_ACTION_MOVE;
86        else if (action == GDK_ACTION_LINK)
87                return GNOME_Evolution_ShellComponentDnd_ACTION_LINK;
88        else if (action == GDK_ACTION_ASK)
89                return GNOME_Evolution_ShellComponentDnd_ACTION_ASK;
90        else {
91                g_warning ("Unknown GdkDragAction %d", action);
92                return GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT;
93        }
94}
95
96static GdkDragAction
97convert_corba_drag_action_to_gdk (GNOME_Evolution_ShellComponentDnd_ActionSet action)
98{
99        if (action == GNOME_Evolution_ShellComponentDnd_ACTION_COPY)
100                return GDK_ACTION_COPY;
101        else if (action == GNOME_Evolution_ShellComponentDnd_ACTION_MOVE)
102                return GDK_ACTION_MOVE;
103        else if (action == GNOME_Evolution_ShellComponentDnd_ACTION_LINK)
104                return GDK_ACTION_LINK;
105        else if (action == GNOME_Evolution_ShellComponentDnd_ACTION_ASK)
106                return GDK_ACTION_ASK;
107        else {
108                g_warning ("unknown GNOME_Evolution_ShellComponentDnd_ActionSet %d", action);
109                return GDK_ACTION_DEFAULT;
110        }
111}
112
113static EvolutionShellComponentClient *
114get_component_at_path (EStorageSet *storage_set,
115                       const char *path)
116{
117        EvolutionShellComponentClient *component_client;
118        EFolderTypeRegistry *folder_type_registry;
119        EFolder *folder;
120
121        folder = e_storage_set_get_folder (storage_set, path);
122        if (folder == NULL)
123                return NULL;
124
125        folder_type_registry = e_storage_set_get_folder_type_registry (storage_set);
126        g_assert (folder_type_registry != NULL);
127
128        component_client = e_folder_type_registry_get_handler_for_type (folder_type_registry,
129                                                                        e_folder_get_type_string (folder));
130
131        return component_client;
132}
133
134/* This will look for the targets in @drag_context, choose one that matches
135   with the allowed types at @path, and return its name.  The EVOLUTION_PATH
136   type always matches.  */
137static const char *
138find_matching_target_for_drag_context (EStorageSet *storage_set,
139                                       const char *path,
140                                       GdkDragContext *drag_context,
141                                       GdkAtom *atom_return)
142{
143        EFolderTypeRegistry *folder_type_registry;
144        EFolder *folder;
145        GList *accepted_types;
146        GList *p, *q;
147
148        folder_type_registry = e_storage_set_get_folder_type_registry (storage_set);
149
150        folder = e_storage_set_get_folder (storage_set, path);
151        if (folder == NULL)
152                return E_FOLDER_DND_PATH_TARGET_TYPE;
153
154        accepted_types = e_folder_type_registry_get_accepted_dnd_types_for_type (folder_type_registry,
155                                                                                 e_folder_get_type_string (folder));
156
157        for (p = drag_context->targets; p != NULL; p = p->next) {
158                char *possible_type;
159
160                possible_type = gdk_atom_name (GPOINTER_TO_INT (p->data));
161                if (strcmp (possible_type, E_FOLDER_DND_PATH_TARGET_TYPE) == 0) {
162                        g_free (possible_type);
163
164                        if (atom_return != NULL)
165                                *atom_return = GPOINTER_TO_INT (p->data);
166
167                        return E_FOLDER_DND_PATH_TARGET_TYPE;
168                }
169
170                for (q = accepted_types; q != NULL; q = q->next) {
171                        const char *accepted_type;
172
173                        accepted_type = (const char *) q->data;
174                        if (strcmp (possible_type, accepted_type) == 0) {
175                                g_free (possible_type);
176
177                                if (atom_return != NULL)
178                                        *atom_return = GPOINTER_TO_INT (p->data);
179
180                                return accepted_type;
181                        }
182                }
183
184                g_free (possible_type);
185        }
186
187        if (atom_return != NULL)
188                *atom_return = 0;
189
190        return NULL;
191}
192
193static gboolean
194handle_evolution_path_drag_motion (EStorageSet *storage_set,
195                                   const char *path,
196                                   GdkDragContext *context,
197                                   unsigned int time)
198{
199        GdkModifierType modifiers;
200        GdkDragAction action;
201
202        gdk_window_get_pointer (NULL, NULL, NULL, &modifiers);
203
204        if ((modifiers & GDK_CONTROL_MASK) != 0) {
205                action = GDK_ACTION_COPY;
206        } else {
207                GtkWidget *source_widget;
208
209                action = GDK_ACTION_MOVE;
210
211                source_widget = gtk_drag_get_source_widget (context);
212                if (source_widget != NULL
213                    && E_IS_STORAGE_SET_VIEW (source_widget)) {
214                        const char *source_path;
215
216                        source_path = e_storage_set_view_get_current_folder (E_STORAGE_SET_VIEW (source_widget));
217                        if (source_path != NULL) {
218                                EFolder *folder;
219                                int source_path_len;
220                                char *destination_path;
221
222                                folder = e_storage_set_get_folder (storage_set, source_path);
223                                if (folder != NULL && e_folder_get_is_stock (folder))
224                                        return FALSE;
225
226                                source_path_len = strlen (path);
227                                if (strcmp (path, source_path) == 0)
228                                        return FALSE;
229
230                                destination_path = g_strconcat (path, "/", g_basename (source_path), NULL);
231                                if (strncmp (destination_path, source_path, source_path_len) == 0) {
232                                        g_free (destination_path);
233                                        return FALSE;
234                                }
235
236                                g_free (destination_path);
237                        }
238                }
239        }
240
241        gdk_drag_status (context, action, time);
242        return TRUE;
243}
244
245
246/* Bridge for the DnD motion event.  */
247
248gboolean
249e_folder_dnd_bridge_motion  (GtkWidget *widget,
250                             GdkDragContext *context,
251                             unsigned int time,
252                             EStorageSet *storage_set,
253                             const char *path)
254{
255        EvolutionShellComponentClient *component_client;
256        GNOME_Evolution_ShellComponentDnd_DestinationFolder destination_folder_interface;
257        GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context corba_context;
258        GNOME_Evolution_ShellComponentDnd_Action suggested_action;
259        CORBA_Environment ev;
260        CORBA_boolean can_handle;
261        EFolder *folder;
262        const char *dnd_type;
263
264        g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
265        g_return_val_if_fail (context != NULL, FALSE);
266        g_return_val_if_fail (E_IS_STORAGE_SET (storage_set), FALSE);
267        g_return_val_if_fail (path != NULL, FALSE);
268
269        dnd_type = find_matching_target_for_drag_context (storage_set, path, context, NULL);
270        if (dnd_type == NULL)
271                return FALSE;
272
273        if (strcmp (dnd_type, E_FOLDER_DND_PATH_TARGET_TYPE) == 0)
274                return handle_evolution_path_drag_motion (storage_set, path, context, time);
275
276        component_client = get_component_at_path (storage_set, path);
277        if (component_client == NULL)
278                return FALSE;
279
280        destination_folder_interface = evolution_shell_component_client_get_dnd_destination_interface (component_client);
281        if (destination_folder_interface == NULL)
282                return FALSE;
283
284        CORBA_exception_init (&ev);
285
286        corba_context.dndType = (char *) dnd_type; /* (Safe cast, as we don't actually free the corba_context.)  */
287        corba_context.possibleActions = convert_gdk_drag_action_set_to_corba (context->actions);
288        corba_context.suggestedAction = convert_gdk_drag_action_to_corba (context->suggested_action);
289
290        folder = e_storage_set_get_folder (storage_set, path);
291       
292        can_handle = GNOME_Evolution_ShellComponentDnd_DestinationFolder_handleMotion (destination_folder_interface,
293                                                                                       e_folder_get_physical_uri (folder),
294                                                                                       e_folder_get_type_string (folder),
295                                                                                       &corba_context,
296                                                                                       &suggested_action,
297                                                                                       &ev);
298        if (ev._major != CORBA_NO_EXCEPTION || ! can_handle) {
299                CORBA_exception_free (&ev);
300                return FALSE;
301        }
302
303        CORBA_exception_free (&ev);
304
305        gdk_drag_status (context, convert_corba_drag_action_to_gdk (suggested_action), time);
306        return TRUE;
307}
308
309
310/* Bridge for the drop event.  */
311
312gboolean
313e_folder_dnd_bridge_drop (GtkWidget *widget,
314                          GdkDragContext *context,
315                          unsigned int time,
316                          EStorageSet *storage_set,
317                          const char *path)
318{
319        GdkAtom atom;
320
321        g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
322        g_return_val_if_fail (context != NULL, FALSE);
323        g_return_val_if_fail (E_IS_STORAGE_SET (storage_set), FALSE);
324        g_return_val_if_fail (path != NULL, FALSE);
325
326        if (context->targets == NULL)
327                return FALSE;
328
329        if (find_matching_target_for_drag_context (storage_set, path, context, &atom) == NULL)
330                return FALSE;
331
332        gtk_drag_get_data (widget, context, atom, time);
333
334        return FALSE;
335}
336
337
338/* Bridge for the data_received event.  */
339
340static gboolean
341handle_data_received_path (GdkDragContext *context,
342                           GtkSelectionData *selection_data,
343                           EStorageSet *storage_set,
344                           const char *path,
345                           GtkWindow *toplevel_window)
346{
347        const char *source_path;
348        char *destination_path;
349        gboolean handled;
350
351        source_path = (const char *) selection_data->data;
352
353        /* (Basic sanity checks.)  */
354        if (source_path == NULL || source_path[0] != E_PATH_SEPARATOR || source_path[1] == '\0')
355                return FALSE;
356
357        destination_path = g_concat_dir_and_file (path, g_basename (source_path));
358
359        switch (context->action) {
360        case GDK_ACTION_MOVE:
361                e_storage_set_async_xfer_folder (storage_set,
362                                                 source_path,
363                                                 destination_path,
364                                                 TRUE,
365                                                 folder_xfer_callback,
366                                                 toplevel_window);
367                handled = TRUE;
368                break;
369        case GDK_ACTION_COPY:
370                e_storage_set_async_xfer_folder (storage_set,
371                                                 source_path,
372                                                 destination_path,
373                                                 FALSE,
374                                                 folder_xfer_callback,
375                                                 toplevel_window);
376                handled = TRUE;
377                break;
378        default:
379                handled = FALSE;
380                g_warning ("EStorageSetView: Unknown action %d", context->action);
381        }
382
383        g_free (destination_path);
384
385        return handled;
386}
387
388static gboolean
389handle_data_received_non_path (GdkDragContext *context,
390                               GtkSelectionData *selection_data,
391                               EStorageSet *storage_set,
392                               const char *path,
393                               const char *target_type)
394{
395        GNOME_Evolution_ShellComponentDnd_DestinationFolder destination_folder_interface;
396        GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context corba_context;
397        GNOME_Evolution_ShellComponentDnd_Data corba_data;
398        EvolutionShellComponentClient *component_client;
399        EFolder *folder;
400        CORBA_Environment ev;
401
402        component_client = get_component_at_path (storage_set, path);
403        if (component_client == NULL)
404                return FALSE;
405
406        destination_folder_interface = evolution_shell_component_client_get_dnd_destination_interface (component_client);
407        if (destination_folder_interface == NULL)
408                return FALSE;
409               
410        CORBA_exception_init (&ev);
411
412        folder = e_storage_set_get_folder (storage_set, path);
413       
414        corba_context.dndType = (char *) target_type;
415        corba_context.possibleActions = convert_gdk_drag_action_set_to_corba (context->actions);
416        corba_context.suggestedAction = convert_gdk_drag_action_to_corba (context->suggested_action);
417
418        corba_data.format = selection_data->format;
419        corba_data.target = selection_data->target;
420
421        corba_data.bytes._release = FALSE;
422
423        if (selection_data->data == NULL) {
424                /* If data is NULL the length is -1 and this would mess things
425                   up so we handle it separately.  */
426                corba_data.bytes._length = 0;
427                corba_data.bytes._buffer = NULL;
428        } else {
429                corba_data.bytes._length = selection_data->length;
430                corba_data.bytes._buffer = selection_data->data;
431        }
432
433        /* pass off the data to the component's DestinationFolderInterface */
434        return GNOME_Evolution_ShellComponentDnd_DestinationFolder_handleDrop (destination_folder_interface,
435                                                                               e_folder_get_physical_uri (folder),
436                                                                               e_folder_get_type_string (folder),
437                                                                               &corba_context,
438                                                                               convert_gdk_drag_action_to_corba (context->action),
439                                                                               &corba_data,
440                                                                               &ev);
441}
442
443void
444e_folder_dnd_bridge_data_received (GtkWidget *widget,
445                                   GdkDragContext *context,
446                                   GtkSelectionData *selection_data,
447                                   unsigned int time,
448                                   EStorageSet *storage_set,
449                                   const char *path)
450{
451        char *target_type;
452        gboolean handled;
453
454        g_return_if_fail (GTK_IS_WIDGET (widget));
455        g_return_if_fail (context != NULL);
456        g_return_if_fail (E_IS_STORAGE_SET (storage_set));
457        g_return_if_fail (path != NULL);
458
459        if (selection_data->data == NULL && selection_data->length == -1)
460                return;
461
462        target_type = gdk_atom_name (selection_data->target);
463
464        if (strcmp (target_type, E_FOLDER_DND_PATH_TARGET_TYPE) != 0) {
465                handled = handle_data_received_non_path (context, selection_data, storage_set,
466                                                         path, target_type);
467        } else {
468                GtkWindow *toplevel_window;
469
470                toplevel_window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
471                handled = handle_data_received_path (context, selection_data, storage_set, path,
472                                                     toplevel_window);
473        }
474
475        g_free (target_type);
476        gtk_drag_finish (context, handled, FALSE, time);
477}
478
Note: See TracBrowser for help on using the repository browser.