r262: Added a check in the dnd code that the requested type is valid
[rox-filer/ma.git] / ROX-Filer / src / gtksavebox.c
blob452535f4ada51d268995f5dbf1e27ca3298ce368
1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "config.h"
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
33 #include "gdk/gdkkeysyms.h"
35 #include "gtksavebox.h"
36 #include "gtk/gtkwidget.h"
37 #include "gtk/gtkalignment.h"
38 #include "gtk/gtkdnd.h"
39 #include "gtk/gtkbutton.h"
40 #include "gtk/gtksignal.h"
41 #include "gtk/gtkpixmap.h"
42 #include "gtk/gtkhbox.h"
43 #include "gtk/gtkeventbox.h"
44 #include "gtk/gtkentry.h"
45 #include "gtk/gtkhseparator.h"
46 #include "gtk/gtkvbox.h"
47 #include "gtk/gtkdialog.h"
48 #include "gtk/gtklabel.h"
50 /* Signals:
52 * gint save_to_file (GtkSavebox *savebox, guchar *pathname)
53 * Save the data to disk using this pathname. Return GTK_XDS_SAVED
54 * on success, or GTK_XDS_SAVE_ERROR on failure (and report the error
55 * to the user somehow). DO NOT mark the data unmodified or change
56 * the pathname for the file - this might be a scrap file transfer.
58 * void saved_to_uri (GtkSavebox *savebox, guchar *uri)
59 * The data is saved and safe. Mark the file as unmodified and update
60 * the pathname/uri for the file to the one given.
62 * void save_done (GtkSavebox *savebox)
63 * The save operation is over. Close the savebox. This signal is sent
64 * regardless of whether the data is now 'safe', but not if no data
65 * has been sent.
68 enum
70 SAVE_TO_FILE,
71 SAVED_TO_URI,
72 SAVE_DONE,
74 LAST_SIGNAL
77 static guint savebox_signals[LAST_SIGNAL] = { 0 };
79 static GtkWidgetClass *parent_class = NULL;
81 /* Longest possible XdndDirectSave0 property value */
82 #define XDS_MAXURILEN 4096
84 static GdkAtom XdndDirectSave;
85 static GdkAtom text_plain;
86 static GdkAtom xa_string;
88 static void gtk_savebox_class_init (GtkSaveboxClass *klass);
89 static void gtk_savebox_init (GtkSavebox *savebox);
90 static void button_press_over_icon (GtkWidget *drag_box,
91 GdkEventButton *event,
92 GtkSavebox *savebox);
93 static void drag_data_get (GtkWidget *widget,
94 GdkDragContext *context,
95 GtkSelectionData *selection_data,
96 guint info,
97 guint32 time);
98 static guchar *read_xds_property (GdkDragContext *context,
99 gboolean delete);
100 static void write_xds_property (GdkDragContext *context,
101 guchar *value);
102 static char *get_local_path (char *uri);
103 static guchar *our_host_name (void);
104 static void drag_end (GtkWidget *widget,
105 GdkDragContext *context);
106 static void do_save (GtkWidget *widget,
107 GtkSavebox *savebox);
108 static gint delete_event (GtkWidget *widget,
109 GdkEventAny *event);
110 static gint key_press_event (GtkWidget *widget,
111 GdkEventKey *event);
112 static void cancel_clicked (GtkWidget *widget,
113 GtkSavebox *savebox);
116 GtkType
117 gtk_savebox_get_type (void)
119 static GtkType savebox_type = 0;
121 if (!savebox_type)
123 static const GtkTypeInfo savebox_info =
125 "GtkSavebox",
126 sizeof (GtkSavebox),
127 sizeof (GtkSaveboxClass),
128 (GtkClassInitFunc) gtk_savebox_class_init,
129 (GtkObjectInitFunc) gtk_savebox_init,
130 /* reserved_1 */ NULL,
131 /* reserved_2 */ NULL,
132 (GtkClassInitFunc) NULL,
135 savebox_type = gtk_type_unique (GTK_TYPE_WINDOW, &savebox_info);
138 return savebox_type;
141 static void
142 gtk_savebox_class_init (GtkSaveboxClass *class)
144 GtkObjectClass *object_class = (GtkObjectClass *) class;
145 GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
147 XdndDirectSave = gdk_atom_intern ("XdndDirectSave0", FALSE);
148 text_plain = gdk_atom_intern ("text/plain", FALSE);
149 xa_string = gdk_atom_intern ("STRING", FALSE);
151 parent_class = gtk_type_class(gtk_window_get_type());
153 class->save_to_file = NULL;
154 widget_class->delete_event = delete_event;
155 widget_class->key_press_event = key_press_event;
157 savebox_signals[SAVE_TO_FILE] = gtk_signal_new ("save_to_file",
158 GTK_RUN_LAST,
159 object_class->type,
160 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
161 save_to_file),
162 gtk_marshal_INT__POINTER,
163 GTK_TYPE_INT, 1,
164 GTK_TYPE_POINTER);
166 savebox_signals[SAVED_TO_URI] = gtk_signal_new ("saved_to_uri",
167 GTK_RUN_LAST,
168 object_class->type,
169 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
170 saved_to_uri),
171 gtk_marshal_NONE__POINTER,
172 GTK_TYPE_NONE, 1,
173 GTK_TYPE_POINTER);
175 savebox_signals[SAVE_DONE] = gtk_signal_new ("save_done",
176 GTK_RUN_LAST,
177 object_class->type,
178 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
179 save_done),
180 gtk_marshal_NONE__NONE,
181 GTK_TYPE_NONE, 0);
183 gtk_object_class_add_signals (object_class, savebox_signals, LAST_SIGNAL);
186 static void
187 gtk_savebox_init (GtkSavebox *savebox)
189 GtkWidget *hbox, *button, *alignment;
190 GtkTargetEntry targets[] = { {"XdndDirectSave0", 0, GTK_TARGET_XDS} };
192 savebox->targets = gtk_target_list_new (targets,
193 sizeof (targets) / sizeof (*targets));
194 savebox->icon = NULL;
196 GTK_WINDOW (savebox)->type = GTK_WINDOW_DIALOG;
197 gtk_window_set_title (GTK_WINDOW (savebox), _("Save As:"));
198 gtk_window_set_position (GTK_WINDOW (savebox), GTK_WIN_POS_MOUSE);
199 gtk_container_set_border_width (GTK_CONTAINER (savebox), 4);
201 savebox->vbox = gtk_vbox_new (FALSE, 0);
202 gtk_container_add (GTK_CONTAINER (savebox), savebox->vbox);
204 alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
205 gtk_box_pack_start (GTK_BOX (savebox->vbox), alignment, TRUE, TRUE, 0);
207 savebox->drag_box = gtk_event_box_new ();
208 gtk_container_set_border_width (GTK_CONTAINER (savebox->drag_box), 4);
209 gtk_widget_add_events (savebox->drag_box, GDK_BUTTON_PRESS_MASK);
210 gtk_signal_connect (GTK_OBJECT (savebox->drag_box), "button_press_event",
211 GTK_SIGNAL_FUNC (button_press_over_icon), savebox);
212 gtk_signal_connect (GTK_OBJECT (savebox), "drag_end",
213 GTK_SIGNAL_FUNC (drag_end), savebox);
214 gtk_signal_connect (GTK_OBJECT (savebox), "drag_data_get",
215 GTK_SIGNAL_FUNC (drag_data_get), savebox);
216 gtk_container_add (GTK_CONTAINER (alignment), savebox->drag_box);
218 savebox->entry = gtk_entry_new ();
219 gtk_signal_connect (GTK_OBJECT (savebox->entry), "activate",
220 GTK_SIGNAL_FUNC (do_save), savebox);
221 gtk_box_pack_start (GTK_BOX (savebox->vbox), savebox->entry, FALSE, TRUE, 4);
223 hbox = gtk_hbox_new (TRUE, 0);
224 gtk_box_pack_start (GTK_BOX (savebox->vbox), hbox, FALSE, TRUE, 0);
226 button = gtk_button_new_with_label (_("OK"));
227 gtk_signal_connect (GTK_OBJECT (button), "clicked",
228 GTK_SIGNAL_FUNC (do_save), savebox);
229 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
230 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
231 gtk_window_set_default (GTK_WINDOW (savebox), button);
233 button = gtk_button_new_with_label (_("Cancel"));
234 gtk_signal_connect (GTK_OBJECT (button), "clicked",
235 GTK_SIGNAL_FUNC (cancel_clicked), savebox);
236 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
237 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
239 gtk_widget_show_all (savebox->vbox);
241 gtk_widget_grab_focus (savebox->entry);
244 GtkWidget*
245 gtk_savebox_new (void)
247 GtkSavebox *savebox;
249 savebox = gtk_type_new (gtk_savebox_get_type ());
251 return GTK_WIDGET (savebox);
254 void
255 gtk_savebox_set_icon (GtkSavebox *savebox, GdkPixmap *pixmap, GdkPixmap *mask)
257 g_return_if_fail (savebox != NULL);
258 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
259 g_return_if_fail (pixmap != NULL);
261 if (savebox->icon)
262 gtk_pixmap_set (GTK_PIXMAP (savebox->icon), pixmap, mask);
263 else
265 savebox->icon = gtk_pixmap_new (pixmap, mask);
266 gtk_container_add (GTK_CONTAINER (savebox->drag_box), savebox->icon);
267 gtk_widget_show(savebox->icon);
271 void
272 gtk_savebox_set_pathname (GtkSavebox *savebox, gchar *pathname)
274 gchar *slash;
275 gint leaf;
277 g_return_if_fail (savebox != NULL);
278 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
279 g_return_if_fail (pathname != NULL);
281 gtk_entry_set_text (GTK_ENTRY (savebox->entry), pathname);
283 slash = strrchr (pathname, '/');
285 leaf = slash ? slash - pathname + 1 : 0;
287 gtk_entry_select_region (GTK_ENTRY (savebox->entry), leaf, -1);
290 static void
291 button_press_over_icon (GtkWidget *drag_box, GdkEventButton *event,
292 GtkSavebox *savebox)
294 GdkDragContext *context;
295 GdkPixmap *pixmap, *mask;
296 guchar *uri = NULL, *leafname;
298 g_return_if_fail (savebox != NULL);
299 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
300 g_return_if_fail (event != NULL);
301 g_return_if_fail (savebox->icon != NULL);
303 savebox->using_xds = FALSE;
304 savebox->data_sent = FALSE;
305 context = gtk_drag_begin (GTK_WIDGET (savebox),
306 savebox->targets, GDK_ACTION_COPY,
307 event->button, (GdkEvent *) event);
309 uri = gtk_entry_get_text (GTK_ENTRY (savebox->entry));
310 if (uri && *uri)
312 leafname = strrchr (uri, '/');
313 if (leafname)
314 leafname++;
315 else
316 leafname = uri;
318 else
319 leafname = _("Unnamed");
321 write_xds_property (context, leafname);
323 gtk_pixmap_get (GTK_PIXMAP (savebox->icon), &pixmap, &mask);
324 gtk_drag_set_icon_pixmap (context,
325 gtk_widget_get_colormap (savebox->icon),
326 pixmap,
327 mask,
328 event->x, event->y);
332 static void
333 drag_data_get (GtkWidget *widget,
334 GdkDragContext *context,
335 GtkSelectionData *selection_data,
336 guint info,
337 guint32 time)
339 GtkSavebox *savebox;
340 guchar to_send = 'E';
341 guchar *uri;
342 guchar *pathname;
344 g_return_if_fail (widget != NULL);
345 g_return_if_fail (GTK_IS_SAVEBOX (widget));
346 g_return_if_fail (context != NULL);
347 g_return_if_fail (selection_data != NULL);
349 savebox = GTK_SAVEBOX (widget);
351 /* We're only concerned with the XDS protocol. Responding to other requests
352 * (including application/octet-stream) is the job of the application.
354 if (info != GTK_TARGET_XDS)
356 /* Assume that the data will be/has been sent */
357 savebox->data_sent = TRUE;
358 return;
361 uri = read_xds_property (context, FALSE);
363 if (uri)
365 gint result = GTK_XDS_NO_HANDLER;
367 pathname = get_local_path (uri);
368 if (!pathname)
369 to_send = 'F'; /* Not on the local machine */
370 else
372 gtk_signal_emit (GTK_OBJECT (widget),
373 savebox_signals[SAVE_TO_FILE],
374 pathname, &result);
376 if (result == GTK_XDS_SAVED)
378 savebox->data_sent = TRUE;
379 to_send = 'S';
381 else if (result != GTK_XDS_SAVE_ERROR)
382 g_warning ("No handler for saving to a file.\n");
384 g_free (uri);
387 else
389 g_warning (_("Remote application wants to use Direct Save, but I can't "
390 "read the XdndDirectSave0 (type text/plain) property.\n"));
393 if (to_send != 'E')
394 savebox->using_xds = TRUE;
395 gtk_selection_data_set (selection_data, xa_string, 8, &to_send, 1);
398 static guchar *
399 read_xds_property (GdkDragContext *context, gboolean delete)
401 guchar *prop_text;
402 guint length;
403 guchar *retval = NULL;
405 g_return_val_if_fail (context != NULL, NULL);
407 if (gdk_property_get (context->source_window, XdndDirectSave, text_plain,
408 0, XDS_MAXURILEN, delete,
409 NULL, NULL, &length, &prop_text)
410 && prop_text)
412 /* Terminate the string */
413 retval = g_realloc (prop_text, length + 1);
414 retval[length] = '\0';
417 return retval;
420 static void
421 write_xds_property (GdkDragContext *context, guchar *value)
423 if (value)
425 gdk_property_change (context->source_window, XdndDirectSave,
426 text_plain, 8, GDK_PROP_MODE_REPLACE,
427 value, strlen (value));
429 else
430 gdk_property_delete (context->source_window, XdndDirectSave);
433 /* Convert a URI to a local pathname (or NULL if it isn't local).
434 * The returned pointer points inside the input string.
435 * Possible input formats:
436 * /path
437 * ///path
438 * //host/path
439 * file://host/path
440 * This function is quite useful generally - it should be somewhere
441 * publically accessible.
443 static char *get_local_path (char *uri)
445 char *host;
447 host = our_host_name ();
449 if (*uri == '/')
451 char *path;
453 if (uri[1] != '/')
454 return uri; /* Just a local path - no host part */
456 path = strchr (uri + 2, '/');
457 if (!path)
458 return NULL; /* //something */
460 if (path - uri == 2)
461 return path; /* ///path */
462 if (strlen (host) == path - uri - 2 &&
463 strncmp (uri + 2, host, path - uri - 2) == 0)
464 return path; /* //my.host/path */
465 /* //other.host/path */
467 else if (g_strncasecmp (uri, "file:", 5) == 0)
469 uri += 5;
471 if (*uri == '/')
472 return get_local_path (uri);
474 else if (strncmp (uri, "./", 2) == 0 || strncmp (uri, "../", 3) == 0)
475 return uri; /* Relative path - let this through */
477 return NULL;
480 /* Return our complete host name */
481 static guchar *our_host_name (void)
483 static char *name = NULL;
485 if (!name)
487 char buffer[XDS_MAXURILEN];
489 if (gethostname (buffer, XDS_MAXURILEN))
491 g_warning ("gethostname(): %s\n", g_strerror (errno));
492 name = "localhost";
494 else
496 buffer[XDS_MAXURILEN - 1] = '\0';
497 name = g_strdup (buffer);
501 return name;
504 static void drag_end (GtkWidget *widget, GdkDragContext *context)
506 g_return_if_fail (widget != NULL);
507 g_return_if_fail (GTK_IS_SAVEBOX (widget));
508 g_return_if_fail (context != NULL);
510 if (GTK_SAVEBOX (widget)->using_xds)
512 guchar *uri;
513 uri = read_xds_property (context, TRUE);
515 if (uri)
517 guchar *path;
519 path = get_local_path (uri);
521 gtk_signal_emit (GTK_OBJECT (widget),
522 savebox_signals[SAVED_TO_URI],
523 path ? path : uri);
524 g_free(uri);
527 else
528 write_xds_property (context, NULL);
530 if (GTK_SAVEBOX (widget)->data_sent)
531 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
534 static void cancel_clicked (GtkWidget *widget, GtkSavebox *savebox)
536 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_DONE]);
539 /* User has clicked Save or pressed Return... */
540 static void do_save (GtkWidget *widget, GtkSavebox *savebox)
542 gint result = GTK_XDS_NO_HANDLER;
543 guchar *pathname, *uri;
545 g_return_if_fail (savebox != NULL);
546 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
548 uri = gtk_entry_get_text (GTK_ENTRY (savebox->entry));
549 pathname = get_local_path (uri);
551 if (!pathname)
553 GtkWidget *dialog, *label, *button;
555 dialog = gtk_dialog_new ();
556 GTK_WINDOW (dialog)->type = GTK_WINDOW_DIALOG;
558 label = gtk_label_new (_("Drag the icon to a directory viewer\n"
559 "(or enter a full pathname)"));
560 gtk_misc_set_padding (GTK_MISC (label), 8, 32);
562 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
563 label, TRUE, TRUE, 4);
565 button = gtk_button_new_with_label (_("OK"));
566 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
567 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
568 gtk_widget_destroy, GTK_OBJECT (dialog));
569 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
570 button, TRUE, TRUE, 32);
571 gtk_window_set_default (GTK_WINDOW (dialog), button);
573 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
575 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
577 gtk_widget_show_all (dialog);
579 return;
582 g_return_if_fail (pathname != NULL);
584 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_TO_FILE],
585 pathname, &result);
587 if (result == GTK_XDS_SAVED)
589 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVED_TO_URI],
590 pathname);
591 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_DONE]);
593 else if (result == GTK_XDS_NO_HANDLER)
594 g_warning ("No handler for saving to a file.\n");
597 static gint
598 delete_event(GtkWidget *widget, GdkEventAny *event)
600 g_return_val_if_fail (widget != NULL, FALSE);
602 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
604 return TRUE;
607 static gint
608 key_press_event(GtkWidget *widget, GdkEventKey *event)
610 gint (*parent_handler)(GtkWidget *widget, GdkEventKey *event);
612 g_return_val_if_fail (widget != NULL, FALSE);
614 if (event->keyval == GDK_Escape)
616 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
617 return TRUE;
620 parent_handler = GTK_WIDGET_CLASS (parent_class)->key_press_event;
622 if (parent_handler)
623 return parent_handler (widget, event);
625 return FALSE;