#ifdef USE_GTK
#include <string.h>
+#include <signal.h>
#include <stdio.h>
#include "lisp.h"
#include "xterm.h"
#include "blockinput.h"
+#include "syssignal.h"
#include "window.h"
#include "atimer.h"
#include "gtkutil.h"
}
-enum
+\f
+/***********************************************************************
+ File dialog functions
+ ***********************************************************************/
+/* Function that is called when the file dialog pops down.
+ W is the dialog widget, RESPONSE is the response code.
+ USER_DATA is what we passed in to g_signal_connect (pointer to int). */
+
+static void
+xg_file_response_cb (w,
+ response,
+ user_data)
+ GtkDialog *w;
+ gint response;
+ gpointer user_data;
{
- XG_FILE_NOT_DONE,
- XG_FILE_OK,
- XG_FILE_CANCEL,
- XG_FILE_DESTROYED,
-};
+ int *ptr = (int *) user_data;
+ *ptr = response;
+}
-/* Callback function invoked when the Ok button is pressed in
- a file dialog.
- W is the file dialog widget,
- ARG points to an integer where we record what has happend. */
-static void
-xg_file_sel_ok (w, arg)
- GtkWidget *w;
- gpointer arg;
+/* Destroy the dialog. This makes it pop down. */
+
+static Lisp_Object
+pop_down_file_dialog (arg)
+ Lisp_Object arg;
{
- *(int*)arg = XG_FILE_OK;
+ struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+ BLOCK_INPUT;
+ gtk_widget_destroy (GTK_WIDGET (p->pointer));
+ UNBLOCK_INPUT;
+ return Qnil;
}
-/* Callback function invoked when the Cancel button is pressed in
- a file dialog.
- W is the file dialog widget,
- ARG points to an integer where we record what has happend. */
+typedef char * (*xg_get_file_func) P_ ((GtkWidget *));
-static void
-xg_file_sel_cancel (w, arg)
+#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
+
+/* Return the selected file for file chooser dialog W.
+ The returned string must be free:d. */
+
+static char *
+xg_get_file_name_from_chooser (w)
GtkWidget *w;
- gpointer arg;
{
- *(int*)arg = XG_FILE_CANCEL;
+ return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
}
-/* Callback function invoked when the file dialog is destroyed (i.e.
- popped down). We must keep track of this, because if this
- happens, GTK destroys the widget. But if for example, Ok is pressed,
- the dialog is popped down, but the dialog widget is not destroyed.
- W is the file dialog widget,
- ARG points to an integer where we record what has happend. */
+/* Read a file name from the user using a file chooser dialog.
+ F is the current frame.
+ PROMPT is a prompt to show to the user. May not be NULL.
+ DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
+ If MUSTMATCH_P is non-zero, the returned file name must be an existing
+ file. *FUNC is set to a function that can be used to retrieve the
+ selected file name from the returned widget.
+
+ Returns the created widget. */
-static void
-xg_file_sel_destroy (w, arg)
+static GtkWidget *
+xg_get_file_with_chooser (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, func)
+ FRAME_PTR f;
+ char *prompt;
+ char *default_filename;
+ int mustmatch_p, only_dir_p;
+ xg_get_file_func *func;
+{
+ GtkWidget *filewin;
+ GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
+ GtkFileChooserAction action = (mustmatch_p ?
+ GTK_FILE_CHOOSER_ACTION_OPEN :
+ GTK_FILE_CHOOSER_ACTION_SAVE);
+
+ if (only_dir_p)
+ action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
+
+ filewin = gtk_file_chooser_dialog_new (prompt, gwin, action,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ (mustmatch_p || only_dir_p ?
+ GTK_STOCK_OPEN : GTK_STOCK_OK),
+ GTK_RESPONSE_OK,
+ NULL);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
+
+ if (default_filename)
+ {
+ Lisp_Object file;
+ struct gcpro gcpro1;
+ GCPRO1 (file);
+
+ file = build_string (default_filename);
+
+ /* File chooser does not understand ~/... in the file name. It must be
+ an absolute name starting with /. */
+ if (default_filename[0] != '/')
+ file = Fexpand_file_name (file, Qnil);
+
+ default_filename = SDATA (file);
+ if (Ffile_directory_p (file))
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filewin),
+ default_filename);
+ else
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (filewin),
+ default_filename);
+
+ UNGCPRO;
+ }
+
+ *func = xg_get_file_name_from_chooser;
+ return filewin;
+}
+#endif /* HAVE_GTK_FILE_CHOOSER_DIALOG_NEW */
+
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+
+/* Return the selected file for file selector dialog W.
+ The returned string must be free:d. */
+
+static char *
+xg_get_file_name_from_selector (w)
GtkWidget *w;
- gpointer arg;
{
- *(int*)arg = XG_FILE_DESTROYED;
+ GtkFileSelection *filesel = GTK_FILE_SELECTION (w);
+ return xstrdup ((char*) gtk_file_selection_get_filename (filesel));
}
-/* Read a file name from the user using a file dialog.
+/* Create a file selection dialog.
F is the current frame.
PROMPT is a prompt to show to the user. May not be NULL.
DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
If MUSTMATCH_P is non-zero, the returned file name must be an existing
- file.
+ file. *FUNC is set to a function that can be used to retrieve the
+ selected file name from the returned widget.
- Returns a file name or NULL if no file was selected.
- The returned string must be freed by the caller. */
+ Returns the created widget. */
-char *
-xg_get_file_name (f, prompt, default_filename, mustmatch_p)
+static GtkWidget *
+xg_get_file_with_selection (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, func)
FRAME_PTR f;
char *prompt;
char *default_filename;
- int mustmatch_p;
+ int mustmatch_p, only_dir_p;
+ xg_get_file_func *func;
{
GtkWidget *filewin;
GtkFileSelection *filesel;
- int filesel_done = XG_FILE_NOT_DONE;
- char *fn = 0;
filewin = gtk_file_selection_new (prompt);
filesel = GTK_FILE_SELECTION (filewin);
- xg_set_screen (filewin, f);
-
- gtk_widget_set_name (filewin, "emacs-filedialog");
-
- gtk_window_set_transient_for (GTK_WINDOW (filewin),
- GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
- gtk_window_set_destroy_with_parent (GTK_WINDOW (filewin), TRUE);
-
- g_signal_connect (G_OBJECT (filesel->ok_button),
- "clicked",
- G_CALLBACK (xg_file_sel_ok),
- &filesel_done);
- g_signal_connect (G_OBJECT (filesel->cancel_button),
- "clicked",
- G_CALLBACK (xg_file_sel_cancel),
- &filesel_done);
- g_signal_connect (G_OBJECT (filesel),
- "destroy",
- G_CALLBACK (xg_file_sel_destroy),
- &filesel_done);
-
if (default_filename)
gtk_file_selection_set_filename (filesel, default_filename);
gtk_file_selection_hide_fileop_buttons (filesel);
}
+ *func = xg_get_file_name_from_selector;
+
+ return filewin;
+}
+#endif /* HAVE_GTK_FILE_SELECTION_NEW */
+
+/* Read a file name from the user using a file dialog, either the old
+ file selection dialog, or the new file chooser dialog. Which to use
+ depends on what the GTK version used has, and what the value of
+ gtk-use-old-file-dialog.
+ F is the current frame.
+ PROMPT is a prompt to show to the user. May not be NULL.
+ DEFAULT_FILENAME is a default selection to be displayed. May be NULL.
+ If MUSTMATCH_P is non-zero, the returned file name must be an existing
+ file.
- gtk_widget_show_all (filewin);
+ Returns a file name or NULL if no file was selected.
+ The returned string must be freed by the caller. */
- while (filesel_done == XG_FILE_NOT_DONE)
- gtk_main_iteration ();
+char *
+xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
+ FRAME_PTR f;
+ char *prompt;
+ char *default_filename;
+ int mustmatch_p, only_dir_p;
+{
+ GtkWidget *w = 0;
+ int count = SPECPDL_INDEX ();
+ char *fn = 0;
+ int filesel_done = 0;
+ xg_get_file_func func;
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ /* I really don't know why this is needed, but without this the GLIBC add on
+ library linuxthreads hangs when the Gnome file chooser backend creates
+ threads. */
+ sigblock (sigmask (__SIGRTMIN));
+#endif /* HAVE_GTK_AND_PTHREAD */
+
+#ifdef HAVE_GTK_FILE_BOTH
+ extern int x_use_old_gtk_file_dialog;
+
+ if (x_use_old_gtk_file_dialog)
+ w = xg_get_file_with_selection (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+ else
+ w = xg_get_file_with_chooser (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+
+#else /* not HAVE_GTK_FILE_BOTH */
+
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+ w = xg_get_file_with_selection (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+#endif
+#ifdef HAVE_GTK_FILE_CHOOSER_DIALOG_NEW
+ w = xg_get_file_with_chooser (f, prompt, default_filename,
+ mustmatch_p, only_dir_p, &func);
+#endif
+
+#endif /* HAVE_GTK_FILE_BOTH */
+
+ xg_set_screen (w, f);
+ gtk_widget_set_name (w, "emacs-filedialog");
+ gtk_window_set_transient_for (GTK_WINDOW (w),
+ GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
+ gtk_window_set_modal (GTK_WINDOW (w), TRUE);
+
+ g_signal_connect (G_OBJECT (w),
+ "response",
+ G_CALLBACK (xg_file_response_cb),
+ &filesel_done);
+
+ /* Don't destroy the widget if closed by the window manager close button. */
+ g_signal_connect (G_OBJECT (w), "delete-event", G_CALLBACK (gtk_true), NULL);
+
+ gtk_widget_show (w);
+
+ record_unwind_protect (pop_down_file_dialog, make_save_value (w, 0));
+ while (! filesel_done)
+ {
+ x_menu_wait_for_event (0);
+ gtk_main_iteration ();
+ }
+
+#if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
+ sigunblock (sigmask (__SIGRTMIN));
+#endif
- if (filesel_done == XG_FILE_OK)
- fn = xstrdup ((char*) gtk_file_selection_get_filename (filesel));
+ if (filesel_done == GTK_RESPONSE_OK)
+ fn = (*func) (w);
- if (filesel_done != XG_FILE_DESTROYED)
- gtk_widget_destroy (filewin);
+ unbind_to (count, Qnil);
return fn;
}
GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
gtk_window_set_destroy_with_parent (GTK_WINDOW (w), TRUE);
gtk_widget_set_name (w, "emacs-dialog");
+ gtk_window_set_modal (GTK_WINDOW (w), TRUE);
}
else if (menu_bar_p || pop_up_p)
{