r845: Require AppRun to be executable before showing a directory as an
[rox-filer.git] / ROX-Filer / src / gtksavebox.c
blob1505f964a86901a9d0cad6447d5ee5f3c7d0330e
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 "support.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 void drag_end (GtkWidget *widget,
103 GdkDragContext *context);
104 static void do_save (GtkWidget *widget,
105 GtkSavebox *savebox);
106 static gint delete_event (GtkWidget *widget,
107 GdkEventAny *event);
108 static gint key_press_event (GtkWidget *widget,
109 GdkEventKey *event);
110 static void cancel_clicked (GtkWidget *widget,
111 GtkSavebox *savebox);
114 GtkType
115 gtk_savebox_get_type (void)
117 static GtkType savebox_type = 0;
119 if (!savebox_type)
121 static const GtkTypeInfo savebox_info =
123 "GtkSavebox",
124 sizeof (GtkSavebox),
125 sizeof (GtkSaveboxClass),
126 (GtkClassInitFunc) gtk_savebox_class_init,
127 (GtkObjectInitFunc) gtk_savebox_init,
128 /* reserved_1 */ NULL,
129 /* reserved_2 */ NULL,
130 (GtkClassInitFunc) NULL,
133 savebox_type = gtk_type_unique (GTK_TYPE_WINDOW, &savebox_info);
136 return savebox_type;
139 #ifndef GTK_CLASS_TYPE
140 # define GTK_CLASS_TYPE(c) (c->type)
141 #endif
143 static void
144 gtk_savebox_class_init (GtkSaveboxClass *class)
146 GtkObjectClass *object_class = (GtkObjectClass *) class;
147 GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
149 XdndDirectSave = gdk_atom_intern ("XdndDirectSave0", FALSE);
150 text_plain = gdk_atom_intern ("text/plain", FALSE);
151 xa_string = gdk_atom_intern ("STRING", FALSE);
153 parent_class = gtk_type_class(gtk_window_get_type());
155 class->save_to_file = NULL;
156 widget_class->delete_event = delete_event;
157 widget_class->key_press_event = key_press_event;
159 savebox_signals[SAVE_TO_FILE] = gtk_signal_new ("save_to_file",
160 GTK_RUN_LAST,
161 GTK_CLASS_TYPE(object_class),
162 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
163 save_to_file),
164 gtk_marshal_INT__POINTER,
165 GTK_TYPE_INT, 1,
166 GTK_TYPE_POINTER);
168 savebox_signals[SAVED_TO_URI] = gtk_signal_new ("saved_to_uri",
169 GTK_RUN_LAST,
170 GTK_CLASS_TYPE(object_class),
171 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
172 saved_to_uri),
173 gtk_marshal_NONE__POINTER,
174 GTK_TYPE_NONE, 1,
175 GTK_TYPE_POINTER);
177 savebox_signals[SAVE_DONE] = gtk_signal_new ("save_done",
178 GTK_RUN_LAST,
179 GTK_CLASS_TYPE(object_class),
180 GTK_SIGNAL_OFFSET (GtkSaveboxClass,
181 save_done),
182 gtk_marshal_NONE__NONE,
183 GTK_TYPE_NONE, 0);
185 #ifndef GTK2
186 gtk_object_class_add_signals (object_class, savebox_signals, LAST_SIGNAL);
187 #endif
190 static void
191 gtk_savebox_init (GtkSavebox *savebox)
193 GtkWidget *hbox, *button, *alignment;
194 GtkTargetEntry targets[] = { {"XdndDirectSave0", 0, GTK_TARGET_XDS} };
196 savebox->targets = gtk_target_list_new (targets,
197 sizeof (targets) / sizeof (*targets));
198 savebox->icon = NULL;
200 GTK_WINDOW (savebox)->type = GTK_WINDOW_DIALOG;
201 gtk_window_set_title (GTK_WINDOW (savebox), _("Save As:"));
202 gtk_window_set_position (GTK_WINDOW (savebox), GTK_WIN_POS_MOUSE);
203 gtk_container_set_border_width (GTK_CONTAINER (savebox), 4);
205 savebox->vbox = gtk_vbox_new (FALSE, 0);
206 gtk_container_add (GTK_CONTAINER (savebox), savebox->vbox);
208 alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
209 gtk_box_pack_start (GTK_BOX (savebox->vbox), alignment, TRUE, TRUE, 0);
211 savebox->drag_box = gtk_event_box_new ();
212 gtk_container_set_border_width (GTK_CONTAINER (savebox->drag_box), 4);
213 gtk_widget_add_events (savebox->drag_box, GDK_BUTTON_PRESS_MASK);
214 gtk_signal_connect (GTK_OBJECT (savebox->drag_box), "button_press_event",
215 GTK_SIGNAL_FUNC (button_press_over_icon), savebox);
216 gtk_signal_connect (GTK_OBJECT (savebox), "drag_end",
217 GTK_SIGNAL_FUNC (drag_end), savebox);
218 gtk_signal_connect (GTK_OBJECT (savebox), "drag_data_get",
219 GTK_SIGNAL_FUNC (drag_data_get), savebox);
220 gtk_container_add (GTK_CONTAINER (alignment), savebox->drag_box);
222 savebox->entry = gtk_entry_new ();
223 gtk_signal_connect (GTK_OBJECT (savebox->entry), "activate",
224 GTK_SIGNAL_FUNC (do_save), savebox);
225 gtk_box_pack_start (GTK_BOX (savebox->vbox), savebox->entry, FALSE, TRUE, 4);
227 hbox = gtk_hbox_new (TRUE, 0);
228 gtk_box_pack_end (GTK_BOX (savebox->vbox), hbox, FALSE, TRUE, 0);
230 button = gtk_button_new_with_label (_("OK"));
231 gtk_signal_connect (GTK_OBJECT (button), "clicked",
232 GTK_SIGNAL_FUNC (do_save), savebox);
233 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
234 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
235 gtk_window_set_default (GTK_WINDOW (savebox), button);
237 button = gtk_button_new_with_label (_("Cancel"));
238 gtk_signal_connect (GTK_OBJECT (button), "clicked",
239 GTK_SIGNAL_FUNC (cancel_clicked), savebox);
240 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
241 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
243 gtk_widget_show_all (savebox->vbox);
245 gtk_widget_grab_focus (savebox->entry);
248 GtkWidget*
249 gtk_savebox_new (void)
251 GtkSavebox *savebox;
253 savebox = gtk_type_new (gtk_savebox_get_type ());
255 return GTK_WIDGET (savebox);
258 void
259 gtk_savebox_set_icon (GtkSavebox *savebox, GdkPixmap *pixmap, GdkPixmap *mask)
261 g_return_if_fail (savebox != NULL);
262 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
263 g_return_if_fail (pixmap != NULL);
265 if (savebox->icon)
266 gtk_pixmap_set (GTK_PIXMAP (savebox->icon), pixmap, mask);
267 else
269 savebox->icon = gtk_pixmap_new (pixmap, mask);
270 gtk_container_add (GTK_CONTAINER (savebox->drag_box), savebox->icon);
271 gtk_widget_show(savebox->icon);
275 void
276 gtk_savebox_set_pathname (GtkSavebox *savebox, gchar *pathname)
278 gchar *slash, *dot;
279 gint leaf;
281 g_return_if_fail (savebox != NULL);
282 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
283 g_return_if_fail (pathname != NULL);
285 gtk_entry_set_text (GTK_ENTRY (savebox->entry), pathname);
287 slash = strrchr (pathname, '/');
289 leaf = slash ? slash - pathname + 1 : 0;
290 dot = strchr(pathname + leaf, '.');
292 /* Gtk+ doesn't seem to scroll the entry properly without this... */
293 gtk_widget_realize (savebox->entry);
294 gtk_entry_set_position (GTK_ENTRY (savebox->entry), -1);
296 gtk_editable_select_region (GTK_EDITABLE (savebox->entry), leaf,
297 dot ? dot - pathname : -1);
300 static void
301 button_press_over_icon (GtkWidget *drag_box, GdkEventButton *event,
302 GtkSavebox *savebox)
304 GdkDragContext *context;
305 GdkPixmap *pixmap, *mask;
306 guchar *uri = NULL, *leafname;
308 g_return_if_fail (savebox != NULL);
309 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
310 g_return_if_fail (event != NULL);
311 g_return_if_fail (savebox->icon != NULL);
313 savebox->using_xds = FALSE;
314 savebox->data_sent = FALSE;
315 context = gtk_drag_begin (GTK_WIDGET (savebox),
316 savebox->targets, GDK_ACTION_COPY,
317 event->button, (GdkEvent *) event);
319 uri = gtk_entry_get_text (GTK_ENTRY (savebox->entry));
320 if (uri && *uri)
322 leafname = strrchr (uri, '/');
323 if (leafname)
324 leafname++;
325 else
326 leafname = uri;
328 else
329 leafname = _("Unnamed");
331 write_xds_property (context, leafname);
333 gtk_pixmap_get (GTK_PIXMAP (savebox->icon), &pixmap, &mask);
334 gtk_drag_set_icon_pixmap (context,
335 gtk_widget_get_colormap (savebox->icon),
336 pixmap,
337 mask,
338 event->x, event->y);
342 static void
343 drag_data_get (GtkWidget *widget,
344 GdkDragContext *context,
345 GtkSelectionData *selection_data,
346 guint info,
347 guint32 time)
349 GtkSavebox *savebox;
350 guchar to_send = 'E';
351 guchar *uri;
352 guchar *pathname;
354 g_return_if_fail (widget != NULL);
355 g_return_if_fail (GTK_IS_SAVEBOX (widget));
356 g_return_if_fail (context != NULL);
357 g_return_if_fail (selection_data != NULL);
359 savebox = GTK_SAVEBOX (widget);
361 /* We're only concerned with the XDS protocol. Responding to other requests
362 * (including application/octet-stream) is the job of the application.
364 if (info != GTK_TARGET_XDS)
366 /* Assume that the data will be/has been sent */
367 savebox->data_sent = TRUE;
368 return;
371 uri = read_xds_property (context, FALSE);
373 if (uri)
375 gint result = GTK_XDS_NO_HANDLER;
377 pathname = get_local_path (uri);
378 if (!pathname)
379 to_send = 'F'; /* Not on the local machine */
380 else
382 gtk_signal_emit (GTK_OBJECT (widget),
383 savebox_signals[SAVE_TO_FILE],
384 pathname, &result);
386 if (result == GTK_XDS_SAVED)
388 savebox->data_sent = TRUE;
389 to_send = 'S';
391 else if (result != GTK_XDS_SAVE_ERROR)
392 g_warning ("No handler for saving to a file.\n");
394 g_free (uri);
397 else
399 g_warning (_("Remote application wants to use Direct Save, but I can't "
400 "read the XdndDirectSave0 (type text/plain) property.\n"));
403 if (to_send != 'E')
404 savebox->using_xds = TRUE;
405 gtk_selection_data_set (selection_data, xa_string, 8, &to_send, 1);
408 static guchar *
409 read_xds_property (GdkDragContext *context, gboolean delete)
411 guchar *prop_text;
412 guint length;
413 guchar *retval = NULL;
415 g_return_val_if_fail (context != NULL, NULL);
417 if (gdk_property_get (context->source_window, XdndDirectSave, text_plain,
418 0, XDS_MAXURILEN, delete,
419 NULL, NULL, &length, &prop_text)
420 && prop_text)
422 /* Terminate the string */
423 retval = g_realloc (prop_text, length + 1);
424 retval[length] = '\0';
427 return retval;
430 static void
431 write_xds_property (GdkDragContext *context, guchar *value)
433 if (value)
435 gdk_property_change (context->source_window, XdndDirectSave,
436 text_plain, 8, GDK_PROP_MODE_REPLACE,
437 value, strlen (value));
439 else
440 gdk_property_delete (context->source_window, XdndDirectSave);
443 static void drag_end (GtkWidget *widget, GdkDragContext *context)
445 g_return_if_fail (widget != NULL);
446 g_return_if_fail (GTK_IS_SAVEBOX (widget));
447 g_return_if_fail (context != NULL);
449 if (GTK_SAVEBOX (widget)->using_xds)
451 guchar *uri;
452 uri = read_xds_property (context, TRUE);
454 if (uri)
456 guchar *path;
458 path = get_local_path (uri);
460 gtk_signal_emit (GTK_OBJECT (widget),
461 savebox_signals[SAVED_TO_URI],
462 path ? path : uri);
463 g_free(uri);
466 else
467 write_xds_property (context, NULL);
469 if (GTK_SAVEBOX (widget)->data_sent)
470 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
473 static void cancel_clicked (GtkWidget *widget, GtkSavebox *savebox)
475 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_DONE]);
478 /* User has clicked Save or pressed Return... */
479 static void do_save (GtkWidget *widget, GtkSavebox *savebox)
481 gint result = GTK_XDS_NO_HANDLER;
482 guchar *pathname, *uri;
484 g_return_if_fail (savebox != NULL);
485 g_return_if_fail (GTK_IS_SAVEBOX (savebox));
487 uri = gtk_entry_get_text (GTK_ENTRY (savebox->entry));
488 pathname = get_local_path (uri);
490 if (!pathname)
492 GtkWidget *dialog, *label, *button;
494 dialog = gtk_dialog_new ();
495 GTK_WINDOW (dialog)->type = GTK_WINDOW_DIALOG;
497 label = gtk_label_new (_("Drag the icon to a directory viewer\n"
498 "(or enter a full pathname)"));
499 gtk_misc_set_padding (GTK_MISC (label), 8, 32);
501 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
502 label, TRUE, TRUE, 4);
504 button = gtk_button_new_with_label (_("OK"));
505 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
506 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
507 GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (dialog));
508 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
509 button, TRUE, TRUE, 32);
510 gtk_window_set_default (GTK_WINDOW (dialog), button);
512 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
514 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
516 gtk_widget_show_all (dialog);
518 return;
521 g_return_if_fail (pathname != NULL);
523 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_TO_FILE],
524 pathname, &result);
526 if (result == GTK_XDS_SAVED)
528 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVED_TO_URI],
529 pathname);
530 gtk_signal_emit (GTK_OBJECT (savebox), savebox_signals[SAVE_DONE]);
532 else if (result == GTK_XDS_NO_HANDLER)
533 g_warning ("No handler for saving to a file.\n");
536 static gint
537 delete_event(GtkWidget *widget, GdkEventAny *event)
539 g_return_val_if_fail (widget != NULL, FALSE);
541 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
543 return TRUE;
546 static gint
547 key_press_event(GtkWidget *widget, GdkEventKey *event)
549 gint (*parent_handler)(GtkWidget *widget, GdkEventKey *event);
551 g_return_val_if_fail (widget != NULL, FALSE);
553 if (event->keyval == GDK_Escape)
555 gtk_signal_emit (GTK_OBJECT (widget), savebox_signals[SAVE_DONE]);
556 return TRUE;
559 parent_handler = GTK_WIDGET_CLASS (parent_class)->key_press_event;
561 if (parent_handler)
562 return parent_handler (widget, event);
564 return FALSE;