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