1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 Takuro Ashie
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * $Id: dnd.c,v 1.19 2004/04/08 14:11:28 makeinu Exp $
22  */
23 
24 #include <string.h>
25 
26 #include "gimageview.h"
27 
28 #include "dnd.h"
29 #include "fileutil.h"
30 #include "gfileutil.h"
31 #include "gtkutils.h"
32 #include "gimv_thumb.h"
33 #include "gimv_thumb_view.h"
34 #include "gimv_thumb_win.h"
35 #include "menu.h"
36 #include "prefs.h"
37 
38 
39 GtkTargetEntry dnd_types_all[] = {
40    {"GIMV_TAB",                   0, TARGET_GIMV_TAB},
41    {"GIMV_COMPONENT",             0, TARGET_GIMV_COMPONENT},
42    {"GIMV_ARCHIVE_MEMBER_LIST",   0, TARGET_GIMV_ARCHIVE_MEMBER_LIST},
43    {"text/uri-list",              0, TARGET_URI_LIST},
44    {"property/bgimage",           0, TARGET_URI_LIST},
45 };
46 const gint dnd_types_all_num = sizeof(dnd_types_all) / sizeof(GtkTargetEntry);
47 
48 GtkTargetEntry *dnd_types_uri = &dnd_types_all[3];
49 const gint dnd_types_uri_num = 1;
50 
51 GtkTargetEntry *dnd_types_archive = &dnd_types_all[2];
52 const gint dnd_types_archive_num = 2;
53 
54 GtkTargetEntry *dnd_types_tab_component = &dnd_types_all[0];
55 const gint dnd_types_tab_component_num = 2;
56 
57 GtkTargetEntry *dnd_types_component = &dnd_types_all[1];
58 const gint dnd_types_component_num = 1;
59 
60 
61 GtkItemFactoryEntry dnd_file_popup_items [] =
62 {
63    {N_("/Open in new tab"), NULL, menu_modal_cb, GDK_ACTION_PRIVATE, NULL},
64    {N_("/---"),             NULL, NULL,          0,                  "<Separator>"},
65    {N_("/Move"),            NULL, menu_modal_cb, GDK_ACTION_MOVE,    NULL},
66    {N_("/Copy"),            NULL, menu_modal_cb, GDK_ACTION_COPY,    NULL},
67    {N_("/Symbolic Link"),   NULL, menu_modal_cb, GDK_ACTION_LINK ,   NULL},
68    {N_("/---"),             NULL, NULL,          0,                  "<Separator>"},
69    {N_("/Cancel"),          NULL, NULL,          0,                  NULL},
70    {NULL, NULL, NULL, 0, NULL},
71 };
72 
73 
74 /*
75  *  dnd_get_file_list:
76  *     @ convert string URI list to GList format.
77  *
78  *  string : file list (string format)
79  *  Return : file list (GList format)
80  */
81 GList *
dnd_get_file_list(const gchar * string,gint len)82 dnd_get_file_list (const gchar *string, gint len)
83 {
84    gchar *file;
85    gchar *ptr, *uri;
86    GList *list = NULL;
87    gint pos = 0;
88 
89    uri = ptr = g_memdup (string, len);
90 
91    while (*ptr && (pos < len)) {
92       if (!strncmp(ptr, "file:", 5)) {
93          ptr += 5;
94          pos += 5;
95       }
96       if (!strncmp(ptr, "//", 2)){
97          ptr += 2;
98          pos += 2;
99       }
100 
101       file = ptr;
102 
103       while (*ptr != '\r' && *ptr != '\n' && *ptr != '\0') {
104          ptr++;
105          pos++;
106       }
107       *ptr++ = '\0';
108       pos++;
109 
110       while (*ptr == '\r' || *ptr == '\n') {
111          ptr++;
112          pos++;
113       }
114 
115       if (file && file[0] != '\r' && file[0] != '\n' && file[0] != '\0')
116          list = g_list_append (list, g_strdup(file));
117    }
118 
119    g_free (uri);
120 
121    return list;
122 }
123 
124 
125 /*
126  *  dnd_src_set:
127  *     @
128  *
129  *  widget : widget to set DnD (source side).
130  */
131 void
dnd_src_set(GtkWidget * widget,const GtkTargetEntry * entry,gint num)132 dnd_src_set (GtkWidget *widget, const GtkTargetEntry *entry, gint num)
133 {
134    /* FIXME */
135    if (conf.dnd_enable_to_external)
136       dnd_types_all[3].flags = 0;
137    else
138       dnd_types_all[3].flags = GTK_TARGET_SAME_APP;
139 
140    gtk_drag_source_set(widget,
141                        GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK,
142                        entry, num,
143                        GDK_ACTION_ASK  | GDK_ACTION_COPY
144                        | GDK_ACTION_MOVE | GDK_ACTION_LINK);
145 }
146 
147 
148 /*
149  *  dnd_dest_set:
150  *     @
151  *
152  *  widget : widget to set DnD (destination side).
153  */
154 void
dnd_dest_set(GtkWidget * widget,const GtkTargetEntry * entry,gint num)155 dnd_dest_set (GtkWidget *widget, const GtkTargetEntry *entry, gint num)
156 {
157    if (conf.dnd_enable_from_external)
158       dnd_types_all[3].flags = 0;
159    else
160       dnd_types_all[3].flags = GTK_TARGET_SAME_APP;
161 
162    gtk_drag_dest_set(widget,
163                      GTK_DEST_DEFAULT_ALL,
164                      /* GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, */
165                      entry, num,
166                      GDK_ACTION_ASK  | GDK_ACTION_COPY
167                      | GDK_ACTION_MOVE | GDK_ACTION_LINK);
168 }
169 
170 
171 /*
172  *  dnd_file_operation:
173  *     @ Open DnD context menu and do specified file operation when data
174  *       received. This function will called by "drag_data_received" signal's
175  *       callback function.
176  *
177  *  dest_dir : destination directory to move/copy/link specified files.
178  *  context  : DnD context.
179  *  seldata  : URI list (string format).
180  *  time     : time when drag data received.
181  *  tw       : Pointer to parent ThumbWindow.
182  */
183 void
dnd_file_operation(const gchar * dest_dir,GdkDragContext * context,GtkSelectionData * seldata,guint time,GimvThumbWin * tw)184 dnd_file_operation (const gchar *dest_dir, GdkDragContext *context,
185                     GtkSelectionData *seldata, guint time, GimvThumbWin *tw)
186 {
187    GtkWidget *dnd_popup;
188    GList *list;
189    GimvThumbView *tv;
190    gboolean dnd_success = TRUE, dnd_delete = FALSE;
191    gint n_menu_items, action;
192 
193    g_return_if_fail (dest_dir && context && seldata);
194 
195    list = dnd_get_file_list (seldata->data, seldata->length);
196 
197    /* create popup menu */
198    n_menu_items = sizeof (dnd_file_popup_items)
199       / sizeof (dnd_file_popup_items[0]) - 1;
200    dnd_popup = menu_create_items (NULL, dnd_file_popup_items,
201                                   n_menu_items, "<DnDPop>",
202                                   NULL);
203 
204 #ifdef USE_GTK2
205    gtk_object_ref (GTK_OBJECT (dnd_popup));
206    gtk_object_sink (GTK_OBJECT (dnd_popup));
207 #endif
208 
209    /* popup menu */
210    action = menu_popup_modal (dnd_popup, NULL, NULL, NULL, NULL);
211 
212    gtk_widget_unref (dnd_popup);
213 
214    if (action == GDK_ACTION_PRIVATE) {
215       open_images_dirs (list, tw, LOAD_CACHE, FALSE);
216    } else {
217       GtkWindow *window = tw ? GTK_WINDOW (tw) : NULL;
218       switch (action) {
219       case GDK_ACTION_MOVE:
220          files2dir (list, dest_dir, FILE_MOVE, window);
221          dnd_delete = TRUE;
222          break;
223       case GDK_ACTION_COPY:
224          files2dir (list, dest_dir, FILE_COPY, window);
225          break;
226       case GDK_ACTION_LINK:
227          files2dir (list, dest_dir, FILE_LINK, window);
228          break;
229       default:
230          dnd_success = FALSE;
231          break;
232       }
233    }
234 
235    gtk_drag_finish(context, dnd_success, dnd_delete, time);
236 
237    /* update dest side file list */
238    tv = gimv_thumb_view_find_opened_dir (dest_dir);
239    if (tv) {
240       gimv_thumb_view_refresh_list (tv);
241       gtk_idle_add (gimv_thumb_view_refresh_list_idle, tv);
242    }
243 
244    g_list_foreach (list, (GFunc) g_free, NULL);
245    g_list_free (list);
246 }
247