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.
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"
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 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
,
108 static gint
key_press_event (GtkWidget
*widget
,
110 static void cancel_clicked (GtkWidget
*widget
,
111 GtkSavebox
*savebox
);
115 gtk_savebox_get_type (void)
117 static GtkType savebox_type
= 0;
121 static const GtkTypeInfo savebox_info
=
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
);
139 #ifndef GTK_CLASS_TYPE
140 # define GTK_CLASS_TYPE(c) (c->type)
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",
161 GTK_CLASS_TYPE(object_class
),
162 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
164 gtk_marshal_INT__POINTER
,
168 savebox_signals
[SAVED_TO_URI
] = gtk_signal_new ("saved_to_uri",
170 GTK_CLASS_TYPE(object_class
),
171 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
173 gtk_marshal_NONE__POINTER
,
177 savebox_signals
[SAVE_DONE
] = gtk_signal_new ("save_done",
179 GTK_CLASS_TYPE(object_class
),
180 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
182 gtk_marshal_NONE__NONE
,
186 gtk_object_class_add_signals (object_class
, savebox_signals
, LAST_SIGNAL
);
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
);
249 gtk_savebox_new (void)
253 savebox
= gtk_type_new (gtk_savebox_get_type ());
255 return GTK_WIDGET (savebox
);
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
);
266 gtk_pixmap_set (GTK_PIXMAP (savebox
->icon
), pixmap
, mask
);
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
);
276 gtk_savebox_set_pathname (GtkSavebox
*savebox
, gchar
*pathname
)
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);
301 button_press_over_icon (GtkWidget
*drag_box
, GdkEventButton
*event
,
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
));
322 leafname
= strrchr (uri
, '/');
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
),
343 drag_data_get (GtkWidget
*widget
,
344 GdkDragContext
*context
,
345 GtkSelectionData
*selection_data
,
350 guchar to_send
= 'E';
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
;
371 uri
= read_xds_property (context
, FALSE
);
375 gint result
= GTK_XDS_NO_HANDLER
;
377 pathname
= get_local_path (uri
);
379 to_send
= 'F'; /* Not on the local machine */
382 gtk_signal_emit (GTK_OBJECT (widget
),
383 savebox_signals
[SAVE_TO_FILE
],
386 if (result
== GTK_XDS_SAVED
)
388 savebox
->data_sent
= TRUE
;
391 else if (result
!= GTK_XDS_SAVE_ERROR
)
392 g_warning ("No handler for saving to a file.\n");
399 g_warning (_("Remote application wants to use Direct Save, but I can't "
400 "read the XdndDirectSave0 (type text/plain) property.\n"));
404 savebox
->using_xds
= TRUE
;
405 gtk_selection_data_set (selection_data
, xa_string
, 8, &to_send
, 1);
409 read_xds_property (GdkDragContext
*context
, gboolean
delete)
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
)
422 /* Terminate the string */
423 retval
= g_realloc (prop_text
, length
+ 1);
424 retval
[length
] = '\0';
431 write_xds_property (GdkDragContext
*context
, guchar
*value
)
435 gdk_property_change (context
->source_window
, XdndDirectSave
,
436 text_plain
, 8, GDK_PROP_MODE_REPLACE
,
437 value
, strlen (value
));
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
)
452 uri
= read_xds_property (context
, TRUE
);
458 path
= get_local_path (uri
);
460 gtk_signal_emit (GTK_OBJECT (widget
),
461 savebox_signals
[SAVED_TO_URI
],
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
);
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
);
521 g_return_if_fail (pathname
!= NULL
);
523 gtk_signal_emit (GTK_OBJECT (savebox
), savebox_signals
[SAVE_TO_FILE
],
526 if (result
== GTK_XDS_SAVED
)
528 gtk_signal_emit (GTK_OBJECT (savebox
), savebox_signals
[SAVED_TO_URI
],
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");
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
]);
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
]);
559 parent_handler
= GTK_WIDGET_CLASS (parent_class
)->key_press_event
;
562 return parent_handler (widget
, event
);