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/.
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"
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
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
,
93 static void drag_data_get (GtkWidget
*widget
,
94 GdkDragContext
*context
,
95 GtkSelectionData
*selection_data
,
98 static guchar
*read_xds_property (GdkDragContext
*context
,
100 static void write_xds_property (GdkDragContext
*context
,
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
,
110 static gint
key_press_event (GtkWidget
*widget
,
112 static void cancel_clicked (GtkWidget
*widget
,
113 GtkSavebox
*savebox
);
117 gtk_savebox_get_type (void)
119 static GtkType savebox_type
= 0;
123 static const GtkTypeInfo savebox_info
=
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
);
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",
160 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
162 gtk_marshal_INT__POINTER
,
166 savebox_signals
[SAVED_TO_URI
] = gtk_signal_new ("saved_to_uri",
169 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
171 gtk_marshal_NONE__POINTER
,
175 savebox_signals
[SAVE_DONE
] = gtk_signal_new ("save_done",
178 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
180 gtk_marshal_NONE__NONE
,
183 gtk_object_class_add_signals (object_class
, savebox_signals
, LAST_SIGNAL
);
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
);
245 gtk_savebox_new (void)
249 savebox
= gtk_type_new (gtk_savebox_get_type ());
251 return GTK_WIDGET (savebox
);
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
);
262 gtk_pixmap_set (GTK_PIXMAP (savebox
->icon
), pixmap
, mask
);
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
);
272 gtk_savebox_set_pathname (GtkSavebox
*savebox
, gchar
*pathname
)
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);
291 button_press_over_icon (GtkWidget
*drag_box
, GdkEventButton
*event
,
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
));
312 leafname
= strrchr (uri
, '/');
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
),
333 drag_data_get (GtkWidget
*widget
,
334 GdkDragContext
*context
,
335 GtkSelectionData
*selection_data
,
340 guchar to_send
= 'E';
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
;
361 uri
= read_xds_property (context
, FALSE
);
365 gint result
= GTK_XDS_NO_HANDLER
;
367 pathname
= get_local_path (uri
);
369 to_send
= 'F'; /* Not on the local machine */
372 gtk_signal_emit (GTK_OBJECT (widget
),
373 savebox_signals
[SAVE_TO_FILE
],
376 if (result
== GTK_XDS_SAVED
)
378 savebox
->data_sent
= TRUE
;
381 else if (result
!= GTK_XDS_SAVE_ERROR
)
382 g_warning ("No handler for saving to a file.\n");
389 g_warning ("Remote application wants to use Direct Save, but I can't "
390 "read the XdndDirectSave0 (type text/plain) property.\n");
394 savebox
->using_xds
= TRUE
;
395 gtk_selection_data_set (selection_data
, xa_string
, 8, &to_send
, 1);
399 read_xds_property (GdkDragContext
*context
, gboolean
delete)
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
)
412 /* Terminate the string */
413 retval
= g_realloc (prop_text
, length
+ 1);
414 retval
[length
] = '\0';
421 write_xds_property (GdkDragContext
*context
, guchar
*value
)
425 gdk_property_change (context
->source_window
, XdndDirectSave
,
426 text_plain
, 8, GDK_PROP_MODE_REPLACE
,
427 value
, strlen (value
));
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:
440 * This function is quite useful generally - it should be somewhere
441 * publically accessible.
443 static char *get_local_path (char *uri
)
447 host
= our_host_name ();
454 return uri
; /* Just a local path - no host part */
456 path
= strchr (uri
+ 2, '/');
458 return NULL
; /* //something */
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)
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 */
480 /* Return our complete host name */
481 static guchar
*our_host_name (void)
483 static char *name
= NULL
;
487 char buffer
[XDS_MAXURILEN
];
489 if (gethostname (buffer
, XDS_MAXURILEN
))
491 g_warning ("gethostname(): %s\n", g_strerror (errno
));
496 buffer
[XDS_MAXURILEN
- 1] = '\0';
497 name
= g_strdup (buffer
);
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
)
513 uri
= read_xds_property (context
, TRUE
);
519 path
= get_local_path (uri
);
521 gtk_signal_emit (GTK_OBJECT (widget
),
522 savebox_signals
[SAVED_TO_URI
],
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
);
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
);
582 g_return_if_fail (pathname
!= NULL
);
584 gtk_signal_emit (GTK_OBJECT (savebox
), savebox_signals
[SAVE_TO_FILE
],
587 if (result
== GTK_XDS_SAVED
)
589 gtk_signal_emit (GTK_OBJECT (savebox
), savebox_signals
[SAVED_TO_URI
],
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");
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
]);
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
]);
620 parent_handler
= GTK_WIDGET_CLASS (parent_class
)->key_press_event
;
623 return parent_handler (widget
, event
);