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"
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
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
,
94 static void drag_data_get (GtkWidget
*widget
,
95 GdkDragContext
*context
,
96 GtkSelectionData
*selection_data
,
99 static guchar
*read_xds_property (GdkDragContext
*context
,
101 static void write_xds_property (GdkDragContext
*context
,
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
,
109 static gint
key_press_event (GtkWidget
*widget
,
111 static void cancel_clicked (GtkWidget
*widget
,
112 GtkSavebox
*savebox
);
116 gtk_savebox_get_type (void)
118 static GtkType savebox_type
= 0;
122 static const GtkTypeInfo savebox_info
=
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
);
140 #ifndef GTK_CLASS_TYPE
141 # define GTK_CLASS_TYPE(c) (c->type)
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",
162 GTK_CLASS_TYPE(object_class
),
163 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
165 gtk_marshal_INT__POINTER
,
169 savebox_signals
[SAVED_TO_URI
] = gtk_signal_new ("saved_to_uri",
171 GTK_CLASS_TYPE(object_class
),
172 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
174 gtk_marshal_NONE__POINTER
,
178 savebox_signals
[SAVE_DONE
] = gtk_signal_new ("save_done",
180 GTK_CLASS_TYPE(object_class
),
181 GTK_SIGNAL_OFFSET (GtkSaveboxClass
,
183 gtk_marshal_NONE__NONE
,
187 gtk_object_class_add_signals (object_class
, savebox_signals
, LAST_SIGNAL
);
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
);
251 gtk_savebox_new (void)
255 savebox
= gtk_type_new (gtk_savebox_get_type ());
257 return GTK_WIDGET (savebox
);
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
);
268 gtk_pixmap_set (GTK_PIXMAP (savebox
->icon
), pixmap
, mask
);
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
);
278 gtk_savebox_set_pathname (GtkSavebox
*savebox
, gchar
*pathname
)
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);
303 button_press_over_icon (GtkWidget
*drag_box
, GdkEventButton
*event
,
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
));
324 leafname
= strrchr (uri
, '/');
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
),
345 drag_data_get (GtkWidget
*widget
,
346 GdkDragContext
*context
,
347 GtkSelectionData
*selection_data
,
352 guchar to_send
= 'E';
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
;
373 uri
= read_xds_property (context
, FALSE
);
377 gint result
= GTK_XDS_NO_HANDLER
;
379 pathname
= get_local_path (uri
);
381 to_send
= 'F'; /* Not on the local machine */
384 gtk_signal_emit (GTK_OBJECT (widget
),
385 savebox_signals
[SAVE_TO_FILE
],
388 if (result
== GTK_XDS_SAVED
)
390 savebox
->data_sent
= TRUE
;
393 else if (result
!= GTK_XDS_SAVE_ERROR
)
394 g_warning ("No handler for saving to a file.\n");
401 g_warning (_("Remote application wants to use Direct Save, but I can't "
402 "read the XdndDirectSave0 (type text/plain) property.\n"));
406 savebox
->using_xds
= TRUE
;
407 gtk_selection_data_set (selection_data
, xa_string
, 8, &to_send
, 1);
411 read_xds_property (GdkDragContext
*context
, gboolean
delete)
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
)
424 /* Terminate the string */
425 retval
= g_realloc (prop_text
, length
+ 1);
426 retval
[length
] = '\0';
433 write_xds_property (GdkDragContext
*context
, guchar
*value
)
437 gdk_property_change (context
->source_window
, XdndDirectSave
,
438 text_plain
, 8, GDK_PROP_MODE_REPLACE
,
439 value
, strlen (value
));
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
)
454 uri
= read_xds_property (context
, TRUE
);
460 path
= get_local_path (uri
);
462 gtk_signal_emit (GTK_OBJECT (widget
),
463 savebox_signals
[SAVED_TO_URI
],
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
);
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
);
523 g_return_if_fail (pathname
!= NULL
);
525 gtk_signal_emit (GTK_OBJECT (savebox
), savebox_signals
[SAVE_TO_FILE
],
528 if (result
== GTK_XDS_SAVED
)
530 gtk_signal_emit (GTK_OBJECT (savebox
), savebox_signals
[SAVED_TO_URI
],
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");
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
]);
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
]);
561 parent_handler
= GTK_WIDGET_CLASS (parent_class
)->key_press_event
;
564 return parent_handler (widget
, event
);