Remove -Wwrite-strings compiler flag
[metze/wireshark/wip.git] / ui / gtk / drag_and_drop.c
1 /* drag_and_drop.c
2  * Drag and Drop
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <gtk/gtk.h>
28
29
30 #include "../file.h"
31 #ifdef HAVE_LIBPCAP
32 #include "ui/capture.h"
33 #endif
34
35 #ifdef HAVE_LIBPCAP
36 #include "ui/capture_globals.h"
37 #endif
38 #include "ui/recent_utils.h"
39 #include "ui/simple_dialog.h"
40
41 #include "ui/gtk/gtkglobals.h"
42 #include "ui/gtk/capture_file_dlg.h"
43 #include "ui/gtk/drag_and_drop.h"
44 #include "ui/gtk/main.h"
45
46 #include "ui/gtk/old-gtk-compat.h"
47
48 #ifdef HAVE_GTKOSXAPPLICATION
49 #include <gtkmacintegration/gtkosxapplication.h>
50 #endif
51
52 enum { DND_TARGET_STRING, DND_TARGET_ROOTWIN, DND_TARGET_URL };
53
54 /* convert drag and drop URI to a local filename */
55 static gchar *
56 dnd_uri2filename(gchar *cf_name)
57 {
58     gchar     *src, *dest;
59     gint      ret;
60     guint     i;
61     gchar     esc[3];
62
63
64     /* Remove URI header.
65      * we have to remove the prefix to get a valid filename. */
66 #ifdef _WIN32
67     /*
68      * On win32 (at least WinXP), this prefix looks like (UNC):
69      * file:////servername/sharename/dir1/dir2/capture-file.cap
70      * or (local filename):
71      * file:///d:/dir1/dir2/capture-file.cap
72      */
73     if (strncmp("file:////", cf_name, 9) == 0) {
74         /* win32 UNC: now becoming: //servername/sharename/dir1/dir2/capture-file.cap */
75         cf_name += 7;
76     } else if (strncmp("file:///", cf_name, 8) == 0) {
77         /* win32 local: now becoming: d:/dir1/dir2/capture-file.cap */
78         cf_name += 8;
79     }
80 #else
81     /*
82      * On UNIX (at least KDE 3.0 Konqueror), this prefix looks like:
83      * file:/dir1/dir2/capture-file.cap
84      *
85      * On UNIX (at least GNOME Nautilus 2.8.2), this prefix looks like:
86      * file:///dir1/dir2/capture-file.cap
87      */
88     if (strncmp("file:", cf_name, 5) == 0) {
89         /* now becoming: /dir1/dir2/capture-file.cap or ///dir1/dir2/capture-file.cap */
90         cf_name += 5;
91         /* shorten //////thing to /thing */
92         for(; cf_name[1] == '/'; ++cf_name);
93     }
94 #endif
95
96     /*
97      * unescape the escaped URI characters (spaces, ...)
98      *
99      * we have to replace escaped chars to their equivalents,
100      * e.g. %20 (always a two digit hexstring) -> ' '
101      * the percent character '%' is escaped be a double one "%%"
102      *
103      * we do this conversation "in place" as the result is always
104      * equal or smaller in size.
105      */
106     src = cf_name;
107     dest = cf_name;
108     while (*src) {
109         if (*src == '%') {
110             src++;
111             if (*src == '%') {
112                 /* this is an escaped '%' char (was: "%%") */
113                 *dest = *src;
114                 src++;
115                 dest++;
116             } else {
117                 /* convert escaped hexnumber to unscaped character */
118                 esc[0] = src[0];
119                 esc[1] = src[1];
120                 esc[2] = '\0';
121                 ret = sscanf(esc, "%x", &i);
122                 if (ret == 1) {
123                     src+=2;
124                     *dest = (gchar) i;
125                     dest++;
126                 } else {
127                     /* somethings wrong, just jump over that char
128                      * this will result in a wrong string, but we might get
129                      * user feedback and can fix it later ;-) */
130                     src++;
131                 }
132             }
133 #ifdef _WIN32
134         } else if (*src == '/') {
135             *dest = '\\';
136             src++;
137             dest++;
138 #endif
139         } else {
140             *dest = *src;
141             src++;
142             dest++;
143         }
144     }
145     *dest = '\0';
146
147     return cf_name;
148 }
149
150 /* open/merge the dnd file */
151 void
152 dnd_open_file_cmd(gchar *cf_names_freeme)
153 {
154     int       err;
155     gchar     *cf_name;
156     int       in_file_count;
157     int       files_work;
158     char      **in_filenames;
159     char      *tmpname;
160
161     if (cf_names_freeme == NULL) return;
162
163     /* DND_TARGET_URL:
164      * The cf_name_freeme is a single string, containing one or more URI's,
165      * terminated by CR/NL chars. The length of the whole field can be found
166      * in the selection_data->length field. If it contains one file, simply open it,
167      * If it contains more than one file, ask to merge these files. */
168
169     /* count the number of input files */
170     cf_name = cf_names_freeme;
171     for(in_file_count = 0; (cf_name = strstr(cf_name, "\r\n")) != NULL; ) {
172         cf_name += 2;
173         in_file_count++;
174     }
175     if (in_file_count == 0) {
176       g_free(cf_names_freeme);
177       return;
178     }
179
180     in_filenames = (char **)g_malloc(sizeof(char*) * in_file_count);
181
182     /* store the starts of the file entries in a gchar array */
183     cf_name = cf_names_freeme;
184     in_filenames[0] = cf_name;
185     for(files_work = 1; (cf_name = strstr(cf_name, "\r\n")) != NULL && files_work < in_file_count; ) {
186         cf_name += 2;
187         in_filenames[files_work] = cf_name;
188         files_work++;
189     }
190
191     /* replace trailing CR NL simply with zeroes (in place), so we get valid terminated strings */
192     cf_name = cf_names_freeme;
193     g_strdelimit(cf_name, "\r\n", '\0');
194
195     /* convert all filenames from URI to local filename (in place) */
196     for(files_work = 0; files_work < in_file_count; files_work++) {
197         in_filenames[files_work] = dnd_uri2filename(in_filenames[files_work]);
198     }
199
200     if (in_file_count == 1) {
201         /* open and read the capture file (this will close an existing file) */
202         if (cf_open(&cfile, in_filenames[0], WTAP_TYPE_AUTO, FALSE, &err) == CF_OK) {
203             /* XXX - add this to the menu if the read fails? */
204             cf_read(&cfile, FALSE);
205             add_menu_recent_capture_file(in_filenames[0]);
206         } else {
207             /* the capture file couldn't be read (doesn't exist, file format unknown, ...) */
208         }
209     } else {
210         /* merge the files in chronological order */
211         tmpname = NULL;
212         if (cf_merge_files(&tmpname, in_file_count, in_filenames,
213                            WTAP_FILE_TYPE_SUBTYPE_PCAPNG, FALSE) == CF_OK) {
214             /* Merge succeeded; close the currently-open file and try
215                to open the merged capture file. */
216             cf_close(&cfile);
217             if (cf_open(&cfile, tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) == CF_OK) {
218                 g_free(tmpname);
219                 cf_read(&cfile, FALSE);
220             } else {
221                 /* The merged file couldn't be read. */
222                 g_free(tmpname);
223             }
224         } else {
225             /* merge failed */
226             g_free(tmpname);
227         }
228     }
229
230     g_free(in_filenames);
231     g_free(cf_names_freeme);
232 }
233
234 /* we have received some drag and drop data */
235 /* (as we only registered to "text/uri-list", we will only get a file list here) */
236 static void
237 dnd_data_received(GtkWidget *widget _U_, GdkDragContext *dc _U_, gint x _U_, gint y _U_,
238                   GtkSelectionData *selection_data, guint info, guint t _U_, gpointer data _U_)
239 {
240     gchar *cf_names_freeme;
241     const guchar *sel_data_data;
242     gint sel_data_len;
243
244     if (info == DND_TARGET_URL) {
245         /* Usually we block incoming events by disabling the corresponding menu/toolbar items.
246          * This is the only place where an incoming event won't be blocked in such a way,
247          * so we have to take care of NOT loading a new file while a different process
248          * (e.g. capture/load/...) is still in progress. */
249
250 #ifdef HAVE_LIBPCAP
251         /* if a capture is running, do nothing but warn the user */
252         if((global_capture_session.state != CAPTURE_STOPPED)) {
253             simple_dialog(ESD_TYPE_CONFIRMATION,
254                         ESD_BTN_OK,
255                         "%sDrag and Drop currently not possible!%s\n\n"
256                         "Dropping a file isn't possible while a capture is in progress.",
257                         simple_dialog_primary_start(), simple_dialog_primary_end());
258             return;
259         }
260 #endif
261
262         /* if another file read is still in progress, do nothing but warn the user */
263         if(cfile.state == FILE_READ_IN_PROGRESS) {
264             simple_dialog(ESD_TYPE_CONFIRMATION,
265                         ESD_BTN_OK,
266                         "%sDrag and Drop currently not possible!%s\n\n"
267                         "Dropping a file isn't possible while loading another capture file.",
268                         simple_dialog_primary_start(), simple_dialog_primary_end());
269             return;
270         }
271
272         /* the selection_data will soon be gone, make a copy first */
273         /* the data string is not zero terminated -> make a zero terminated "copy" of it */
274         sel_data_len = gtk_selection_data_get_length(selection_data);
275         sel_data_data = gtk_selection_data_get_data(selection_data);
276         cf_names_freeme = (gchar *)g_malloc(sel_data_len + 1);
277         memcpy(cf_names_freeme, sel_data_data, sel_data_len);
278         cf_names_freeme[sel_data_len] = '\0';
279
280         /* If there's unsaved data, let the user save it first.
281            If they cancel out of it, don't open the file. */
282         if (do_file_close(&cfile, FALSE, " before opening a new capture file"))
283             dnd_open_file_cmd(cf_names_freeme);
284     }
285 }
286
287 #ifdef HAVE_GTKOSXAPPLICATION
288 gboolean
289 gtk_osx_openFile (GtkosxApplication *app _U_, gchar *path, gpointer user_data _U_)
290 {
291     GtkSelectionData selection_data;
292     gchar* selection_path;
293     size_t length = strlen(path);
294
295     selection_path = (gchar *)g_malloc(length + 3);
296     memcpy(selection_path, path, length);
297
298     selection_path[length] = '\r';
299     selection_path[length + 1] = '\n';
300     selection_path[length + 2] = '\0';
301
302     memset(&selection_data, 0, sizeof(selection_data));
303
304     gtk_selection_data_set(&selection_data, gdk_atom_intern_static_string ("text/uri-list"), 8, (guchar*) selection_path, (gint)(length + 2));
305     dnd_data_received(NULL, NULL, 0, 0, &selection_data, DND_TARGET_URL, 0, 0);
306
307     return TRUE;
308 }
309 #endif
310
311 /* init the drag and drop functionality */
312 void
313 dnd_init(GtkWidget *w)
314 {
315     /* we are only interested in the URI list containing filenames */
316     static GtkTargetEntry target_entry[] = {
317          /*{"STRING", 0, DND_TARGET_STRING},*/
318          /*{"text/plain", 0, DND_TARGET_STRING},*/
319          {"text/uri-list", 0, DND_TARGET_URL}
320     };
321
322     /* set this window as a dnd destination */
323     gtk_drag_dest_set(
324          w, GTK_DEST_DEFAULT_ALL, target_entry,
325          sizeof(target_entry) / sizeof(GtkTargetEntry),
326          (GdkDragAction)(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
327
328     /* get notified, if some dnd coming in */
329     g_signal_connect(w, "drag_data_received", G_CALLBACK(dnd_data_received), NULL);
330 #ifdef HAVE_GTKOSXAPPLICATION
331     g_signal_connect(g_object_new(GTKOSX_TYPE_APPLICATION, NULL), "NSApplicationOpenFile", G_CALLBACK(gtk_osx_openFile), NULL);
332 #endif
333 }
334
335 /*
336  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
337  *
338  * Local variables:
339  * c-basic-offset: 4
340  * tab-width: 8
341  * indent-tabs-mode: nil
342  * End:
343  *
344  * vi: set shiftwidth=4 tabstop=8 expandtab:
345  * :indentSize=4:tabSize=8:noTabs=true:
346  */