ti83-sdk/tool/tilem-src/gui/filedlg.c

1040 lines
26 KiB
C
Raw Normal View History

2023-10-08 16:33:37 +00:00
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <gtk/gtk.h>
#include "gtk-compat.h"
#include "filedlg.h"
#ifdef GDK_WINDOWING_WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <commdlg.h>
# include <shlobj.h>
# include <gdk/gdkwin32.h>
# include <wchar.h>
# 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 */