gntfilesel.c

Tue, 21 May 2019 01:20:54 +0000

author
Gary Kramlich <grim@reaperworld.com>
date
Tue, 21 May 2019 01:20:54 +0000
changeset 1324
4e6d829dcbc5
parent 1319
2b331e084d56
child 1327
8278895039b8
permissions
-rw-r--r--

Merged in default (pull request #78)

Clear the main loop on WM destruction.

Approved-by: Gary Kramlich

/*
 * GNT - The GLib Ncurses Toolkit
 *
 * GNT is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This library 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 2 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "gntinternal.h"
#undef GNT_LOG_DOMAIN
#define GNT_LOG_DOMAIN "FileSel"

#include "gntbutton.h"
#include "gntentry.h"
#include "gntfilesel.h"
#include "gntlabel.h"
#include "gntstyle.h"
#include "gnttree.h"

#include "gntwidgetprivate.h"

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <glib/gstdio.h>

typedef struct
{
	GntWindow parent;

	GntWidget *dirs;     /* list of files */
	GntWidget *files;    /* list of directories */
	GntWidget *location; /* location entry */

	GntWidget *select; /* select button */
	GntWidget *cancel; /* cancel button */

	char *current; /* Full path of the current location */
	char *suggest; /* Suggested filename */
	/* XXX: someone should make these useful */
	gboolean must_exist; /* Make sure the selected file (the name entered in
	                        'location') exists */
	gboolean dirsonly;   /* Show only directories */
	gboolean multiselect;
	GList *tags; /* List of tagged files when multiselect is set */

	gboolean (*read_fn)(const char *path, GList **files, GError **error);
} GntFileSelPrivate;

enum
{
	SIG_FILE_SELECTED,
	SIG_CANCELLED,
	SIGS
};

static guint signals[SIGS] = { 0 };
static void (*orig_map)(GntWidget *widget);
static void (*orig_size_request)(GntWidget *widget);

static void select_activated_cb(GntWidget *button, GntFileSel *sel);
static void cancel_activated_cb(GntWidget *button, GntFileSel *sel);

G_DEFINE_TYPE_WITH_PRIVATE(GntFileSel, gnt_file_sel, GNT_TYPE_WINDOW)

static void
gnt_file_sel_destroy(GntWidget *widget)
{
	GntFileSel *sel = GNT_FILE_SEL(widget);
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);

	g_free(priv->current);
	g_free(priv->suggest);
	if (priv->tags) {
		g_list_free_full(priv->tags, g_free);
	}
}

static char *
process_path(const char *path)
{
	char **splits = NULL;
	int i, j;
	char *str, *ret;

	splits = g_strsplit(path, G_DIR_SEPARATOR_S, -1);
	for (i = 0, j = 0; splits[i]; i++) {
		if (strcmp(splits[i], ".") == 0) {
			g_free(splits[i]);
			splits[i] = NULL;
		} else if (strcmp(splits[i], "..") == 0) {
			if (j)
				j--;
			g_free(splits[i]);
			splits[i] = NULL;
		} else {
			if (i != j) {
				g_free(splits[j]);
				splits[j] = splits[i];
				splits[i] = NULL;
			}
			j++;
		}
	}
	g_free(splits[j]);
	splits[j] = NULL;
	str = g_build_pathv(G_DIR_SEPARATOR_S, splits);
	ret = g_strdup_printf(G_DIR_SEPARATOR_S "%s", str);
	g_free(str);
	g_strfreev(splits);
	return ret;
}

static void
update_location(GntFileSel *sel)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	char *old;
	const char *tmp;
	tmp = priv->suggest ? priv->suggest
	                    : (const char *)gnt_tree_get_selection_data(
	                              priv->dirsonly ? GNT_TREE(priv->dirs)
	                                             : GNT_TREE(priv->files));
	old = g_strdup_printf("%s%s%s", SAFE(priv->current),
	                      SAFE(priv->current)[1] ? G_DIR_SEPARATOR_S : "",
	                      tmp ? tmp : "");
	gnt_entry_set_text(GNT_ENTRY(priv->location), old);
	g_free(old);
}

static gboolean
is_tagged(GntFileSel *sel, const char *f)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	char *ret =
	        g_strdup_printf("%s%s%s", priv->current,
	                        priv->current[1] ? G_DIR_SEPARATOR_S : "", f);
	gboolean find =
	        g_list_find_custom(priv->tags, ret,
	                           (GCompareFunc)g_utf8_collate) != NULL;
	g_free(ret);
	return find;
}

GntFile* gnt_file_new_dir(const char *name)
{
	GntFile *file = g_new0(GntFile, 1);
	file->basename = g_strdup(name);
	file->type = GNT_FILE_DIR;
	return file;
}

GntFile* gnt_file_new(const char *name, unsigned long size)
{
	GntFile *file = g_new0(GntFile, 1);
	file->basename = g_strdup(name);
	file->type = GNT_FILE_REGULAR;
	file->size = size;
	return file;
}

static gboolean
local_read_fn(const char *path, GList **files, GError **error)
{
	GDir *dir;
	GntFile *file;
	const char *str;

	dir = g_dir_open(path, 0, error);
	if (dir == NULL || (error && *error)) {
		return FALSE;
	}

	*files = NULL;
	if (*path != '\0' && strcmp(path, G_DIR_SEPARATOR_S)) {
		file = gnt_file_new_dir("..");
		*files = g_list_prepend(*files, file);
	}

	while ((str = g_dir_read_name(dir)) != NULL) {
		char *fp = g_build_filename(path, str, NULL);
		GStatBuf st;

		if (g_stat(fp, &st)) {
			gnt_warning("Error stating location %s", fp);
		} else {
			if (S_ISDIR(st.st_mode)) {
				file = gnt_file_new_dir(str);
			} else {
				file = gnt_file_new(str, (long)st.st_size);
			}
			*files = g_list_prepend(*files, file);
		}
		g_free(fp);
	}
	g_dir_close(dir);

	*files = g_list_reverse(*files);
	return TRUE;
}

static void
gnt_file_free(GntFile *file)
{
	g_return_if_fail(file != NULL);

	g_free(file->fullpath);
	g_free(file->basename);
	g_free(file);
}

static gboolean
location_changed(GntFileSel *sel, GError **err)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	GList *files, *iter;
	gboolean success;

	if (!priv->dirs) {
		return TRUE;
	}

	gnt_tree_remove_all(GNT_TREE(priv->dirs));
	if (priv->files) {
		gnt_tree_remove_all(GNT_TREE(priv->files));
	}
	gnt_entry_set_text(GNT_ENTRY(priv->location), NULL);
	if (priv->current == NULL) {
		if (gnt_widget_get_mapped(GNT_WIDGET(sel))) {
			gnt_widget_draw(GNT_WIDGET(sel));
		}
		return TRUE;
	}

	/* XXX:\
	 * XXX: This is blocking.
	 * XXX:/
	 */
	files = NULL;
	if (priv->read_fn) {
		success = priv->read_fn(priv->current, &files, err);
	} else {
		success = local_read_fn(priv->current, &files, err);
	}

	if (!success || *err) {
		gnt_warning("error opening location %s (%s)", priv->current,
		            *err ? (*err)->message : "reason unknown");
		return FALSE;
	}

	for (iter = files; iter; iter = iter->next) {
		GntFile *file = iter->data;
		char *str = file->basename;
		if (file->type == GNT_FILE_DIR) {
			gnt_tree_add_row_after(
			        GNT_TREE(priv->dirs), g_strdup(str),
			        gnt_tree_create_row(GNT_TREE(priv->dirs), str),
			        NULL, NULL);
			if (priv->multiselect && priv->dirsonly &&
			    is_tagged(sel, str)) {
				gnt_tree_set_row_flags(GNT_TREE(priv->dirs),
				                       (gpointer)str,
				                       GNT_TEXT_FLAG_BOLD);
			}
		} else if (!priv->dirsonly) {
			char size[128];
			snprintf(size, sizeof(size), "%ld", file->size);

			gnt_tree_add_row_after(
			        GNT_TREE(priv->files), g_strdup(str),
			        gnt_tree_create_row(GNT_TREE(priv->files), str,
			                            size, ""),
			        NULL, NULL);
			if (priv->multiselect && is_tagged(sel, str)) {
				gnt_tree_set_row_flags(GNT_TREE(priv->files),
				                       (gpointer)str,
				                       GNT_TEXT_FLAG_BOLD);
			}
		}
	}
	g_list_free_full(files, (GDestroyNotify)gnt_file_free);
	if (gnt_widget_get_mapped(GNT_WIDGET(sel))) {
		gnt_widget_draw(GNT_WIDGET(sel));
	}
	return TRUE;
}

static gboolean
dir_key_pressed(GntTree *tree, const char *key, GntFileSel *sel)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	if (strcmp(key, "\r") == 0 || strcmp(key, "\n") == 0) {
		char *str = g_strdup(gnt_tree_get_selection_data(tree));
		char *path, *dir;

		if (!str)
			return TRUE;

		path = g_build_filename(priv->current, str, NULL);
		dir = g_path_get_basename(priv->current);
		if (!gnt_file_sel_set_current_location(sel, path)) {
			gnt_tree_set_selected(tree, str);
		} else if (strcmp(str, "..") == 0) {
			gnt_tree_set_selected(tree, dir);
		}
		gnt_bindable_perform_action_named(GNT_BINDABLE(tree), "end-search", NULL);
		g_free(dir);
		g_free(str);
		g_free(path);
		return TRUE;
	}
	return FALSE;
}

static gboolean
location_key_pressed(G_GNUC_UNUSED GntTree *tree, const char *key,
                     GntFileSel *sel)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	char *path;
	char *str;

	if (strcmp(key, "\r") && strcmp(key, "\n"))
		return FALSE;

	str = (char *)gnt_entry_get_text(GNT_ENTRY(priv->location));
	if (*str == G_DIR_SEPARATOR)
		path = g_strdup(str);
	else
		path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
		                       priv->current, str);
	str = process_path(path);
	g_free(path);
	path = str;

	if (gnt_file_sel_set_current_location(sel, path))
		goto success;

	path = g_path_get_dirname(str);
	g_free(str);

	if (!gnt_file_sel_set_current_location(sel, path)) {
		g_free(path);
		return FALSE;
	}

	/* XXX: Add support for globbing via g_pattern_spec_* */

success:
	g_free(path);
	return TRUE;
}

static void
file_sel_changed(GntWidget *widget, G_GNUC_UNUSED gpointer old,
                 G_GNUC_UNUSED gpointer current, GntFileSel *sel)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	if (gnt_widget_get_has_focus(widget)) {
		g_free(priv->suggest);
		priv->suggest = NULL;
		update_location(sel);
	}
}

static void
gnt_file_sel_map(GntWidget *widget)
{
	GntFileSel *sel = GNT_FILE_SEL(widget);
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	GntWidget *hbox, *vbox;

	if (priv->current == NULL) {
		gnt_file_sel_set_current_location(sel, g_get_home_dir());
	}

	vbox = gnt_vbox_new(FALSE);
	gnt_box_set_pad(GNT_BOX(vbox), 0);
	gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_MID);

	/* The dir. and files list */
	hbox = gnt_hbox_new(FALSE);
	gnt_box_set_pad(GNT_BOX(hbox), 0);

	gnt_box_add_widget(GNT_BOX(hbox), priv->dirs);

	if (!priv->dirsonly) {
		gnt_box_add_widget(GNT_BOX(hbox), priv->files);
	} else {
		g_signal_connect(G_OBJECT(priv->dirs), "selection_changed",
		                 G_CALLBACK(file_sel_changed), sel);
	}

	gnt_box_add_widget(GNT_BOX(vbox), hbox);
	gnt_box_add_widget(GNT_BOX(vbox), priv->location);

	/* The buttons */
	hbox = gnt_hbox_new(FALSE);
	gnt_box_add_widget(GNT_BOX(hbox), priv->cancel);
	gnt_box_add_widget(GNT_BOX(hbox), priv->select);
	gnt_box_add_widget(GNT_BOX(vbox), hbox);

	gnt_box_add_widget(GNT_BOX(sel), vbox);
	orig_map(widget);
	update_location(sel);
}

static gboolean
toggle_tag_selection(GntBindable *bind, G_GNUC_UNUSED GList *params)
{
	GntFileSel *sel = GNT_FILE_SEL(bind);
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	char *str;
	GList *find;
	char *file;
	GntWidget *tree;

	if (!priv->multiselect) {
		return FALSE;
	}
	tree = priv->dirsonly ? priv->dirs : priv->files;
	if (!gnt_widget_has_focus(tree) ||
			gnt_tree_is_searching(GNT_TREE(tree)))
		return FALSE;

	file = gnt_tree_get_selection_data(GNT_TREE(tree));

	str = gnt_file_sel_get_selected_file(sel);
	if ((find = g_list_find_custom(priv->tags, str,
	                               (GCompareFunc)g_utf8_collate)) != NULL) {
		g_free(find->data);
		priv->tags = g_list_delete_link(priv->tags, find);
		gnt_tree_set_row_flags(GNT_TREE(tree), file, GNT_TEXT_FLAG_NORMAL);
		g_free(str);
	} else {
		priv->tags = g_list_prepend(priv->tags, str);
		gnt_tree_set_row_flags(GNT_TREE(tree), file, GNT_TEXT_FLAG_BOLD);
	}

	gnt_bindable_perform_action_named(GNT_BINDABLE(tree), "move-down", NULL);

	return TRUE;
}

static gboolean
clear_tags(GntBindable *bind, G_GNUC_UNUSED GList *params)
{
	GntFileSel *sel = GNT_FILE_SEL(bind);
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	GntWidget *tree;
	GList *iter;

	if (!priv->multiselect) {
		return FALSE;
	}
	tree = priv->dirsonly ? priv->dirs : priv->files;
	if (!gnt_widget_has_focus(tree) ||
			gnt_tree_is_searching(GNT_TREE(tree)))
		return FALSE;

	g_list_free_full(priv->tags, g_free);
	priv->tags = NULL;

	for (iter = gnt_tree_get_rows(GNT_TREE(tree)); iter;
	     iter = iter->next) {
		gnt_tree_set_row_flags(GNT_TREE(tree), iter->data, GNT_TEXT_FLAG_NORMAL);
	}

	return TRUE;
}

static gboolean
up_directory(GntBindable *bind, G_GNUC_UNUSED GList *params)
{
	char *path, *dir;
	GntFileSel *sel = GNT_FILE_SEL(bind);
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	if (!gnt_widget_has_focus(priv->dirs) &&
	    !gnt_widget_has_focus(priv->files)) {
		return FALSE;
	}
	if (gnt_tree_is_searching(GNT_TREE(priv->dirs)) ||
	    gnt_tree_is_searching(GNT_TREE(priv->files))) {
		return FALSE;
	}

	path = g_build_filename(priv->current, "..", NULL);
	dir = g_path_get_basename(priv->current);
	if (gnt_file_sel_set_current_location(sel, path)) {
		gnt_tree_set_selected(GNT_TREE(priv->dirs), dir);
	}
	g_free(dir);
	g_free(path);
	return TRUE;
}

static void
gnt_file_sel_size_request(GntWidget *widget)
{
	GntFileSelPrivate *priv = NULL;
	gint width, height;

	gnt_widget_get_internal_size(widget, NULL, &height);
	if (height > 0) {
		return;
	}

	priv = gnt_file_sel_get_instance_private(GNT_FILE_SEL(widget));

	gnt_widget_get_internal_size(priv->dirs, &width, NULL);
	gnt_widget_set_internal_size(priv->dirs, width, 16);

	gnt_widget_get_internal_size(priv->files, &width, NULL);
	gnt_widget_set_internal_size(priv->files, width, 16);

	orig_size_request(widget);
}

static void
gnt_file_sel_class_init(GntFileSelClass *klass)
{
	GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
	GntWidgetClass *widget_class = GNT_WIDGET_CLASS(klass);

	widget_class->destroy = gnt_file_sel_destroy;
	orig_map = widget_class->map;
	widget_class->map = gnt_file_sel_map;
	orig_size_request = widget_class->size_request;
	widget_class->size_request = gnt_file_sel_size_request;

	/**
	 * GntFileSel::file-selected:
	 * @widget: The file selection window that received the signal
	 * @path: The full path to the selected file
	 * @file: The name of the file only
	 *
	 * The ::file-selected signal is emitted when the Select button or the
	 * file tree on a #GntFileSel is activated.
	 *
	 * Since: 2.1.0
	 */
	signals[SIG_FILE_SELECTED] =
		g_signal_new("file_selected",
					 G_TYPE_FROM_CLASS(klass),
					 G_SIGNAL_RUN_LAST,
					 G_STRUCT_OFFSET(GntFileSelClass, file_selected),
					 NULL, NULL, NULL,
					 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);

	/**
	 * GntFileSel::cancelled:
	 * @widget: The file selection window that received the signal
	 *
	 * The ::cancelled signal is emitted when the Cancel button on a
	 * #GntFileSel is activated.
	 *
	 * Since: 2.14.0
	 */
	signals[SIG_CANCELLED] =
		g_signal_new("cancelled",
				G_TYPE_FROM_CLASS(klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET(GntFileSelClass, cancelled),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);

	gnt_bindable_class_register_action(bindable, "toggle-tag", toggle_tag_selection, "t", NULL);
	gnt_bindable_class_register_action(bindable, "clear-tags", clear_tags, "c", NULL);
	gnt_bindable_class_register_action(bindable, "up-directory", up_directory, GNT_KEY_BACKSPACE, NULL);
	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
}

static void
gnt_file_sel_init(GntFileSel *sel)
{
	GntFileSelPrivate *priv = gnt_file_sel_get_instance_private(sel);
	priv->dirs = gnt_tree_new();
	gnt_tree_set_compare_func(GNT_TREE(priv->dirs),
	                          (GCompareFunc)g_utf8_collate);
	gnt_tree_set_hash_fns(GNT_TREE(priv->dirs), g_str_hash, g_str_equal,
	                      g_free);
	gnt_tree_set_column_titles(GNT_TREE(priv->dirs), "Directories");
	gnt_tree_set_show_title(GNT_TREE(priv->dirs), TRUE);
	gnt_tree_set_col_width(GNT_TREE(priv->dirs), 0, 20);
	g_signal_connect(G_OBJECT(priv->dirs), "key_pressed",
	                 G_CALLBACK(dir_key_pressed), sel);

	priv->files = gnt_tree_new_with_columns(2); /* Name, Size */
	gnt_tree_set_compare_func(GNT_TREE(priv->files),
	                          (GCompareFunc)g_utf8_collate);
	gnt_tree_set_hash_fns(GNT_TREE(priv->files), g_str_hash, g_str_equal,
	                      g_free);
	gnt_tree_set_column_titles(GNT_TREE(priv->files), "Filename", "Size");
	gnt_tree_set_show_title(GNT_TREE(priv->files), TRUE);
	gnt_tree_set_col_width(GNT_TREE(priv->files), 0, 25);
	gnt_tree_set_col_width(GNT_TREE(priv->files), 1, 10);
	gnt_tree_set_column_is_right_aligned(GNT_TREE(priv->files), 1, TRUE);
	g_signal_connect(G_OBJECT(priv->files), "selection_changed",
	                 G_CALLBACK(file_sel_changed), sel);

	/* The location entry */
	priv->location = gnt_entry_new(NULL);
	g_signal_connect(G_OBJECT(priv->location), "key_pressed",
	                 G_CALLBACK(location_key_pressed), sel);

	priv->cancel = gnt_button_new("Cancel");
	g_signal_connect(G_OBJECT(priv->cancel), "activate",
	                 G_CALLBACK(cancel_activated_cb), sel);

	priv->select = gnt_button_new("Select");

	g_signal_connect_swapped(G_OBJECT(priv->files), "activate",
	                         G_CALLBACK(gnt_widget_activate), priv->select);
	g_signal_connect(G_OBJECT(priv->select), "activate",
	                 G_CALLBACK(select_activated_cb), sel);
}

/******************************************************************************
 * GntFileSel API
 *****************************************************************************/
static void
select_activated_cb(G_GNUC_UNUSED GntWidget *button, GntFileSel *sel)
{
	char *path = gnt_file_sel_get_selected_file(sel);
	char *file = g_path_get_basename(path);
	g_signal_emit(sel, signals[SIG_FILE_SELECTED], 0, path, file);
	g_free(file);
	g_free(path);
}

static void
cancel_activated_cb(G_GNUC_UNUSED GntWidget *button, GntFileSel *sel)
{
	g_signal_emit(sel, signals[SIG_CANCELLED], 0);
}

GntWidget *gnt_file_sel_new(void)
{
	GntWidget *widget = g_object_new(GNT_TYPE_FILE_SEL, NULL);
	return widget;
}

gboolean gnt_file_sel_set_current_location(GntFileSel *sel, const char *path)
{
	GntFileSelPrivate *priv = NULL;
	char *old;
	GError *error = NULL;
	gboolean ret = TRUE;

	g_return_val_if_fail(GNT_IS_FILE_SEL(sel), FALSE);
	priv = gnt_file_sel_get_instance_private(sel);

	old = priv->current;
	priv->current = process_path(path);
	if (!location_changed(sel, &error)) {
		g_error_free(error);
		error = NULL;
		g_free(priv->current);
		priv->current = old;
		location_changed(sel, &error);
		ret = FALSE;
	} else
		g_free(old);

	update_location(sel);
	return ret;
}

void gnt_file_sel_set_dirs_only(GntFileSel *sel, gboolean dirs)
{
	GntFileSelPrivate *priv = NULL;

	g_return_if_fail(GNT_IS_FILE_SEL(sel));
	priv = gnt_file_sel_get_instance_private(sel);

	priv->dirsonly = dirs;
}

gboolean gnt_file_sel_get_dirs_only(GntFileSel *sel)
{
	GntFileSelPrivate *priv = NULL;

	g_return_val_if_fail(GNT_IS_FILE_SEL(sel), FALSE);
	priv = gnt_file_sel_get_instance_private(sel);

	return priv->dirsonly;
}

void gnt_file_sel_set_suggested_filename(GntFileSel *sel, const char *suggest)
{
	GntFileSelPrivate *priv = NULL;

	g_return_if_fail(GNT_IS_FILE_SEL(sel));
	priv = gnt_file_sel_get_instance_private(sel);

	g_free(priv->suggest);
	priv->suggest = g_strdup(suggest);
}

char *gnt_file_sel_get_selected_file(GntFileSel *sel)
{
	GntFileSelPrivate *priv = NULL;
	char *ret;

	g_return_val_if_fail(GNT_IS_FILE_SEL(sel), NULL);
	priv = gnt_file_sel_get_instance_private(sel);

	if (priv->dirsonly) {
		ret = g_path_get_dirname(
		        gnt_entry_get_text(GNT_ENTRY(priv->location)));
	} else {
		ret = g_strdup(gnt_entry_get_text(GNT_ENTRY(priv->location)));
	}
	return ret;
}

void gnt_file_sel_set_must_exist(GntFileSel *sel, gboolean must)
{
	GntFileSelPrivate *priv = NULL;

	g_return_if_fail(GNT_IS_FILE_SEL(sel));
	priv = gnt_file_sel_get_instance_private(sel);

	/*XXX: What do I do with this? */
	priv->must_exist = must;
}

gboolean gnt_file_sel_get_must_exist(GntFileSel *sel)
{
	GntFileSelPrivate *priv = NULL;

	g_return_val_if_fail(GNT_IS_FILE_SEL(sel), FALSE);
	priv = gnt_file_sel_get_instance_private(sel);

	return priv->must_exist;
}

void gnt_file_sel_set_multi_select(GntFileSel *sel, gboolean set)
{
	GntFileSelPrivate *priv = NULL;

	g_return_if_fail(GNT_IS_FILE_SEL(sel));
	priv = gnt_file_sel_get_instance_private(sel);

	priv->multiselect = set;
}

GList *gnt_file_sel_get_selected_multi_files(GntFileSel *sel)
{
	GntFileSelPrivate *priv = NULL;
	GList *list = NULL, *iter;
	char *str = NULL;

	g_return_val_if_fail(GNT_IS_FILE_SEL(sel), NULL);
	priv = gnt_file_sel_get_instance_private(sel);

	str = gnt_file_sel_get_selected_file(sel);

	for (iter = priv->tags; iter; iter = iter->next) {
		list = g_list_prepend(list, g_strdup(iter->data));
		if (g_utf8_collate(str, iter->data)) {
			g_free(str);
			str = NULL;
		}
	}
	if (str)
		list = g_list_prepend(list, str);
	list = g_list_reverse(list);
	return list;
}

void gnt_file_sel_set_read_fn(GntFileSel *sel, gboolean (*read_fn)(const char *path, GList **files, GError **error))
{
	GntFileSelPrivate *priv = NULL;

	g_return_if_fail(GNT_IS_FILE_SEL(sel));
	priv = gnt_file_sel_get_instance_private(sel);

	priv->read_fn = read_fn;
}

/**************************************************************************
 * GntFile GBoxed API
 **************************************************************************/
static GntFile *
gnt_file_copy(GntFile *file)
{
	GntFile *file_new;

	g_return_val_if_fail(file != NULL, NULL);

	file_new = g_new(GntFile, 1);
	*file_new = *file;

	file_new->fullpath = g_strdup(file->fullpath);
	file_new->basename = g_strdup(file->basename);

	return file_new;
}

GType
gnt_file_get_type(void)
{
	static GType type = 0;

	if (type == 0) {
		type = g_boxed_type_register_static("GntFile",
				(GBoxedCopyFunc)gnt_file_copy,
				(GBoxedFreeFunc)gnt_file_free);
	}

	return type;
}

mercurial