/* * TilEm II * * Copyright (c) 2011 Benjamin Moody * Copyright (c) 2011 Thibault Duponchelle // FIXME : My work is based on yours benjamin. Should I put "portions"?! Or something else ? * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "gtk-compat.h" #include "filedlg.h" #ifdef GDK_WINDOWING_WIN32 # define WIN32_LEAN_AND_MEAN # include # include # include # include # include # ifndef OPENFILENAME_SIZE_VERSION_400 # define OPENFILENAME_SIZE_VERSION_400 sizeof(OPENFILENAMEA) # endif struct fcinfo { const char *title; gboolean save; HWND parent_window; char *filename; char *dirname; char *extension; const char *filters; unsigned int flags; }; #define BUFFER_SIZE 32768 static char * file_chooser_main(const struct fcinfo *fci) { if (G_WIN32_HAVE_WIDECHAR_API()) { OPENFILENAMEW ofnw; wchar_t *titlew, *filterw, *initdirw, *defextw; wchar_t filenamew[BUFFER_SIZE + 1]; wchar_t *p; int result; int i; titlew = g_utf8_to_utf16(fci->title, -1, 0, 0, 0); filterw = g_utf8_to_utf16(fci->filters, -1, 0, 0, 0); for (i = 0; filterw[i]; i++) if (filterw[i] == '\n') filterw[i] = 0; memset(&ofnw, 0, sizeof(ofnw)); ofnw.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofnw.hwndOwner = fci->parent_window; ofnw.lpstrTitle = titlew; ofnw.lpstrFilter = filterw; ofnw.nFilterIndex = 1; ofnw.lpstrFile = filenamew; ofnw.nMaxFile = BUFFER_SIZE; memset(filenamew, 0, sizeof(filenamew)); if (fci->filename) { p = g_utf8_to_utf16(fci->filename, -1, 0, 0, 0); if (p) { wcsncpy(filenamew, p, BUFFER_SIZE); g_free(p); } } if (fci->dirname) initdirw = g_utf8_to_utf16(fci->dirname, -1, 0, 0, 0); else initdirw = NULL; if (fci->extension) defextw = g_utf8_to_utf16(fci->extension, -1, 0, 0, 0); else defextw = NULL; ofnw.lpstrInitialDir = initdirw; ofnw.lpstrDefExt = defextw; ofnw.Flags = fci->flags; result = (fci->save ? GetSaveFileNameW(&ofnw) : GetOpenFileNameW(&ofnw)); g_free(titlew); g_free(filterw); g_free(initdirw); g_free(defextw); if (!result) return NULL; if ((fci->flags & OFN_ALLOWMULTISELECT)) { for (i = 0; i < BUFFER_SIZE; i++) { if (filenamew[i] == 0 && filenamew[i + 1] == 0) break; else if (filenamew[i] == '/') filenamew[i] = '\\'; else if (filenamew[i] == 0) filenamew[i] = '/'; } } return g_utf16_to_utf8(filenamew, -1, 0, 0, 0); } else { OPENFILENAMEA ofna; char *titlel, *filterl, *initdirl, *defextl; char filenamel[BUFFER_SIZE + 1]; char *p; int result; int i; titlel = g_locale_from_utf8(fci->title, -1, 0, 0, 0); filterl = g_locale_from_utf8(fci->filters, -1, 0, 0, 0); for (i = 0; filterl[i]; i++) if (filterl[i] == '\n') filterl[i] = 0; memset(&ofna, 0, sizeof(ofna)); ofna.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofna.hwndOwner = fci->parent_window; ofna.lpstrTitle = titlel; ofna.lpstrFilter = filterl; ofna.nFilterIndex = 1; ofna.lpstrFile = filenamel; ofna.nMaxFile = BUFFER_SIZE; memset(filenamel, 0, sizeof(filenamel)); if (fci->filename) { p = g_locale_from_utf8(fci->filename, -1, 0, 0, 0); if (p) { strncpy(filenamel, p, BUFFER_SIZE); g_free(p); } } if (fci->dirname) initdirl = g_locale_from_utf8(fci->dirname, -1, 0, 0, 0); else initdirl = NULL; if (fci->extension) defextl = g_locale_from_utf8(fci->extension, -1, 0, 0, 0); else defextl = NULL; ofna.lpstrInitialDir = initdirl; ofna.lpstrDefExt = defextl; ofna.Flags = fci->flags; result = (fci->save ? GetSaveFileNameA(&ofna) : GetOpenFileNameA(&ofna)); g_free(titlel); g_free(filterl); g_free(initdirl); g_free(defextl); if (!result) return NULL; if ((fci->flags & OFN_ALLOWMULTISELECT)) { for (i = 0; i < BUFFER_SIZE; i++) { if (filenamel[i] == 0 && filenamel[i + 1] == 0) break; else if (filenamel[i] == '/') filenamel[i] = '\\'; else if (filenamel[i] == 0) filenamel[i] = '/'; } } return g_locale_to_utf8(filenamel, -1, 0, 0, 0); } } static gboolean wakeup(G_GNUC_UNUSED gpointer data) { gtk_main_quit(); return FALSE; } static gpointer file_chooser_thread(gpointer data) { struct fcinfo *fci = data; gpointer res = file_chooser_main(fci); g_idle_add(wakeup, NULL); return res; } static char * build_filter_string(const char *desc1, const char *pattern1, va_list ap) { GString *str = g_string_new(NULL); while (desc1 && pattern1) { if (pattern1[0]) { g_string_append(str, desc1); g_string_append_c(str, '\n'); g_string_append(str, pattern1); g_string_append_c(str, '\n'); } desc1 = va_arg(ap, char *); if (!desc1) break; pattern1 = va_arg(ap, char *); } return g_string_free(str, FALSE); } static char ** run_file_chooser1(const char *title, GtkWindow *parent, gboolean save, gboolean multiple, const char *suggest_name, const char *suggest_dir, const char *filters) { struct fcinfo fci; GThread *thread; GtkWidget *dummy; GdkWindow *pwin; char *fname, *p, *dir; char **result; int i; if (!g_thread_supported()) g_thread_init(NULL); fci.title = title; fci.save = save; if (parent && (pwin = gtk_widget_get_window(GTK_WIDGET(parent)))) fci.parent_window = GDK_WINDOW_HWND(pwin); else fci.parent_window = NULL; if (suggest_name && suggest_dir) { fci.filename = g_build_filename(suggest_dir, suggest_name, NULL); fci.dirname = NULL; } else if (suggest_name) { fci.filename = g_strdup(suggest_name); fci.dirname = NULL; } else if (suggest_dir) { fci.filename = NULL; fci.dirname = g_strdup(suggest_dir); } else { fci.filename = fci.dirname = NULL; } if (suggest_name && (p = strrchr(suggest_name, '.'))) fci.extension = g_strdup(p + 1); else fci.extension = NULL; fci.filters = filters; fci.flags = (OFN_HIDEREADONLY | OFN_EXPLORER); if (save) fci.flags |= OFN_OVERWRITEPROMPT; else { fci.flags |= OFN_FILEMUSTEXIST; if (multiple) fci.flags |= OFN_ALLOWMULTISELECT; } if ((thread = g_thread_create(file_chooser_thread, &fci, TRUE, NULL))) { dummy = gtk_invisible_new(); gtk_grab_add(dummy); gtk_main(); fname = g_thread_join(thread); gtk_widget_destroy(dummy); } else { fname = file_chooser_main(&fci); } g_free(fci.filename); g_free(fci.dirname); g_free(fci.extension); if (!fname) { return NULL; } else if (multiple && (p = strchr(fname, '/'))) { dir = g_strndup(fname, p - fname); result = g_strsplit(p + 1, "/", -1); for (i = 0; result[i]; i++) { p = result[i]; result[i] = g_build_filename(dir, p, NULL); g_free(p); } g_free(fname); return result; } else { result = g_new(char *, 2); result[0] = fname; result[1] = NULL; return result; } } static char ** run_file_chooser(const char *title, GtkWindow *parent, gboolean save, gboolean multiple, const char *suggest_name, const char *suggest_dir, const char *desc1, const char *pattern1, va_list ap) { char *filters; char **result; filters = build_filter_string(desc1, pattern1, ap); result = run_file_chooser1(title, parent, save, multiple, suggest_name, suggest_dir, filters); g_free(filters); return result; } struct dcinfo { const char *title; HWND parent_window; wchar_t *suggest_dir_w; char *suggest_dir_l; }; static int CALLBACK dir_chooser_callback(HWND hwnd, UINT uMsg, G_GNUC_UNUSED LPARAM lParam, LPARAM lpData) { const struct dcinfo *dci = (struct dcinfo*) lpData; if (uMsg != BFFM_INITIALIZED) return 0; if (G_WIN32_HAVE_WIDECHAR_API()) SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) dci->suggest_dir_w); else SendMessageA(hwnd, BFFM_SETSELECTIONA, TRUE, (LPARAM) dci->suggest_dir_l); return 0; } static char * dir_chooser_main(const struct dcinfo *dci) { LPITEMIDLIST idl; char *result = NULL; CoInitialize(NULL); if (G_WIN32_HAVE_WIDECHAR_API()) { BROWSEINFOW bifw; wchar_t dirnamew[MAX_PATH + 1]; memset(&bifw, 0, sizeof(bifw)); bifw.hwndOwner = dci->parent_window; bifw.lpszTitle = g_utf8_to_utf16(dci->title, -1, 0, 0, 0); bifw.ulFlags = (BIF_RETURNONLYFSDIRS | BIF_USENEWUI); bifw.lpfn = &dir_chooser_callback; bifw.lParam = (LPARAM) dci; idl = SHBrowseForFolderW(&bifw); if (idl && SHGetPathFromIDListW(idl, dirnamew)) result = g_utf16_to_utf8(dirnamew, -1, 0, 0, 0); } else { BROWSEINFOA bifa; char dirnamel[MAX_PATH + 1]; memset(&bifa, 0, sizeof(bifa)); bifa.hwndOwner = dci->parent_window; bifa.lpszTitle = g_locale_from_utf8(dci->title, -1, 0, 0, 0); bifa.ulFlags = (BIF_RETURNONLYFSDIRS | BIF_USENEWUI); bifa.lpfn = &dir_chooser_callback; bifa.lParam = (LPARAM) dci; idl = SHBrowseForFolderA(&bifa); if (idl && SHGetPathFromIDListA(idl, dirnamel)) result = g_locale_to_utf8(dirnamel, -1, 0, 0, 0); } if (idl) CoTaskMemFree(idl); CoUninitialize(); return result; } static gpointer dir_chooser_thread(gpointer data) { struct dcinfo *dci = data; gpointer res = dir_chooser_main(dci); g_idle_add(wakeup, NULL); return res; } static char* run_dir_chooser(G_GNUC_UNUSED const char *title, GtkWindow *parent, G_GNUC_UNUSED gboolean save, const char *suggest_dir) { struct dcinfo dci; GdkWindow *pwin; GThread *thread; GtkWidget *dummy; char *dname; if (!g_thread_supported()) g_thread_init(NULL); dci.title = "Select a folder to save received files."; if (parent && (pwin = gtk_widget_get_window(GTK_WIDGET(parent)))) dci.parent_window = GDK_WINDOW_HWND(pwin); else dci.parent_window = NULL; if (suggest_dir) { dci.suggest_dir_w = g_utf8_to_utf16(suggest_dir, -1, 0, 0, 0); dci.suggest_dir_l = g_locale_from_utf8(suggest_dir, -1, 0, 0, 0); } else { dci.suggest_dir_w = NULL; dci.suggest_dir_l = NULL; } if ((thread = g_thread_create(dir_chooser_thread, &dci, TRUE, NULL))) { dummy = gtk_invisible_new(); gtk_grab_add(dummy); gtk_main(); dname = g_thread_join(thread); gtk_widget_destroy(dummy); } else { dname = dir_chooser_main(&dci); } g_free(dci.suggest_dir_w); g_free(dci.suggest_dir_l); return dname; } #else /* ! GDK_WINDOWING_WIN32 */ /* Case insensitive filter function */ static gboolean filter_lowercase(const GtkFileFilterInfo *info, gpointer data) { GSList *list = data; const char *base; char *lowercase, *reversed; int length; gboolean matched = FALSE; if ((base = strrchr(info->filename, G_DIR_SEPARATOR))) base++; else base = info->filename; lowercase = g_ascii_strdown(base, -1); length = strlen(lowercase); reversed = g_memdup(lowercase, length + 1); g_strreverse(reversed); while (list) { if (g_pattern_match(list->data, length, lowercase, reversed)) { matched = TRUE; break; } list = list->next; } g_free(lowercase); g_free(reversed); return matched; } static void free_filter_info(gpointer data) { GSList *list = data, *l; for (l = list; l; l = l->next) g_pattern_spec_free(l->data); g_slist_free(list); } static void setup_file_filters(GtkFileChooser *chooser, const char *desc1, const char *pattern1, va_list ap) { GtkFileFilter *ffilt; char **pats; GPatternSpec *pspec; GSList *pspeclist; int i; while (desc1 && pattern1) { if (pattern1[0]) { ffilt = gtk_file_filter_new(); gtk_file_filter_set_name(ffilt, desc1); pats = g_strsplit(pattern1, ";", -1); pspeclist = NULL; for (i = 0; pats && pats[i]; i++) { pspec = g_pattern_spec_new(pats[i]); pspeclist = g_slist_prepend(pspeclist, pspec); } g_strfreev(pats); gtk_file_filter_add_custom(ffilt, GTK_FILE_FILTER_FILENAME, &filter_lowercase, pspeclist, &free_filter_info); gtk_file_chooser_add_filter(chooser, ffilt); } desc1 = va_arg(ap, char *); if (!desc1) break; pattern1 = va_arg(ap, char *); } } static gboolean prompt_overwrite(const char *fname, GtkWindow *parent) { GtkWidget *dlg; GtkWidget *button; char *p, *q; if (!g_file_test(fname, G_FILE_TEST_EXISTS)) return TRUE; if (!g_file_test(fname, G_FILE_TEST_IS_REGULAR)) return FALSE; p = g_filename_display_basename(fname); dlg = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "A file named \"%s\" already exists. " "Do you want to replace it?", p); g_free(p); p = g_path_get_dirname(fname); q = g_filename_display_basename(p); gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG(dlg), "The file already exists in \"%s\". Replacing it will " "overwrite its contents.", q); g_free(p); g_free(q); gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); button = gtk_button_new_with_mnemonic("_Replace"); gtk_widget_set_can_default(button, TRUE); gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_BUTTON)); gtk_widget_show(button); gtk_dialog_add_action_widget(GTK_DIALOG(dlg), button, GTK_RESPONSE_ACCEPT); gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1); if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) { gtk_widget_destroy(dlg); return TRUE; } gtk_widget_destroy(dlg); return FALSE; } static char ** run_file_chooser(const char *title, GtkWindow *parent, gboolean save, gboolean multiple, const char *suggest_name, const char *suggest_dir, const char *desc1, const char *pattern1, va_list ap) { GtkWidget *filesel; GSList *filelist, *l; char *fname; char **fnames; int i, n; filesel = gtk_file_chooser_dialog_new(title, parent, (save ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, (save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN), GTK_RESPONSE_ACCEPT, NULL); gtk_dialog_set_alternative_button_order(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT); if (suggest_dir) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), suggest_dir); if (suggest_name) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filesel), suggest_name); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filesel), multiple); setup_file_filters(GTK_FILE_CHOOSER(filesel), desc1, pattern1, ap); while (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) { if (save) { fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel)); if (!fname || !prompt_overwrite(fname, GTK_WINDOW(filesel))) { g_free(fname); continue; } fnames = g_new(char *, 2); fnames[0] = fname; fnames[1] = NULL; gtk_widget_destroy(filesel); return fnames; } else { filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(filesel)); if (!filelist) continue; n = g_slist_length(filelist); fnames = g_new(char *, n + 1); i = 0; for (l = filelist; l; l = l->next) fnames[i++] = l->data; g_slist_free(filelist); fnames[n] = NULL; for (i = 0; i < n; i++) if (!g_file_test(fnames[i], G_FILE_TEST_IS_REGULAR)) break; if (i < n) { g_strfreev(fnames); continue; } gtk_widget_destroy(filesel); return fnames; } } gtk_widget_destroy(filesel); return NULL; } static char* run_dir_chooser(const char *title, GtkWindow *parent, gboolean save, const char *suggest_dir) { GtkWidget *filesel; char *fname; filesel = gtk_file_chooser_dialog_new(title, parent, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, (save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN), GTK_RESPONSE_ACCEPT, NULL); gtk_dialog_set_alternative_button_order(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT); if (suggest_dir) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), suggest_dir); while (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) { fname = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(filesel)); if (!fname) { g_free(fname); continue; } gtk_widget_destroy(filesel); return fname; } gtk_widget_destroy(filesel); return NULL; } #endif /* ! GDK_WINDOWING_WIN32 */ char * prompt_open_file(const char *title, GtkWindow *parent, const char *suggest_dir, const char *desc1, const char *pattern1, ...) { char **result, *fname; va_list ap; va_start(ap, pattern1); result = run_file_chooser(title, parent, FALSE, FALSE, NULL, suggest_dir, desc1, pattern1, ap); va_end(ap); if (!result || !result[0] || result[1]) { g_strfreev(result); return NULL; } else { fname = result[0]; g_free(result); return fname; } } char ** prompt_open_files(const char *title, GtkWindow *parent, const char *suggest_dir, const char *desc1, const char *pattern1, ...) { char **result; va_list ap; va_start(ap, pattern1); result = run_file_chooser(title, parent, FALSE, TRUE, NULL, suggest_dir, desc1, pattern1, ap); va_end(ap); return result; } char * prompt_save_file(const char *title, GtkWindow *parent, const char *suggest_name, const char *suggest_dir, const char *desc1, const char *pattern1, ...) { char **result, *fname; va_list ap; va_start(ap, pattern1); result = run_file_chooser(title, parent, TRUE, FALSE, suggest_name, suggest_dir, desc1, pattern1, ap); va_end(ap); if (!result || !result[0] || result[1]) { g_strfreev(result); return NULL; } else { fname = result[0]; g_free(result); return fname; } } char * prompt_select_dir(const char *title, GtkWindow *parent, const char *suggest_dir) { char *dirname; dirname = run_dir_chooser(title, parent, TRUE, suggest_dir); if (!dirname) { return NULL; } else { return dirname; } } /**************** File entry ****************/ #ifdef GDK_WINDOWING_WIN32 typedef struct _FileEntry { GtkHBox parent; GtkWidget *entry; GtkWidget *button; char *title; char *filters; char *filename; } FileEntry; typedef struct _FileEntryClass { GtkHBoxClass parent; } FileEntryClass; static guint selection_changed_signal = 0; G_DEFINE_TYPE(FileEntry, file_entry, GTK_TYPE_HBOX); static void file_entry_finalize(GObject *obj) { FileEntry *fe = (FileEntry*) obj; g_free(fe->title); g_free(fe->filters); g_free(fe->filename); } void file_entry_set_filename(GtkWidget *entry, const char *filename) { FileEntry *fe = (FileEntry*) entry; if (filename && filename[0]) { if (!fe->filename || strcmp(filename, fe->filename)) { g_free(fe->filename); fe->filename = g_strdup(filename); gtk_entry_set_text(GTK_ENTRY(fe->entry), filename); g_signal_emit(fe, selection_changed_signal, 0, NULL); } } else if (fe->filename) { g_free(fe->filename); fe->filename = NULL; g_signal_emit(fe, selection_changed_signal, 0, NULL); } } char * file_entry_get_filename(GtkWidget *entry) { FileEntry *fe = (FileEntry*) entry; if (fe->filename) return g_strdup(fe->filename); else return NULL; } static void focus_changed(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GParamSpec *pspec, gpointer data) { FileEntry *fe = data; const char *text; text = gtk_entry_get_text(GTK_ENTRY(fe->entry)); file_entry_set_filename(GTK_WIDGET(fe), text); } static void browse_for_files(G_GNUC_UNUSED GtkButton *btn, gpointer data) { FileEntry *fe = data; GtkWidget *parent; char **result; char *bname, *dname; parent = gtk_widget_get_toplevel(GTK_WIDGET(fe)); if (fe->filename) { bname = g_path_get_basename(fe->filename); dname = g_path_get_dirname(fe->filename); } else { bname = dname = NULL; } result = run_file_chooser1(fe->title, GTK_WINDOW(parent), FALSE, FALSE, bname, dname, fe->filters); g_free(bname); g_free(dname); if (result && result[0]) file_entry_set_filename(GTK_WIDGET(fe), result[0]); g_strfreev(result); } static void file_entry_init(FileEntry *fe) { gtk_box_set_spacing(GTK_BOX(fe), 6); fe->entry = gtk_entry_new(); fe->button = gtk_button_new_with_label("Browse..."); gtk_box_pack_start(GTK_BOX(fe), fe->entry, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(fe), fe->button, FALSE, FALSE, 0); gtk_widget_show(fe->entry); gtk_widget_show(fe->button); g_signal_connect(fe->entry, "notify::is-focus", G_CALLBACK(focus_changed), fe); g_signal_connect(fe->button, "clicked", G_CALLBACK(browse_for_files), fe); } static void file_entry_class_init(FileEntryClass *class) { GObjectClass *obj_class; obj_class = G_OBJECT_CLASS(class); obj_class->finalize = file_entry_finalize; selection_changed_signal = g_signal_new("selection-changed", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } GtkWidget * file_entry_new(const char *title, const char *desc1, const char *pattern1, ...) { FileEntry *fe = g_object_new(file_entry_get_type(), NULL); va_list ap; fe->title = g_strdup(title); va_start(ap, pattern1); fe->filters = build_filter_string(desc1, pattern1, ap); va_end(ap); return GTK_WIDGET(fe); } #else /* ! GDK_WINDOWING_WIN32 */ GtkWidget * file_entry_new(const char *title, const char *desc1, const char *pattern1, ...) { GtkWidget *btn; va_list ap; btn = gtk_file_chooser_button_new(title, GTK_FILE_CHOOSER_ACTION_OPEN); va_start(ap, pattern1); setup_file_filters(GTK_FILE_CHOOSER(btn), desc1, pattern1, ap); va_end(ap); return btn; } void file_entry_set_filename(GtkWidget *fe, const char *filename) { gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fe), filename); } char * file_entry_get_filename(GtkWidget *fe) { return gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fe)); } #endif /* ! GDK_WINDOWING_WIN32 */