r898: Applied Bernard Jungen's latest patch:
[rox-filer.git] / ROX-Filer / src / gtksavebox.c
blobe88dd4401aed94b8ade0f9799f61421dc6cad0bc
1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1991-the ROX-Filer team.
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 * Note: This file is formatted like the Gtk+ sources, as it is/was hoped
22 * to include it in Gtk+ at some point.
25 #include "config.h"
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
31 #include "gdk/gdkkeysyms.h"
33 #include "gtksavebox.h"
34 #include "gtk/gtkwidget.h"
35 #include "gtk/gtkalignment.h"
36 #include "gtk/gtkdnd.h"
37 #include "gtk/gtkbutton.h"
38 #include "gtk/gtksignal.h"
39 #include "gtk/gtkpixmap.h"
40 #include "gtk/gtkhbox.h"
41 #include "gtk/gtkeventbox.h"
42 #include "gtk/gtkentry.h"
43 #include "gtk/gtkhseparator.h"
44 #include "gtk/gtkvbox.h"
45 #include "gtk/gtkdialog.h"
46 #include "gtk/gtklabel.h"
48 #include "global.h"
49 #include "support.h"
51 /* Signals:
53 * gint save_to_file (GtkSavebox *savebox, guchar *pathname)
54 * Save the data to disk using this pathname. Return GTK_XDS_SAVED
55 * on success, or GTK_XDS_SAVE_ERROR on failure (and report the error
56 * to the user somehow). DO NOT mark the data unmodified or change
57 * the pathname for the file - this might be a scrap file transfer.
59 * void saved_to_uri (GtkSavebox *savebox, guchar *uri)
60 * The data is saved and safe. Mark the file as unmodified and update
61 * the pathname/uri for the file to the one given.
63 * void save_done (GtkSavebox *savebox)
64 * The save operation is over. Close the savebox. This signal is sent
65 * regardless of whether the data is now 'safe', but not if no data
66 * has been sent.
69 enum
71 SAVE_TO_FILE,
72 SAVED_TO_URI,
73 SAVE_DONE,
75 LAST_SIGNAL
78 static guint savebox_signals[LAST_SIGNAL] = { 0 };
80 static GtkWidgetClass *parent_class = NULL;
82 /* Longest possible XdndDirectSave0 property value */
83 #define XDS_MAXURILEN 4096
85 static GdkAtom XdndDirectSave;
86 static GdkAtom text_plain;
87 static GdkAtom xa_string;
89 static void gtk_savebox_class_init (GtkSaveboxClass *klass);
90 static void gtk_savebox_init (GtkSavebox *savebox);
91 static void button_press_over_icon (GtkWidget *drag_box,
92 GdkEventButton *event,
93 GtkSavebox *savebox);
94 static void drag_data_get (GtkWidget *widget,
95 GdkDragContext *context,
96 GtkSelectionData *selection_data,
97 guint info,
98 guint32 time);
99 static guchar *read_xds_property (GdkDragContext *context,
100 gboolean delete);
101 static void write_xds_property (GdkDragContext *context,
102 guchar *value);
103 static void drag_end (GtkWidget *widget,
104 GdkDragContext *context);
105 static void do_save (GtkWidget *widget,
106 GtkSavebox *savebox);
107 static gint delete_event (GtkWidget *widget,
108 GdkEventAny *event);
109 static gint key_press_event (GtkWidget *widget,
110 GdkEventKey *event);
111 static void cancel_clicked (GtkWidget *widget,
112 GtkSavebox *savebox);
115 GtkType
116 gtk_savebox_get_type (void)
118 static GtkType savebox_type = 0;
120 if (!savebox_type)
122 static const GtkTypeInfo savebox_info =
124 "GtkSavebox",
125 sizeof (GtkSavebox),
126 sizeof (GtkSaveboxClass),
127 (GtkClassInitFunc) gtk_savebox_class_init,
128 (GtkObjectInitFunc) gtk_savebox_init,
129 /* reserved_1 */ NULL,
130 /* reserved_2 */ NULL,
131 (GtkClassInitFunc) NULL,
134 savebox_type = gtk_type_unique (GTK_TYPE_WINDOW, &savebox_info);
137 return savebox_type;
140 #ifndef GTK_CLASS_TYPE
141 # define GTK_CLASS_TYPE(c) (c->type)
142 #endif
144 static void
145 gtk_savebox_class_init (GtkSaveboxClass *class)
147 GtkObjectClass *object_class = (GtkObjectClass *) class;
148 GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
150 XdndDirectSave = gdk_atom_intern ("XdndDirectSave0", FALSE);
151 text_plain = gdk_atom_intern ("text/plain", FALSE);
152 xa_string = gdk_atom_intern ("STRING", FALSE);
154 parent_class = gtk_type_class(gtk_window_get_type());
156 class->save_to_file = NULL;
157 widget_class->delete_event = delete_event;
158 widget_class->key_press_event = key_press_event;
160 savebox_signals[SAVE_TO_FILE] = gtk_signal_new ("save_to_file",
161 GTK_RUN_LAST,
162 GTK_CLASS_TYPE(object_class),
163 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
164 save_to_file),
165 gtk_marshal_INT__POINTER,
166 GTK_TYPE_INT, 1,
167 GTK_TYPE_POINTER);
169 savebox_signals[SAVED_TO_URI] = gtk_signal_new ("saved_to_uri",
170 GTK_RUN_LAST,
171 GTK_CLASS_TYPE(object_class),
172 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
173 saved_to_uri),
174 gtk_marshal_NONE__POINTER,
175 GTK_TYPE_NONE, 1,
176 GTK_TYPE_POINTER);
178 savebox_signals[SAVE_DONE] = gtk_signal_new ("save_done",
179 GTK_RUN_LAST,
180 GTK_CLASS_TYPE(object_class),
181 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
182 save_done),
183 gtk_marshal_NONE__NONE,
184 GTK_TYPE_NONE, 0);
186 #ifndef GTK2
187 gtk_object_class_add_signals (object_class, savebox_signals, LAST_SIGNAL);
188 #endif
191 static void
192 gtk_savebox_init (GtkSavebox *savebox)
194 GtkWidget *hbox, *button, *alignment;
195 GtkTargetEntry targets[] = { {"XdndDirectSave0", 0, GTK_TARGET_XDS} };
197 savebox->targets = gtk_target_list_new (targets,
198 sizeof (targets) / sizeof (*targets));
199 savebox->icon = NULL;
201 GTK_WINDOW (savebox)->type = GTK_WINDOW_DIALOG;
202 gtk_window_set_title (GTK_WINDOW (savebox), _("Save As:"));
203 gtk_window_set_position (GTK_WINDOW (savebox), GTK_WIN_POS_MOUSE);
204 gtk_window_set_wmclass (GTK_WINDOW (savebox), "savebox", "Savebox");
205 gtk_container_set_border_width (GTK_CONTAINER (savebox), 4);
207 savebox->vbox = gtk_vbox_new (FALSE, 0);
208 gtk_container_add (GTK_CONTAINER (savebox), savebox->vbox);
210 alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
211 gtk_box_pack_start (GTK_BOX (savebox->vbox), alignment, TRUE, TRUE, 0);
213 savebox->drag_box = gtk_event_box_new ();
214 gtk_container_set_border_width (GTK_CONTAINER (savebox->drag_box), 4);
215 gtk_widget_add_events (savebox->drag_box, GDK_BUTTON_PRESS_MASK);
216 gtk_signal_connect (GTK_OBJECT (savebox->drag_box), "button_press_event",
217 GTK_SIGNAL_FUNC (button_press_over_icon), savebox);
218 gtk_signal_connect (GTK_OBJECT (savebox), "drag_end",
219 GTK_SIGNAL_FUNC (drag_end), savebox);
220 gtk_signal_connect (GTK_OBJECT (savebox), "drag_data_get",
221 GTK_SIGNAL_FUNC (drag_data_get), savebox);
222 gtk_container_add (GTK_CONTAINER (alignment), savebox->drag_box);
224 savebox->entry = gtk_entry_new ();
225 gtk_signal_connect (GTK_OBJECT (savebox->entry), "activate",
226 GTK_SIGNAL_FUNC (do_save), savebox);
227 gtk_box_pack_start (GTK_BOX (savebox->vbox), savebox->entry, FALSE, TRUE, 4);
229 hbox = gtk_hbox_new (TRUE, 0);
230 gtk_box_pack_end (GTK_BOX (savebox->vbox), hbox, FALSE, TRUE, 0);
232 button = gtk_button_new_with_label (_("OK"));
233 gtk_signal_connect (GTK_OBJECT (button), "clicked",
234 GTK_SIGNAL_FUNC (do_save), savebox);
235 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
236 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
237 gtk_window_set_default (GTK_WINDOW (savebox), button);
239 button = gtk_button_new_with_label (_("Cancel"));
240 gtk_signal_connect (GTK_OBJECT (button), "clicked",
241 GTK_SIGNAL_FUNC (cancel_clicked), savebox);
242 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
243 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
245 gtk_widget_show_all (savebox->vbox);
247 gtk_widget_grab_focus (savebox->entry);
250 GtkWidget*
251 gtk_savebox_new (void)
253 GtkSavebox *savebox;
255 savebox = gtk_type_new (gtk_savebox_get_type ());
257 return GTK_WIDGET (savebox);
260 void
261 gtk_savebox_set_icon (GtkSavebox *savebox, GdkPixmap *pixmap, GdkPixmap *mask)
263 g_return_if_fail (savebox != NULL);
264 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
265 g_return_if_fail (pixmap != NULL);
267 if (savebox->icon)
268 gtk_pixmap_set (GTK_PIXMAP (savebox->icon), pixmap, mask);
269 else
271 savebox->icon = gtk_pixmap_new (pixmap, mask);
272 gtk_container_add (GTK_CONTAINER (savebox->drag_box), savebox->icon);
273 gtk_widget_show(savebox->icon);
277 void
278 gtk_savebox_set_pathname (GtkSavebox *savebox, gchar *pathname)
280 gchar *slash, *dot;
281 gint leaf;
283 g_return_if_fail (savebox != NULL);
284 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
285 g_return_if_fail (pathname != NULL);
287 gtk_entry_set_text (GTK_ENTRY (savebox->entry), pathname);
289 slash = strrchr (pathname, '/');
291 leaf = slash ? slash - pathname + 1 : 0;
292 dot = strchr(pathname + leaf, '.');
294 /* Gtk+ doesn't seem to scroll the entry properly without this... */
295 gtk_widget_realize (savebox->entry);
296 gtk_entry_set_position (GTK_ENTRY (savebox->entry), -1);
298 gtk_editable_select_region (GTK_EDITABLE (savebox->entry), leaf,
299 dot ? dot - pathname : -1);
302 static void
303 button_press_over_icon (GtkWidget *drag_box, GdkEventButton *event,
304 GtkSavebox *savebox)
306 GdkDragContext *context;
307 GdkPixmap *pixmap, *mask;
308 guchar *uri = NULL, *leafname;
310 g_return_if_fail (savebox != NULL);
311 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
312 g_return_if_fail (event != NULL);
313 g_return_if_fail (savebox->icon != NULL);
315 savebox->using_xds = FALSE;
316 savebox->data_sent = FALSE;
317 context = gtk_drag_begin (GTK_WIDGET (savebox),
318 savebox->targets, GDK_ACTION_COPY,
319 event->button, (GdkEvent *) event);
321 uri = gtk_entry_get_text (GTK_ENTRY (savebox->entry));
322 if (uri && *uri)
324 leafname = strrchr (uri, '/');
325 if (leafname)
326 leafname++;
327 else
328 leafname = uri;
330 else
331 leafname = _("Unnamed");
333 write_xds_property (context, leafname);
335 gtk_pixmap_get (GTK_PIXMAP (savebox->icon), &pixmap, &mask);
336 gtk_drag_set_icon_pixmap (context,
337 gtk_widget_get_colormap (savebox->icon),
338 pixmap,
339 mask,
340 event->x, event->y);
344 static void
345 drag_data_get (GtkWidget *widget,
346 GdkDragContext *context,
347 GtkSelectionData *selection_data,
348 guint info,
349 guint32 time)
351 GtkSavebox *savebox;
352 guchar to_send = 'E';
353 guchar *uri;
354 guchar *pathname;
356 g_return_if_fail (widget != NULL);
357 g_return_if_fail (GTK_IS_SAVEBOX (widget));
358 g_return_if_fail (context != NULL);
359 g_return_if_fail (selection_data != NULL);
361 savebox = GTK_SAVEBOX (widget);
363 /* We're only concerned with the XDS protocol. Responding to other requests
364 * (including application/octet-stream) is the job of the application.
366 if (info != GTK_TARGET_XDS)
368 /* Assume that the data will be/has been sent */
369 savebox->data_sent = TRUE;
370 return;
373 uri = read_xds_property (context, FALSE);
375 if (uri)
377 gint result = GTK_XDS_NO_HANDLER;
379 pathname = get_local_path (uri);
380 if (!pathname)
381 to_send = 'F'; /* Not on the local machine */
382 else
384 gtk_signal_emit (GTK_OBJECT (widget),
385 savebox_signals[SAVE_TO_FILE],
386 pathname, &result);
388 if (result == GTK_XDS_SAVED)
390 savebox->data_sent = TRUE;
391 to_send = 'S';
393 else if (result != GTK_XDS_SAVE_ERROR)
394 g_warning ("No handler for saving to a file.\n");
396 g_free (uri);
399 else
401 g_warning (_("Remote application wants to use Direct Save, but I can't "
402 "read the XdndDirectSave0 (type text/plain) property.\n"));
405 if (to_send != 'E')
406 savebox->using_xds = TRUE;
407 gtk_selection_data_set (selection_data, xa_string, 8, &to_send, 1);
410 static guchar *
411 read_xds_property (GdkDragContext *context, gboolean delete)
413 guchar *prop_text;
414 guint length;
415 guchar *retval = NULL;
417 g_return_val_if_fail (context != NULL, NULL);
419 if (gdk_property_get (context->source_window, XdndDirectSave, text_plain,
420 0, XDS_MAXURILEN, delete,
421 NULL, NULL, &length, &prop_text)
422 && prop_text)
424 /* Terminate the string */
425 retval = g_realloc (prop_text, length + 1);
426 retval[length] = '\0';
429 return retval;
432 static void
433 write_xds_property (GdkDragContext *context, guchar *value)
435 if (value)
437 gdk_property_change (context->source_window, XdndDirectSave,
438 text_plain, 8, GDK_PROP_MODE_REPLACE,
439 value, strlen (value));
441 else
442 gdk_property_delete (context->source_window, XdndDirectSave);
445 static void drag_end (GtkWidget *widget, GdkDragContext *context)
447 g_return_if_fail (widget != NULL);
448 g_return_if_fail (GTK_IS_SAVEBOX (widget));
449 g_return_if_fail (context != NULL);
451 if (GTK_SAVEBOX (widget)->using_xds)
453 guchar *uri;
454 uri = read_xds_property (context, TRUE);
456 if (uri)
458 guchar *path;
460 path = get_local_path (uri);
462 gtk_signal_emit (GTK_OBJECT (widget),
463 savebox_signals[SAVED_TO_URI],
464 path ? path : uri);
465 g_free(uri);
468 else
469 write_xds_property (context, NULL);
471 if (GTK_SAVEBOX (widget)->data_sent)
472 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
475 static void cancel_clicked (GtkWidget *widget, GtkSavebox *savebox)
477 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_DONE]);
480 /* User has clicked Save or pressed Return... */
481 static void do_save (GtkWidget *widget, GtkSavebox *savebox)
483 gint result = GTK_XDS_NO_HANDLER;
484 guchar *pathname, *uri;
486 g_return_if_fail (savebox != NULL);
487 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
489 uri = gtk_entry_get_text (GTK_ENTRY (savebox->entry));
490 pathname = get_local_path (uri);
492 if (!pathname)
494 GtkWidget *dialog, *label, *button;
496 dialog = gtk_dialog_new ();
497 GTK_WINDOW (dialog)->type = GTK_WINDOW_DIALOG;
499 label = gtk_label_new (_("Drag the icon to a directory viewer\n"
500 "(or enter a full pathname)"));
501 gtk_misc_set_padding (GTK_MISC (label), 8, 32);
503 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
504 label, TRUE, TRUE, 4);
506 button = gtk_button_new_with_label (_("OK"));
507 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
508 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
509 GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (dialog));
510 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
511 button, TRUE, TRUE, 32);
512 gtk_window_set_default (GTK_WINDOW (dialog), button);
514 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
516 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
518 gtk_widget_show_all (dialog);
520 return;
523 g_return_if_fail (pathname != NULL);
525 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_TO_FILE],
526 pathname, &result);
528 if (result == GTK_XDS_SAVED)
530 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVED_TO_URI],
531 pathname);
532 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_DONE]);
534 else if (result == GTK_XDS_NO_HANDLER)
535 g_warning ("No handler for saving to a file.\n");
538 static gint
539 delete_event(GtkWidget *widget, GdkEventAny *event)
541 g_return_val_if_fail (widget != NULL, FALSE);
543 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
545 return TRUE;
548 static gint
549 key_press_event(GtkWidget *widget, GdkEventKey *event)
551 gint (*parent_handler)(GtkWidget *widget, GdkEventKey *event);
553 g_return_val_if_fail (widget != NULL, FALSE);
555 if (event->keyval == GDK_Escape)
557 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
558 return TRUE;
561 parent_handler = GTK_WIDGET_CLASS (parent_class)->key_press_event;
563 if (parent_handler)
564 return parent_handler (widget, event);
566 return FALSE;