medit/moo/mooedit/moofileview.c
2005-08-12 05:05:14 +00:00

3558 lines
117 KiB
C

/*
* mooedit/moofileview.c
*
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* 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 2 of the License, or
* (at your option) any later version.
*
* See COPYING file that comes with this distribution.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef GTK_DISABLE_DEPRECATED
# undef GTK_DISABLE_DEPRECATED
# include <gtk/gtktoolbar.h>
# define GTK_DISABLE_DEPRECATED
#endif
#include "mooedit/moofileview.h"
#include "mooedit/mooeditfilemgr.h"
#include "mooedit/moofilesystem.h"
#include "mooutils/moomarshals.h"
#include "mooutils/mooiconview.h"
#include "mooutils/moosignal.h"
#include <gdk/gdkkeysyms.h>
#include <glib/gstdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#ifdef USE_XDGMIME
#include "mooedit/xdgmime/xdgmime.h"
#endif
#define TIMEOUT 0.1
enum {
TREEVIEW_PAGE = 0,
ICONVIEW_PAGE = 1
};
typedef struct _History History;
typedef struct _Completion Completion;
struct _MooFileViewPrivate {
GtkListStore *store;
GHashTable *files; /* File* -> GtkTreeRowReference* */
gboolean need_to_clear; /* next add_file_in_idle will clear list store */
gboolean process_changes_now; /* first bunch of files in a dir, should be
put into liststore immediately */
GHashTable *files_to_add; /* File* */
GHashTable *files_to_remove;
GHashTable *files_to_change;
guint add_files_idle;
guint remove_files_idle;
guint change_files_idle;
GtkTreeModel *filter_model;
MooFolder *current_dir;
MooFileSystem *file_system;
GtkIconSize icon_size;
GtkNotebook *notebook;
MooFileViewType view_type;
GtkTreeView *treeview;
GtkTreeViewColumn *tree_name_column;
MooIconView *iconview;
gboolean show_hidden_files;
gboolean show_two_dots;
History *history;
gboolean select_first;
char *name_to_select;
char *temp_visible; /* tempporary visible name, for interactive search */
GtkTreeRowReference *temp_visible_row; /* row containing temp_visible (in the store) */
MooEditFileMgr *mgr;
GtkToggleButton *filter_button;
GtkComboBox *filter_combo;
GtkEntry *filter_entry;
GtkFileFilter *current_filter;
gboolean use_current_filter;
GtkEntry *search_entry;
gboolean in_search;
Completion *completion;
};
static void moo_file_view_finalize (GObject *object);
static void moo_file_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void moo_file_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean moo_file_view_key_press (GtkWidget *widget,
GdkEventKey *event,
MooFileView *fileview);
static void moo_file_view_set_file_mgr (MooFileView *fileview,
MooEditFileMgr *mgr);
static void moo_file_view_set_current_dir (MooFileView *fileview,
MooFolder *folder);
static gboolean moo_file_view_chdir_real (MooFileView *fileview,
const char *dir,
GError **error);
static void moo_file_view_go_up (MooFileView *fileview);
static void moo_file_view_go_home (MooFileView *fileview);
static void moo_file_view_go_back (MooFileView *fileview);
static void moo_file_view_go_forward(MooFileView *fileview);
static void toggle_show_hidden (MooFileView *fileview);
static void history_init (MooFileView *fileview);
static void history_free (MooFileView *fileview);
static void history_clear (MooFileView *fileview);
static void history_goto (MooFileView *fileview,
const char *dirname);
static const char *history_go (MooFileView *fileview,
GtkDirectionType where);
static void history_revert_go (MooFileView *fileview);
static gboolean filter_visible_func (GtkTreeModel *model,
GtkTreeIter *iter,
MooFileView *fileview);
static int tree_compare_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b);
static gboolean moo_file_view_check_visible (MooFileView *fileview,
MooFile *file,
gboolean ignore_hidden,
gboolean ignore_two_dots);
static void folder_deleted (MooFolder *folder,
MooFileView *fileview);
static void files_added (MooFolder *folder,
GSList *files,
MooFileView *fileview);
static void files_changed (MooFolder *folder,
GSList *files,
MooFileView *fileview);
static void files_removed (MooFolder *folder,
GSList *files,
MooFileView *fileview);
static void connect_folder (MooFileView *fileview);
static void disconnect_folder (MooFileView *fileview);
static void fileview_add_files (MooFileView *fileview,
GSList *files);
static void fileview_remove_files (MooFileView *fileview,
GSList *files);
static void fileview_change_files (MooFileView *fileview,
GSList *files);
static void init_gui (MooFileView *fileview);
static void focus_to_file_view (MooFileView *fileview);
static void focus_to_filter_entry (MooFileView *fileview);
static GtkWidget *create_toolbar (MooFileView *fileview);
static GtkWidget *create_notebook (MooFileView *fileview);
static GtkWidget *create_popup_menu (MooFileView *fileview);
static void goto_item_activated (GtkWidget *widget,
MooFileView *fileview);
static void show_hidden_toggled (GtkWidget *widget,
MooFileView *fileview);
static void show_two_dots_toggled (GtkWidget *widget,
MooFileView *fileview);
static void view_type_item_toggled (GtkWidget *widget,
MooFileView *fileview);
static GtkWidget *create_filter_combo (MooFileView *fileview);
static void init_filter_combo (MooFileView *fileview);
static void filter_button_toggled (MooFileView *fileview);
static void filter_combo_changed (MooFileView *fileview);
static void filter_entry_activate (MooFileView *fileview);
static void fileview_set_filter (MooFileView *fileview,
GtkFileFilter *filter);
static void fileview_set_use_filter (MooFileView *fileview,
gboolean use,
gboolean block_signals);
static GtkWidget *create_treeview (MooFileView *fileview);
static void tree_row_activated (GtkTreeView *treeview,
GtkTreePath *treepath,
GtkTreeViewColumn *column,
MooFileView *fileview);
static gboolean tree_button_press (GtkTreeView *treeview,
GdkEventButton *event,
MooFileView *fileview);
static GtkWidget *create_iconview (MooFileView *fileview);
static void icon_item_activated (MooIconView *iconview,
GtkTreePath *treepath,
MooFileView *fileview);
static gboolean icon_button_press (MooIconView *iconview,
GdkEventButton *event,
MooFileView *fileview);
static GtkWidget *get_file_view_widget (MooFileView *fileview);
static void completion_init (MooFileView *fileview);
static void completion_free (MooFileView *fileview);
/* MOO_TYPE_FILE_VIEW */
G_DEFINE_TYPE (MooFileView, moo_file_view, GTK_TYPE_VBOX)
enum {
PROP_0,
PROP_CURRENT_DIRECTORY,
PROP_FILE_MGR
};
enum {
COLUMN_FILE = 0,
COLUMN_PIXBUF = 1,
COLUMN_DISPLAY_NAME = 2,
COLUMN_SIZE = 3,
COLUMN_DATE = 4,
COLUMN_COLOR = 5,
COLUMN_STYLE = 6,
COLUMN_STYLE_SET = 7,
NUM_COLUMNS
};
enum {
CHDIR,
ACTIVATE,
POPULATE_POPUP,
GO_UP,
GO_BACK,
GO_FORWARD,
GO_HOME,
FOCUS_TO_FILTER_ENTRY,
FOCUS_TO_FILE_VIEW,
TOGGLE_SHOW_HIDDEN,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void moo_file_view_class_init (MooFileViewClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
// GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkBindingSet *binding_set;
gobject_class->finalize = moo_file_view_finalize;
gobject_class->set_property = moo_file_view_set_property;
gobject_class->get_property = moo_file_view_get_property;
klass->chdir = moo_file_view_chdir_real;
g_object_class_install_property (gobject_class,
PROP_CURRENT_DIRECTORY,
g_param_spec_string ("current-directory",
"current-directory",
"current-directory",
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_FILE_MGR,
g_param_spec_object ("file-mgr",
"file-mgr",
"file-mgr",
MOO_TYPE_EDIT_FILE_MGR,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
signals[CHDIR] =
g_signal_new ("chdir",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (MooFileViewClass, chdir),
NULL, NULL,
_moo_marshal_BOOLEAN__STRING_POINTER,
G_TYPE_BOOLEAN, 2,
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
G_TYPE_POINTER);
signals[ACTIVATE] =
moo_signal_new_cb ("activate",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
NULL,
NULL, NULL,
_moo_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
signals[POPULATE_POPUP] =
moo_signal_new_cb ("populate-popup",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
NULL,
NULL, NULL,
_moo_marshal_VOID__STRING_OBJECT,
G_TYPE_NONE, 2,
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
GTK_TYPE_MENU);
signals[GO_UP] =
moo_signal_new_cb ("go-up",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (moo_file_view_go_up),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[GO_FORWARD] =
moo_signal_new_cb ("go-forward",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (moo_file_view_go_forward),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[GO_BACK] =
moo_signal_new_cb ("go-back",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (moo_file_view_go_back),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[GO_HOME] =
moo_signal_new_cb ("go-home",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (moo_file_view_go_home),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[FOCUS_TO_FILTER_ENTRY] =
moo_signal_new_cb ("focus-to-filter-entry",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (focus_to_filter_entry),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[FOCUS_TO_FILE_VIEW] =
moo_signal_new_cb ("focus-to-file-view",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (focus_to_file_view),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[TOGGLE_SHOW_HIDDEN] =
moo_signal_new_cb ("toggle-show-hidden",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (toggle_show_hidden),
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set,
GDK_Up, GDK_MOD1_MASK,
"go-up",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Up, GDK_MOD1_MASK,
"go-up",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_Left, GDK_MOD1_MASK,
"go-back",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Left, GDK_MOD1_MASK,
"go-back",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_Right, GDK_MOD1_MASK,
"go-forward",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Right, GDK_MOD1_MASK,
"go-forward",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_Home, GDK_MOD1_MASK,
"go-home",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_KP_Home, GDK_MOD1_MASK,
"go-home",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_f, GDK_MOD1_MASK | GDK_SHIFT_MASK,
"focus-to-filter-entry",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_b, GDK_MOD1_MASK | GDK_SHIFT_MASK,
"focus-to-file-view",
0);
gtk_binding_entry_add_signal (binding_set,
GDK_h, GDK_MOD1_MASK | GDK_SHIFT_MASK,
"toggle-show-hidden",
0);
}
static void moo_file_view_init (MooFileView *fileview)
{
fileview->priv = g_new0 (MooFileViewPrivate, 1);
fileview->priv->show_hidden_files = FALSE;
fileview->priv->view_type = MOO_FILE_VIEW_ICON;
fileview->priv->use_current_filter = FALSE;
history_init (fileview);
fileview->priv->store =
gtk_list_store_new (NUM_COLUMNS,
MOO_TYPE_FILE, /* COLUMN_FILE */
GDK_TYPE_PIXBUF, /* COLUMN_PIXBUF */
G_TYPE_STRING, /* COLUMN_DISPLAY_NAME */
G_TYPE_STRING, /* COLUMN_SIZE */
G_TYPE_STRING, /* COLUMN_DATE */
G_TYPE_STRING, /* COLUMN_COLOR */
PANGO_TYPE_STYLE, /* COLUMN_STYLE */
G_TYPE_BOOLEAN); /* COLUMN_STYLE_SET */
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (fileview->priv->store),
COLUMN_FILE,
(GtkTreeIterCompareFunc) tree_compare_func,
fileview, NULL);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (fileview->priv->store),
COLUMN_FILE, GTK_SORT_ASCENDING);
fileview->priv->filter_model =
gtk_tree_model_filter_new (GTK_TREE_MODEL (fileview->priv->store),
NULL);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (fileview->priv->filter_model),
(GtkTreeModelFilterVisibleFunc) filter_visible_func,
fileview, NULL);
init_gui (fileview);
completion_init (fileview);
fileview->priv->name_to_select = NULL;
fileview->priv->current_dir = NULL;
fileview->priv->file_system = moo_file_system_create ();
fileview->priv->icon_size = GTK_ICON_SIZE_MENU;
}
static void moo_file_view_finalize (GObject *object)
{
MooFileView *fileview = MOO_FILE_VIEW (object);
if (fileview->priv->files)
g_hash_table_destroy (fileview->priv->files);
g_object_unref (fileview->priv->filter_model);
g_object_unref (fileview->priv->store);
history_free (fileview);
g_free (fileview->priv->name_to_select);
fileview->priv->name_to_select = NULL;
if (fileview->priv->mgr)
g_object_unref (fileview->priv->mgr);
if (fileview->priv->current_filter)
g_object_unref (fileview->priv->current_filter);
g_free (fileview->priv->temp_visible);
gtk_tree_row_reference_free (fileview->priv->temp_visible_row);
disconnect_folder (fileview);
g_object_unref (fileview->priv->file_system);
completion_free (fileview);
g_free (fileview->priv);
G_OBJECT_CLASS (moo_file_view_parent_class)->finalize (object);
}
GtkWidget *moo_file_view_new (void)
{
return GTK_WIDGET (g_object_new (MOO_TYPE_FILE_VIEW, NULL));
}
static void connect_folder (MooFileView *fileview)
{
g_return_if_fail (fileview->priv->current_dir != NULL);
g_signal_connect (fileview->priv->current_dir, "files-added",
G_CALLBACK (files_added), fileview);
g_signal_connect (fileview->priv->current_dir, "files-changed",
G_CALLBACK (files_changed), fileview);
g_signal_connect (fileview->priv->current_dir, "files-removed",
G_CALLBACK (files_removed), fileview);
g_signal_connect (fileview->priv->current_dir, "deleted",
G_CALLBACK (folder_deleted), fileview);
fileview->priv->files_to_add =
g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) moo_file_unref,
NULL);
fileview->priv->files_to_remove =
g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) moo_file_unref,
NULL);
fileview->priv->files_to_change =
g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) moo_file_unref,
NULL);
}
static void disconnect_folder (MooFileView *fileview)
{
if (fileview->priv->current_dir)
{
g_signal_handlers_disconnect_by_func (fileview->priv->current_dir,
(gpointer)files_added,
fileview);
g_signal_handlers_disconnect_by_func (fileview->priv->current_dir,
(gpointer)files_changed,
fileview);
g_signal_handlers_disconnect_by_func (fileview->priv->current_dir,
(gpointer)files_removed,
fileview);
g_signal_handlers_disconnect_by_func (fileview->priv->current_dir,
(gpointer)folder_deleted,
fileview);
}
if (fileview->priv->files_to_add)
g_hash_table_destroy (fileview->priv->files_to_add);
fileview->priv->files_to_add = NULL;
if (fileview->priv->add_files_idle)
g_source_remove (fileview->priv->add_files_idle);
fileview->priv->add_files_idle = 0;
if (fileview->priv->files_to_remove)
g_hash_table_destroy (fileview->priv->files_to_remove);
fileview->priv->files_to_remove = NULL;
if (fileview->priv->remove_files_idle)
g_source_remove (fileview->priv->remove_files_idle);
fileview->priv->remove_files_idle = 0;
if (fileview->priv->files_to_change)
g_hash_table_destroy (fileview->priv->files_to_change);
fileview->priv->files_to_change = NULL;
if (fileview->priv->change_files_idle)
g_source_remove (fileview->priv->change_files_idle);
fileview->priv->change_files_idle = 0;
}
static gboolean clear_list_in_idle (MooFileView *fileview)
{
if (fileview->priv->need_to_clear)
gtk_list_store_clear (fileview->priv->store);
return FALSE;
}
static void moo_file_view_set_current_dir (MooFileView *fileview,
MooFolder *folder)
{
GSList *files;
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
g_return_if_fail (!folder || MOO_IS_FOLDER (folder));
if (folder == fileview->priv->current_dir)
return;
if (!folder)
{
if (fileview->priv->current_dir)
{
disconnect_folder (fileview);
g_object_unref (fileview->priv->current_dir);
fileview->priv->current_dir = NULL;
history_clear (fileview);
gtk_list_store_clear (fileview->priv->store);
}
return;
}
disconnect_folder (fileview);
if (fileview->priv->current_dir)
g_object_unref (fileview->priv->current_dir);
fileview->priv->current_dir = g_object_ref (folder);
history_goto (fileview, moo_folder_get_path (folder));
fileview->priv->need_to_clear = TRUE;
g_idle_add_full (G_PRIORITY_LOW,
(GSourceFunc) clear_list_in_idle,
fileview, NULL);
fileview->priv->process_changes_now = TRUE;
if (fileview->priv->files)
g_hash_table_destroy (fileview->priv->files);
fileview->priv->files =
g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) moo_file_unref,
(GDestroyNotify) gtk_tree_row_reference_free);
connect_folder (fileview);
fileview->priv->select_first = TRUE;
files = moo_folder_list_files (folder);
fileview_add_files (fileview, files);
g_slist_foreach (files, (GFunc) moo_file_unref, NULL);
g_slist_free (files);
}
static gboolean moo_file_view_chdir_real(MooFileView *fileview,
const char *new_dir,
GError **error)
{
char *real_new_dir;
MooFolder *folder;
g_return_val_if_fail (MOO_IS_FILE_VIEW (fileview), FALSE);
if (!new_dir)
{
moo_file_view_set_current_dir (fileview, NULL);
return TRUE;
}
if (g_path_is_absolute (new_dir) || !fileview->priv->current_dir)
{
real_new_dir = g_strdup (new_dir);
}
else
{
real_new_dir = g_build_filename (moo_folder_get_path (fileview->priv->current_dir),
new_dir, NULL);
}
folder = moo_file_system_get_folder (fileview->priv->file_system,
real_new_dir,
MOO_FILE_ALL_FLAGS,
error);
g_free (real_new_dir);
if (!folder)
return FALSE;
moo_file_view_set_current_dir (fileview, folder);
g_object_unref (folder);
return TRUE;
}
static void folder_deleted (MooFolder *folder,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->current_dir);
disconnect_folder (fileview);
gtk_list_store_clear (fileview->priv->store);
}
static void files_added (MooFolder *folder,
GSList *files,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->current_dir);
fileview_add_files (fileview, files);
}
static void files_changed (MooFolder *folder,
GSList *files,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->current_dir);
fileview_change_files (fileview, files);
}
static void files_removed (MooFolder *folder,
GSList *files,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->current_dir);
fileview_remove_files (fileview, files);
}
typedef void (*ForEachFileFunc) (MooFileView *fileview,
MooFile *file);
static gboolean do_timed_func (MooFile *file,
G_GNUC_UNUSED gpointer dummy,
gpointer user_data)
{
struct {
GTimer *timer;
gboolean stop;
MooFileView *fileview;
ForEachFileFunc func;
}* data = user_data;
if (data->stop)
return FALSE;
data->func (data->fileview, file);
if (g_timer_elapsed (data->timer, NULL) > TIMEOUT)
data->stop = TRUE;
return TRUE;
}
static void files_table_do_timed (GHashTable *files_table,
ForEachFileFunc func,
MooFileView *fileview)
{
struct {
GTimer *timer;
gboolean stop;
MooFileView *fileview;
ForEachFileFunc func;
} data;
data.timer = g_timer_new ();
data.stop = FALSE;
data.fileview = fileview;
data.func = func;
g_hash_table_foreach_remove (files_table,
(GHRFunc) do_timed_func,
&data);
g_timer_destroy (data.timer);
}
static void start_add (MooFileView *fileview);
static void start_change (MooFileView *fileview);
static void start_remove (MooFileView *fileview);
static void add_a_bit (MooFileView *fileview);
static void change_a_bit (MooFileView *fileview);
static void remove_a_bit (MooFileView *fileview);
static void add_one_file (MooFileView *fileview,
MooFile *file)
{
GtkTreeIter iter;
GtkTreePath *path;
GtkTreeRowReference *old;
g_assert (file != NULL);
old = g_hash_table_lookup (fileview->priv->files, file);
g_assert (old == NULL);
if (old && gtk_tree_row_reference_valid (old))
{
g_warning ("readding file");
path = gtk_tree_row_reference_get_path (old);
gtk_tree_model_get_iter (GTK_TREE_MODEL (fileview->priv->store),
&iter, path);
gtk_tree_path_free (path);
}
else
{
gtk_list_store_prepend (fileview->priv->store, &iter);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (fileview->priv->store),
&iter);
g_hash_table_insert (fileview->priv->files,
moo_file_ref (file),
gtk_tree_row_reference_new (GTK_TREE_MODEL (fileview->priv->store),
path));
gtk_tree_path_free (path);
}
gtk_list_store_set (fileview->priv->store, &iter,
COLUMN_FILE, file,
COLUMN_PIXBUF,
moo_file_get_icon (file,
GTK_WIDGET (fileview),
GTK_ICON_SIZE_MENU),
COLUMN_DISPLAY_NAME,
moo_file_get_display_basename (file),
COLUMN_SIZE, NULL,
COLUMN_DATE, NULL, -1);
if (fileview->priv->name_to_select)
{
if (!strcmp (fileview->priv->name_to_select,
moo_file_get_display_basename (file)))
{
g_free (fileview->priv->name_to_select);
fileview->priv->name_to_select = NULL;
moo_file_view_select_file (fileview,
moo_file_get_display_basename (file));
}
}
else if (fileview->priv->select_first)
{
fileview->priv->select_first = FALSE;
moo_file_view_select_file (fileview,
moo_file_get_display_basename (file));
}
}
static gboolean fileview_add_files_in_idle (MooFileView *fileview)
{
if (fileview->priv->need_to_clear)
{
gtk_list_store_clear (fileview->priv->store);
fileview->priv->need_to_clear = FALSE;
}
if (fileview->priv->remove_files_idle)
remove_a_bit (fileview);
else
add_a_bit (fileview);
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (fileview->priv->store),
COLUMN_FILE,
(GtkTreeIterCompareFunc) tree_compare_func,
fileview, NULL);
if (!g_hash_table_size (fileview->priv->files_to_add))
{
if (fileview->priv->add_files_idle)
g_source_remove (fileview->priv->add_files_idle);
fileview->priv->add_files_idle = 0;
return FALSE;
}
else
{
return TRUE;
}
}
static void fileview_add_files (MooFileView *fileview,
GSList *files)
{
GSList *l;
gboolean add = FALSE;
gboolean change = FALSE;
for (l = files; l != NULL; l = l->next)
{
MooFile *file = l->data;
g_assert (file != NULL);
if (g_hash_table_lookup (fileview->priv->files_to_remove, file))
{
g_assert (g_hash_table_lookup (fileview->priv->files, file));
g_assert (!g_hash_table_lookup (fileview->priv->files_to_change, file));
g_assert (!g_hash_table_lookup (fileview->priv->files_to_add, file));
g_hash_table_remove (fileview->priv->files_to_remove, file);
g_hash_table_insert (fileview->priv->files_to_change,
moo_file_ref (file), NULL);
change = TRUE;
}
else
{
g_assert (!g_hash_table_lookup (fileview->priv->files, file));
g_assert (!g_hash_table_lookup (fileview->priv->files_to_add, file));
g_assert (!g_hash_table_lookup (fileview->priv->files_to_change, file));
g_hash_table_insert (fileview->priv->files_to_add,
moo_file_ref (file), NULL);
add = TRUE;
}
}
if (add)
start_add (fileview);
if (change)
start_change (fileview);
}
static void remove_one_file (MooFileView *fileview,
MooFile *file)
{
GtkTreeIter iter;
GtkTreePath *path;
GtkTreeRowReference *ref;
g_assert (file != NULL);
ref = g_hash_table_lookup (fileview->priv->files, file);
g_assert (ref != NULL && gtk_tree_row_reference_valid (ref));
path = gtk_tree_row_reference_get_path (ref);
gtk_tree_model_get_iter (GTK_TREE_MODEL (fileview->priv->store), &iter, path);
gtk_list_store_remove (fileview->priv->store, &iter);
g_hash_table_remove (fileview->priv->files, file);
}
static gboolean fileview_remove_files_in_idle (MooFileView *fileview)
{
remove_a_bit (fileview);
if (!g_hash_table_size (fileview->priv->files_to_remove))
{
g_source_remove (fileview->priv->remove_files_idle);
fileview->priv->remove_files_idle = 0;
return FALSE;
}
else
{
return TRUE;
}
}
static void fileview_remove_files (MooFileView *fileview,
GSList *files)
{
GSList *l;
gboolean remove = FALSE;
for (l = files; l != NULL; l = l->next)
{
MooFile *file = l->data;
g_assert (file != NULL);
g_hash_table_remove (fileview->priv->files_to_add, file);
g_hash_table_remove (fileview->priv->files_to_change, file);
if (g_hash_table_lookup (fileview->priv->files, file))
{
remove = TRUE;
g_hash_table_insert (fileview->priv->files_to_remove,
moo_file_ref (file), NULL);
}
}
if (remove)
start_remove (fileview);
}
static void change_one_file (MooFileView *fileview,
MooFile *file)
{
GtkTreeIter iter;
GtkTreePath *path;
GtkTreeRowReference *ref;
g_assert (file != NULL);
ref = g_hash_table_lookup (fileview->priv->files, file);
g_assert (ref != NULL);
g_assert (gtk_tree_row_reference_valid (ref));
path = gtk_tree_row_reference_get_path (ref);
gtk_tree_model_get_iter (GTK_TREE_MODEL (fileview->priv->store), &iter, path);
gtk_list_store_set (fileview->priv->store, &iter,
COLUMN_FILE, file,
COLUMN_PIXBUF,
moo_file_get_icon (file,
GTK_WIDGET (fileview),
GTK_ICON_SIZE_MENU),
COLUMN_DISPLAY_NAME,
moo_file_get_display_basename (file),
COLUMN_SIZE, NULL,
COLUMN_DATE, NULL, -1);
}
static gboolean fileview_change_files_in_idle (MooFileView *fileview)
{
if (fileview->priv->remove_files_idle)
remove_a_bit (fileview);
else if (fileview->priv->add_files_idle)
add_a_bit (fileview);
else
change_a_bit (fileview);
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (fileview->priv->store),
COLUMN_FILE,
(GtkTreeIterCompareFunc) tree_compare_func,
fileview, NULL);
if (!g_hash_table_size (fileview->priv->files_to_change))
{
if (fileview->priv->change_files_idle)
g_source_remove (fileview->priv->change_files_idle);
fileview->priv->change_files_idle = 0;
return FALSE;
}
else
{
return TRUE;
}
}
static void fileview_change_files (MooFileView *fileview,
GSList *files)
{
GSList *l;
gboolean add = FALSE;
gboolean change = FALSE;
for (l = files; l != NULL; l = l->next)
{
MooFile *file = l->data;
g_assert (file != NULL);
g_hash_table_remove (fileview->priv->files_to_remove, file);
if (!g_hash_table_lookup (fileview->priv->files, file))
{
g_hash_table_insert (fileview->priv->files_to_add,
moo_file_ref (file), NULL);
add = TRUE;
}
else
{
g_hash_table_insert (fileview->priv->files_to_change,
moo_file_ref (file), NULL);
change = TRUE;
}
}
if (add)
start_add (fileview);
if (change)
start_change (fileview);
}
static void start_add (MooFileView *fileview)
{
if (!fileview->priv->add_files_idle)
{
if (!fileview->priv->process_changes_now || fileview_add_files_in_idle (fileview))
fileview->priv->add_files_idle =
g_idle_add ((GSourceFunc) fileview_add_files_in_idle,
fileview);
else
start_change (fileview);
}
fileview->priv->process_changes_now = FALSE;
}
static void start_change (MooFileView *fileview)
{
if (!fileview->priv->change_files_idle)
{
if (!fileview->priv->process_changes_now || fileview_change_files_in_idle (fileview))
fileview->priv->change_files_idle =
g_idle_add ((GSourceFunc) fileview_change_files_in_idle,
fileview);
}
fileview->priv->process_changes_now = FALSE;
}
static void start_remove (MooFileView *fileview)
{
if (!fileview->priv->remove_files_idle)
fileview->priv->remove_files_idle =
g_idle_add ((GSourceFunc) fileview_remove_files_in_idle,
fileview);
}
static void remove_a_bit (MooFileView *fileview)
{
files_table_do_timed (fileview->priv->files_to_remove,
remove_one_file,
fileview);
}
static void add_a_bit (MooFileView *fileview)
{
files_table_do_timed (fileview->priv->files_to_add,
add_one_file,
fileview);
}
static void change_a_bit (MooFileView *fileview)
{
files_table_do_timed (fileview->priv->files_to_change,
change_one_file,
fileview);
}
static void init_gui (MooFileView *fileview)
{
GtkBox *box;
GtkWidget *toolbar, *notebook, *filter_combo, *search_entry;
box = GTK_BOX (fileview);
toolbar = create_toolbar (fileview);
gtk_widget_show (toolbar);
gtk_box_pack_start (box, toolbar, FALSE, FALSE, 0);
fileview->toolbar = toolbar;
notebook = create_notebook (fileview);
gtk_widget_show (notebook);
gtk_box_pack_start (box, notebook, TRUE, TRUE, 0);
fileview->priv->notebook = GTK_NOTEBOOK (notebook);
search_entry = gtk_entry_new ();
g_object_set_data (G_OBJECT (search_entry), "moo-file-view", fileview);
gtk_widget_set_no_show_all (search_entry, TRUE);
gtk_box_pack_start (box, search_entry, FALSE, FALSE, 0);
fileview->priv->search_entry = GTK_ENTRY (search_entry);
filter_combo = create_filter_combo (fileview);
gtk_widget_show (filter_combo);
gtk_box_pack_start (box, filter_combo, FALSE, FALSE, 0);
if (fileview->priv->view_type == MOO_FILE_VIEW_ICON)
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
ICONVIEW_PAGE);
else
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
TREEVIEW_PAGE);
focus_to_file_view (fileview);
}
static void focus_to_file_view (MooFileView *fileview)
{
gtk_widget_grab_focus (get_file_view_widget (fileview));
}
static void focus_to_filter_entry (MooFileView *fileview)
{
gtk_widget_grab_focus (GTK_WIDGET(fileview->priv->filter_entry));
}
void moo_file_view_set_view_type (MooFileView *fileview,
MooFileViewType type)
{
MooIconView *iconview;
GtkTreeView *treeview;
GtkTreeSelection *selection;
GtkTreePath *path;
GtkTreeIter iter;
g_return_if_fail (type == MOO_FILE_VIEW_ICON ||
type == MOO_FILE_VIEW_LIST);
if (fileview->priv->view_type == type)
return;
iconview = fileview->priv->iconview;
treeview = fileview->priv->treeview;
selection = gtk_tree_view_get_selection (treeview);
fileview->priv->view_type = type;
if (type == MOO_FILE_VIEW_LIST)
{
path = moo_icon_view_get_selected (iconview);
gtk_tree_selection_unselect_all (selection);
if (path)
{
gtk_tree_selection_select_path (selection, path);
gtk_tree_path_free (path);
}
}
else
{
GtkTreeModel *model = NULL;
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
path = gtk_tree_model_get_path (model, &iter);
moo_icon_view_select_path (iconview, path);
gtk_tree_path_free (path);
}
else
{
moo_icon_view_select_path (iconview, NULL);
}
}
if (type == MOO_FILE_VIEW_ICON)
gtk_notebook_set_current_page (fileview->priv->notebook,
ICONVIEW_PAGE);
else
gtk_notebook_set_current_page (fileview->priv->notebook,
TREEVIEW_PAGE);
}
static void create_button (MooFileView *fileview,
GtkWidget *box,
GtkTooltips *tooltips,
const char *stock_id,
const char *tip,
const char *signal)
{
GtkWidget *icon, *button;
icon = gtk_image_new_from_stock (stock_id,
GTK_ICON_SIZE_MENU);
gtk_widget_show (GTK_WIDGET (icon));
button = gtk_button_new ();
gtk_container_add (GTK_CONTAINER (button), icon);
gtk_widget_show (button);
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
gtk_tooltips_set_tip (tooltips, button, tip, tip);
gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
g_object_set_data (G_OBJECT (button), "moo-file-view-signal",
(gpointer) signal);
g_signal_connect (button, "clicked",
G_CALLBACK (goto_item_activated),
fileview);
}
static GtkWidget *create_toolbar (MooFileView *fileview)
{
GtkWidget *toolbar;
GtkTooltips *tooltips;
tooltips = gtk_tooltips_new ();
toolbar = gtk_hbox_new (FALSE, 0);
create_button (fileview, toolbar, tooltips,
GTK_STOCK_GO_UP, "Up", "go-up");
create_button (fileview, toolbar, tooltips,
GTK_STOCK_GO_BACK, "Back", "go-back");
create_button (fileview, toolbar, tooltips,
GTK_STOCK_GO_FORWARD, "Forward", "go-forward");
create_button (fileview, toolbar, tooltips,
GTK_STOCK_HOME, "Home", "go-home");
return toolbar;
}
static void goto_item_activated (GtkWidget *widget,
MooFileView *fileview)
{
const char *signal = g_object_get_data (G_OBJECT (widget),
"moo-file-view-signal");
g_return_if_fail (signal != NULL);
g_signal_emit_by_name (fileview, signal);
}
static GtkWidget *create_notebook (MooFileView *fileview)
{
GtkWidget *notebook, *swin, *treeview, *iconview;
notebook = gtk_notebook_new ();
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
swin = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (swin);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), swin, NULL);
treeview = create_treeview (fileview);
gtk_widget_show (treeview);
gtk_container_add (GTK_CONTAINER (swin), treeview);
fileview->priv->treeview = GTK_TREE_VIEW (treeview);
g_signal_connect (treeview, "key-press-event",
G_CALLBACK (moo_file_view_key_press),
fileview);
swin = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (swin);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
GTK_POLICY_ALWAYS,
GTK_POLICY_AUTOMATIC);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), swin, NULL);
iconview = create_iconview (fileview);
gtk_widget_show (iconview);
gtk_container_add (GTK_CONTAINER (swin), iconview);
fileview->priv->iconview = MOO_ICON_VIEW (iconview);
return notebook;
}
static GtkWidget *create_filter_combo (G_GNUC_UNUSED MooFileView *fileview)
{
GtkWidget *hbox, *button, *combo;
hbox = gtk_hbox_new (FALSE, 0);
button = gtk_toggle_button_new_with_label ("Filter");
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
combo = gtk_combo_box_entry_new ();
gtk_widget_show (combo);
gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
fileview->priv->filter_button = GTK_TOGGLE_BUTTON (button);
fileview->priv->filter_combo = GTK_COMBO_BOX (combo);
fileview->priv->filter_entry = GTK_ENTRY (GTK_BIN(combo)->child);
g_signal_connect_swapped (button, "toggled",
G_CALLBACK (filter_button_toggled),
fileview);
g_signal_connect_data (combo, "changed",
G_CALLBACK (filter_combo_changed),
fileview, NULL,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_swapped (GTK_BIN(combo)->child, "activate",
G_CALLBACK (filter_entry_activate),
fileview);
return hbox;
}
static GtkWidget *create_treeview (MooFileView *fileview)
{
GtkWidget *treeview;
GtkTreeViewColumn *column;
GtkCellRenderer *cell;
treeview = gtk_tree_view_new_with_model (fileview->priv->filter_model);
// gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview), COLUMN_DISPLAY_NAME);
g_signal_connect (treeview, "row-activated",
G_CALLBACK (tree_row_activated), fileview);
g_signal_connect (treeview, "button-press-event",
G_CALLBACK (tree_button_press), fileview);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "Name");
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
fileview->priv->tree_name_column = column;
cell = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"pixbuf", COLUMN_PIXBUF,
NULL);
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, TRUE);
gtk_tree_view_column_set_attributes (column, cell,
"text", COLUMN_DISPLAY_NAME,
"style", COLUMN_STYLE,
"style-set", COLUMN_STYLE_SET,
NULL);
#if 1
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "Size");
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"text", COLUMN_SIZE,
"style", COLUMN_STYLE,
"style-set", COLUMN_STYLE_SET,
NULL);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, "Date");
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
cell = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, cell, FALSE);
gtk_tree_view_column_set_attributes (column, cell,
"text", COLUMN_DATE,
"style", COLUMN_STYLE,
"style-set", COLUMN_STYLE_SET,
NULL);
#endif
return treeview;
}
static GtkWidget *create_iconview (MooFileView *fileview)
{
GtkWidget *iconview;
GtkCellRenderer *cell;
iconview = moo_icon_view_new_with_model (fileview->priv->filter_model);
g_signal_connect (iconview, "key-press-event",
G_CALLBACK (moo_file_view_key_press),
fileview);
moo_icon_view_set_attributes (MOO_ICON_VIEW (iconview),
MOO_ICON_VIEW_CELL_PIXBUF,
"pixbuf", COLUMN_PIXBUF,
NULL);
moo_icon_view_set_attributes (MOO_ICON_VIEW (iconview),
MOO_ICON_VIEW_CELL_TEXT,
"text", COLUMN_DISPLAY_NAME,
"style", COLUMN_STYLE,
"style-set", COLUMN_STYLE_SET,
NULL);
cell = moo_icon_view_get_cell (MOO_ICON_VIEW (iconview),
MOO_ICON_VIEW_CELL_TEXT);
g_object_set (cell, "xalign", 0.0, NULL);
cell = moo_icon_view_get_cell (MOO_ICON_VIEW (iconview),
MOO_ICON_VIEW_CELL_PIXBUF);
g_object_set (cell, "xpad", 1, "ypad", 1, NULL);
g_signal_connect (iconview, "item-activated",
G_CALLBACK (icon_item_activated), fileview);
g_signal_connect (iconview, "button-press-event",
G_CALLBACK (icon_button_press), fileview);
return iconview;
}
static gboolean moo_file_view_check_visible (MooFileView *fileview,
MooFile *file,
gboolean ignore_hidden,
gboolean ignore_two_dots)
{
if (!file)
return FALSE;
if (!strcmp (moo_file_get_basename (file), ".."))
{
if (!ignore_two_dots)
return fileview->priv->show_two_dots;
else
return FALSE;
}
if (moo_file_test (file, MOO_FILE_IS_HIDDEN))
{
if (!ignore_hidden)
return fileview->priv->show_hidden_files;
else
return TRUE;
}
if (moo_file_test (file, MOO_FILE_IS_FOLDER))
return TRUE;
if (fileview->priv->current_filter && fileview->priv->use_current_filter)
{
GtkFileFilterInfo filter_info;
GtkFileFilter *filter = fileview->priv->current_filter;
filter_info.contains = gtk_file_filter_get_needed (filter);
filter_info.filename = moo_file_get_basename (file);
filter_info.uri = NULL;
filter_info.display_name = moo_file_get_display_basename (file);
filter_info.mime_type = moo_file_get_mime_type (file);
return gtk_file_filter_filter (fileview->priv->current_filter,
&filter_info);
}
return TRUE;
}
static gboolean filter_visible_func (GtkTreeModel *model,
GtkTreeIter *iter,
MooFileView *fileview)
{
MooFile *file;
gboolean visible = TRUE;
gtk_tree_model_get (model, iter, COLUMN_FILE, &file, -1);
if (file && fileview->priv->temp_visible &&
!strcmp (moo_file_get_basename (file), fileview->priv->temp_visible))
{
#if 1
GtkTreePath *tmp = NULL, *tmp2 = NULL;
g_assert (fileview->priv->temp_visible_row != NULL);
g_assert (gtk_tree_row_reference_valid (fileview->priv->temp_visible_row));
tmp = gtk_tree_row_reference_get_path (fileview->priv->temp_visible_row);
tmp2 = gtk_tree_model_get_path (model, iter);
g_assert (!gtk_tree_path_compare (tmp, tmp2));
gtk_tree_path_free (tmp);
gtk_tree_path_free (tmp2);
#endif
visible = TRUE;
}
else
{
visible = moo_file_view_check_visible (fileview, file, FALSE, FALSE);
}
moo_file_unref (file);
return visible;
}
static int tree_compare_func (GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b)
{
MooFile *f1, *f2;
gboolean is_dir1, is_dir2;
gboolean result = 0;
gtk_tree_model_get (model, a, COLUMN_FILE, &f1, -1);
gtk_tree_model_get (model, b, COLUMN_FILE, &f2, -1);
g_assert (f1 != NULL && f2 != NULL);
if (f1 == f2)
{
result = 0;
goto out;
}
if (!f1 || !f2)
{
if (f1 < f2)
result = -1;
else
result = 1;
goto out;
}
is_dir1 = moo_file_test (f1, MOO_FILE_IS_FOLDER);
is_dir2 = moo_file_test (f2, MOO_FILE_IS_FOLDER);
if (is_dir1 != is_dir2)
{
if (is_dir1 && !is_dir2)
result = -1;
else
result = 1;
goto out;
}
if (is_dir1)
{
if (!strcmp (moo_file_get_basename (f1), ".."))
result = -1;
else if (!strcmp (moo_file_get_basename (f2), ".."))
result = 1;
else
result = strcmp (moo_file_get_basename (f1),
moo_file_get_basename (f2));
goto out;
}
result = strcmp (moo_file_get_basename (f1),
moo_file_get_basename (f2));
out:
moo_file_unref (f1);
moo_file_unref (f2);
return result;
}
gboolean moo_file_view_chdir (MooFileView *fileview,
const char *dir,
GError **error)
{
gboolean result;
g_return_val_if_fail (MOO_IS_FILE_VIEW (fileview), FALSE);
g_signal_emit (fileview, signals[CHDIR], 0, dir, error, &result);
return result;
}
static void moo_file_view_go_up (MooFileView *fileview)
{
MooFolder *parent;
char *name;
g_return_if_fail (fileview->priv->current_dir != NULL);
parent = moo_folder_get_parent (fileview->priv->current_dir,
MOO_FILE_HAS_ICON);
g_print ("current dir: '%s'\nparent dir: '%s'\n",
moo_folder_get_path (fileview->priv->current_dir),
moo_folder_get_path (parent));
if (parent != fileview->priv->current_dir)
{
name = g_filename_display_basename (
moo_folder_get_path (fileview->priv->current_dir));
moo_file_view_set_current_dir (fileview, parent);
moo_file_view_select_file (fileview, name);
g_free (name);
}
g_object_unref (parent);
}
static void moo_file_view_go_home (MooFileView *fileview)
{
const char *dir;
GError *error = NULL;
dir = g_get_home_dir ();
if (!moo_file_view_chdir (fileview, dir, &error))
{
g_warning ("%s: could not go up", G_STRLOC);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
}
}
static void tree_path_activated (MooFileView *fileview,
GtkTreePath *filter_path)
{
MooFile *file = NULL;
GtkTreeIter iter;
if (!gtk_tree_model_get_iter (fileview->priv->filter_model, &iter, filter_path))
{
g_return_if_reached ();
}
gtk_tree_model_get (fileview->priv->filter_model,
&iter, COLUMN_FILE, &file, -1);
g_return_if_fail (file != NULL);
if (!strcmp (moo_file_get_basename (file), ".."))
{
g_signal_emit_by_name (fileview, "go-up");
}
else if (moo_file_test (file, MOO_FILE_IS_FOLDER))
{
GError *error = NULL;
if (!moo_file_view_chdir (fileview, moo_file_get_basename (file), &error))
{
g_warning ("%s: could not go into '%s'",
G_STRLOC, moo_file_get_basename (file));
if (error)
{
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
}
}
else
{
char *path;
g_assert (fileview->priv->current_dir != NULL);
path = g_build_filename (moo_folder_get_path (fileview->priv->current_dir),
moo_file_get_basename (file), NULL);
g_signal_emit (fileview, signals[ACTIVATE], 0, path);
g_free (path);
}
moo_file_unref (file);
}
static void tree_row_activated (G_GNUC_UNUSED GtkTreeView *treeview,
GtkTreePath *filter_treepath,
G_GNUC_UNUSED GtkTreeViewColumn *column,
MooFileView *fileview)
{
tree_path_activated (fileview, filter_treepath);
}
static void icon_item_activated (G_GNUC_UNUSED MooIconView *iconview,
GtkTreePath *filter_treepath,
MooFileView *fileview)
{
tree_path_activated (fileview, filter_treepath);
}
static void moo_file_view_go (MooFileView *fileview,
GtkDirectionType where)
{
const char *dir;
GError *error = NULL;
dir = history_go (fileview, where);
if (dir)
{
if (!moo_file_view_chdir (fileview, dir, &error))
{
g_warning ("%s: could not go into '%s'",
G_STRLOC, dir);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
history_revert_go (fileview);
}
}
}
static void moo_file_view_go_back (MooFileView *fileview)
{
moo_file_view_go (fileview, GTK_DIR_LEFT);
}
static void moo_file_view_go_forward(MooFileView *fileview)
{
moo_file_view_go (fileview, GTK_DIR_RIGHT);
}
struct _History {
gboolean done;
GtkDirectionType direction;
GList *list;
GList *current;
};
static void history_init (MooFileView *fileview)
{
fileview->priv->history = g_new0 (History, 1);
}
static void history_free (MooFileView *fileview)
{
g_list_foreach (fileview->priv->history->list, (GFunc) g_free, NULL);
g_list_free (fileview->priv->history->list);
g_free (fileview->priv->history);
fileview->priv->history = NULL;
}
static void history_clear (MooFileView *fileview)
{
g_list_foreach (fileview->priv->history->list, (GFunc) g_free, NULL);
g_list_free (fileview->priv->history->list);
fileview->priv->history->current = NULL;
fileview->priv->history->list = NULL;
}
static const char *history_go (MooFileView *fileview,
GtkDirectionType where)
{
History *hist = fileview->priv->history;
const char *dir;
g_assert (where == GTK_DIR_LEFT || where == GTK_DIR_RIGHT);
if (!hist->current)
return NULL;
if (where == GTK_DIR_RIGHT)
{
if (!hist->current->next)
return NULL;
dir = hist->current->next->data;
hist->current = hist->current->next;
}
else
{
if (!hist->current->prev)
return NULL;
dir = hist->current->prev->data;
hist->current = hist->current->prev;
}
hist->done = TRUE;
hist->direction = where;
return dir;
}
static void history_revert_go (MooFileView *fileview)
{
History *hist = fileview->priv->history;
g_assert (hist->done);
if (hist->direction == GTK_DIR_LEFT)
{
g_assert (hist->current && hist->current->next);
hist->current = hist->current->next;
}
else
{
g_assert (hist->current && hist->current->prev);
hist->current = hist->current->prev;
}
hist->done = FALSE;
}
static void history_goto (MooFileView *fileview,
const char *dirname)
{
History *hist = fileview->priv->history;
g_return_if_fail (dirname != NULL);
if (hist->done)
{
hist->done = FALSE;
return;
}
if (hist->current && hist->current->next)
{
GList *l;
for (l = hist->current->next; l != NULL; l = l->next)
g_free (l->data);
l = hist->current->next;
hist->current->next = NULL;
g_list_free (l);
}
if (!hist->current || strcmp (dirname, (char*) hist->current))
{
hist->list = g_list_append (hist->list, g_strdup (dirname));
hist->current = g_list_last (hist->list);
}
}
static void moo_file_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MooFileView *fileview = MOO_FILE_VIEW (object);
GError *error = NULL;
const char *dir;
switch (prop_id)
{
case PROP_CURRENT_DIRECTORY:
dir = g_value_get_string (value);
if (dir)
{
if (!moo_file_view_chdir (fileview, dir, &error))
{
g_warning ("%s: could not chdir to '%s'",
G_STRLOC, dir);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
}
}
else
{
moo_file_view_chdir (fileview, NULL, NULL);
}
break;
case PROP_FILE_MGR:
moo_file_view_set_file_mgr (fileview,
g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void moo_file_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MooFileView *fileview = MOO_FILE_VIEW (object);
switch (prop_id)
{
case PROP_CURRENT_DIRECTORY:
if (fileview->priv->current_dir)
g_value_set_string (value, moo_folder_get_path (fileview->priv->current_dir));
else
g_value_set_string (value, NULL);
break;
case PROP_FILE_MGR:
g_value_set_object (value, fileview->priv->mgr);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void moo_file_view_set_file_mgr (MooFileView *fileview,
MooEditFileMgr *mgr)
{
if (!mgr)
{
mgr = moo_edit_file_mgr_new ();
moo_file_view_set_file_mgr (fileview, mgr);
g_object_unref (mgr);
return;
}
if (mgr == fileview->priv->mgr)
return;
if (fileview->priv->mgr)
g_object_unref (fileview->priv->mgr);
fileview->priv->mgr = mgr;
g_object_ref (fileview->priv->mgr);
init_filter_combo (fileview);
}
static void block_filter_signals (MooFileView *fileview)
{
g_signal_handlers_block_by_func (fileview->priv->filter_combo,
(gpointer) filter_button_toggled,
fileview);
g_signal_handlers_block_by_func (fileview->priv->filter_combo,
(gpointer) filter_combo_changed,
fileview);
g_signal_handlers_block_by_func (fileview->priv->filter_combo,
(gpointer) filter_entry_activate,
fileview);
}
static void unblock_filter_signals (MooFileView *fileview)
{
g_signal_handlers_unblock_by_func (fileview->priv->filter_combo,
(gpointer) filter_button_toggled,
fileview);
g_signal_handlers_unblock_by_func (fileview->priv->filter_combo,
(gpointer) filter_combo_changed,
fileview);
g_signal_handlers_unblock_by_func (fileview->priv->filter_combo,
(gpointer) filter_entry_activate,
fileview);
}
static void init_filter_combo (MooFileView *fileview)
{
MooEditFileMgr *mgr = fileview->priv->mgr;
GtkFileFilter *filter;
block_filter_signals (fileview);
moo_edit_file_mgr_init_filter_combo (mgr, fileview->priv->filter_combo);
if (fileview->priv->current_filter)
g_object_unref (fileview->priv->current_filter);
fileview->priv->current_filter = NULL;
fileview->priv->use_current_filter = FALSE;
gtk_toggle_button_set_active (fileview->priv->filter_button, FALSE);
gtk_entry_set_text (fileview->priv->filter_entry, "");
unblock_filter_signals (fileview);
filter = moo_edit_file_mgr_get_last_filter (mgr);
if (filter) fileview_set_filter (fileview, filter);
}
static void filter_button_toggled (MooFileView *fileview)
{
gboolean active =
gtk_toggle_button_get_active (fileview->priv->filter_button);
if (active == fileview->priv->use_current_filter)
return;
fileview_set_use_filter (fileview, active, TRUE);
focus_to_file_view (fileview);
}
static void filter_combo_changed (MooFileView *fileview)
{
GtkTreeIter iter;
GtkFileFilter *filter;
MooEditFileMgr *mgr = fileview->priv->mgr;
GtkComboBox *combo = fileview->priv->filter_combo;
if (!gtk_combo_box_get_active_iter (combo, &iter))
return;
filter = moo_edit_file_mgr_get_filter (mgr, &iter);
g_return_if_fail (filter != NULL);
fileview_set_filter (fileview, filter);
focus_to_file_view (fileview);
}
static void filter_entry_activate (MooFileView *fileview)
{
const char *text;
GtkFileFilter *filter;
MooEditFileMgr *mgr = fileview->priv->mgr;
text = gtk_entry_get_text (fileview->priv->filter_entry);
if (text && text[0])
filter = moo_edit_file_mgr_new_user_filter (mgr, text);
else
filter = NULL;
fileview_set_filter (fileview, filter);
focus_to_file_view (fileview);
}
static void fileview_set_filter (MooFileView *fileview,
GtkFileFilter *filter)
{
GtkFileFilter *null_filter;
if (filter && filter == fileview->priv->current_filter)
{
fileview_set_use_filter (fileview, TRUE, TRUE);
return;
}
null_filter = moo_edit_file_mgr_get_null_filter (fileview->priv->mgr);
if (filter == null_filter)
return fileview_set_filter (fileview, NULL);
if (filter && (gtk_file_filter_get_needed (filter) & GTK_FILE_FILTER_URI))
{
g_warning ("%s: The filter set wants uri, but i do not know "
"how to work with uri's. Ignoring", G_STRLOC);
return;
}
block_filter_signals (fileview);
if (fileview->priv->current_filter)
g_object_unref (fileview->priv->current_filter);
fileview->priv->current_filter = filter;
if (filter)
{
const char *name;
gtk_object_sink (GTK_OBJECT (g_object_ref (filter)));
name = gtk_file_filter_get_name (filter);
gtk_entry_set_text (fileview->priv->filter_entry, name);
fileview_set_use_filter (fileview, TRUE, FALSE);
}
else
{
gtk_entry_set_text (fileview->priv->filter_entry, "");
fileview_set_use_filter (fileview, FALSE, FALSE);
}
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
(fileview->priv->filter_model));
unblock_filter_signals (fileview);
}
static void fileview_set_use_filter (MooFileView *fileview,
gboolean use,
gboolean block_signals)
{
if (block_signals)
block_filter_signals (fileview);
gtk_toggle_button_set_active (fileview->priv->filter_button, use);
if (fileview->priv->use_current_filter != use)
{
if (fileview->priv->current_filter)
{
fileview->priv->use_current_filter = use;
if (block_signals)
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
(fileview->priv->filter_model));
}
else
{
fileview->priv->use_current_filter = FALSE;
gtk_toggle_button_set_active (fileview->priv->filter_button, FALSE);
}
}
if (block_signals)
unblock_filter_signals (fileview);
}
static void create_goto_item (MooFileView *fileview,
GtkWidget *menu,
const char *label,
const char *stock_id,
const char *signal)
{
GtkWidget *item, *icon;
icon = gtk_image_new_from_stock (stock_id,
GTK_ICON_SIZE_MENU);
gtk_widget_show (GTK_WIDGET (icon));
item = gtk_image_menu_item_new_with_label (label);
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), icon);
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
g_object_set_data (G_OBJECT (item), "moo-file-view-signal",
(gpointer) signal);
g_signal_connect (item, "activate",
G_CALLBACK (goto_item_activated),
fileview);
}
static void add_separator_item (GtkWidget *menu)
{
GtkWidget *item = gtk_separator_menu_item_new ();
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
}
static void view_type_item_toggled (GtkWidget *widget,
MooFileView *fileview)
{
gboolean active = FALSE;
g_object_get (widget, "active", &active, NULL);
if (active)
{
MooFileViewType view_type = GPOINTER_TO_INT (
g_object_get_data (G_OBJECT (widget), "moo-file-view-type"));
moo_file_view_set_view_type (fileview, view_type);
}
}
static void create_view_type_items (MooFileView *fileview,
GtkWidget *menu)
{
GtkWidget *icon_item, *list_item;
GSList *group = NULL;
icon_item = gtk_radio_menu_item_new_with_label (group, "Short View");
gtk_widget_show (icon_item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), icon_item);
g_object_set_data (G_OBJECT (icon_item), "moo-file-view-type",
GINT_TO_POINTER (MOO_FILE_VIEW_ICON));
group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (icon_item));
list_item = gtk_radio_menu_item_new_with_label (group, "Detailed View");
gtk_widget_show (list_item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), list_item);
g_object_set_data (G_OBJECT (list_item), "moo-file-view-type",
GINT_TO_POINTER (MOO_FILE_VIEW_LIST));
if (fileview->priv->view_type == MOO_FILE_VIEW_ICON)
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (icon_item), TRUE);
else
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (list_item), TRUE);
g_signal_connect (icon_item, "toggled",
G_CALLBACK (view_type_item_toggled), fileview);
g_signal_connect (list_item, "toggled",
G_CALLBACK (view_type_item_toggled), fileview);
}
static void create_view_submenu (MooFileView *fileview,
GtkWidget *menu)
{
GtkWidget *view_item, *item, *submenu;
view_item = gtk_menu_item_new_with_label ("View");
gtk_widget_show (view_item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), view_item);
submenu = gtk_menu_new ();
gtk_widget_show (submenu);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (view_item), submenu);
item = gtk_check_menu_item_new_with_label ("Show Hidden Files");
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
fileview->priv->show_hidden_files);
g_signal_connect (item, "toggled",
G_CALLBACK (show_hidden_toggled), fileview);
item = gtk_check_menu_item_new_with_label ("Show Parent Folder");
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
fileview->priv->show_two_dots);
g_signal_connect (item, "toggled",
G_CALLBACK (show_two_dots_toggled), fileview);
add_separator_item (submenu);
create_view_type_items (fileview, submenu);
}
static GtkWidget *create_popup_menu (MooFileView *fileview)
{
GtkWidget *menu;
menu = gtk_menu_new ();
create_goto_item (fileview, menu, "Parent Folder",
GTK_STOCK_GO_UP, "go-up");
create_goto_item (fileview, menu, "Go Back",
GTK_STOCK_GO_BACK, "go-back");
create_goto_item (fileview, menu, "Go Forward",
GTK_STOCK_GO_FORWARD, "go-forward");
create_goto_item (fileview, menu, "Home Folder",
GTK_STOCK_HOME, "go-home");
add_separator_item (menu);
create_view_submenu (fileview, menu);
return menu;
}
static gboolean really_destroy_menu (GtkWidget *menu)
{
gtk_widget_destroy (menu);
return FALSE;
}
static void destroy_menu (GtkWidget *menu)
{
g_idle_add ((GSourceFunc) really_destroy_menu, menu);
}
static void do_popup (MooFileView *fileview,
GdkEventButton *event,
GtkTreePath *filter_path)
{
GtkWidget *menu;
MooFile *file = NULL;
char *path = NULL;
if (filter_path)
{
GtkTreeIter iter;
g_return_if_fail (gtk_tree_model_get_iter (fileview->priv->filter_model,
&iter, filter_path));
gtk_tree_model_get (fileview->priv->filter_model, &iter,
COLUMN_FILE, &file, -1);
g_return_if_fail (file != NULL);
}
menu = create_popup_menu (fileview);
g_signal_connect (menu, "deactivate", G_CALLBACK (destroy_menu), NULL);
if (file != NULL)
{
g_assert (fileview->priv->current_dir != NULL);
path = g_build_filename (moo_folder_get_path (fileview->priv->current_dir),
moo_file_get_basename (file), NULL);
}
g_signal_emit (fileview, signals[POPULATE_POPUP], 0, path, menu);
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
event->button, event->time);
g_free (path);
moo_file_unref (file);
}
static gboolean tree_button_press (GtkTreeView *treeview,
GdkEventButton *event,
MooFileView *fileview)
{
GtkTreeSelection *selection;
GtkTreePath *filter_path = NULL;
if (event->button != 3)
return FALSE;
selection = gtk_tree_view_get_selection (treeview);
if (gtk_tree_view_get_path_at_pos (treeview, event->x, event->y,
&filter_path, NULL, NULL, NULL))
{
gtk_tree_selection_unselect_all (selection);
gtk_tree_selection_select_path (selection, filter_path);
}
do_popup (fileview, event, filter_path);
gtk_tree_path_free (filter_path);
return TRUE;
}
static gboolean icon_button_press (MooIconView *iconview,
GdkEventButton *event,
MooFileView *fileview)
{
GtkTreePath *filter_path = NULL;
if (event->button != 3)
return FALSE;
filter_path = moo_icon_view_get_path (iconview, event->x, event->y);
if (filter_path)
moo_icon_view_select_path (iconview, filter_path);
do_popup (fileview, event, filter_path);
gtk_tree_path_free (filter_path);
return TRUE;
}
static void show_hidden_toggled (GtkWidget *widget,
MooFileView *fileview)
{
gboolean active = fileview->priv->show_hidden_files;
g_object_get (G_OBJECT (widget), "active", &active, NULL);
moo_file_view_set_show_hidden (fileview, active);
}
static void show_two_dots_toggled (GtkWidget *widget,
MooFileView *fileview)
{
gboolean active = fileview->priv->show_two_dots;
g_object_get (G_OBJECT (widget), "active", &active, NULL);
moo_file_view_set_show_parent (fileview, active);
}
void moo_file_view_set_show_hidden (MooFileView *fileview,
gboolean show)
{
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
if (fileview->priv->show_hidden_files != show)
{
fileview->priv->show_hidden_files = show;
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (
fileview->priv->filter_model));
}
}
void moo_file_view_set_show_parent (MooFileView *fileview,
gboolean show)
{
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
if (fileview->priv->show_two_dots != show)
{
fileview->priv->show_two_dots = show;
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (
fileview->priv->filter_model));
}
}
static void toggle_show_hidden (MooFileView *fileview)
{
moo_file_view_set_show_hidden (fileview,
!fileview->priv->show_hidden_files);
}
static GtkWidget *get_file_view_widget (MooFileView *fileview)
{
if (fileview->priv->view_type == MOO_FILE_VIEW_ICON)
return GTK_WIDGET(fileview->priv->iconview);
else
return GTK_WIDGET(fileview->priv->treeview);
}
/****************************************************************************/
/* Auxiliary stuff
*/
static void file_widget_move_selection (MooFileView *fileview,
GtkTreeIter *filter_iter)
{
GtkTreePath *path = NULL;
if (filter_iter)
{
path = gtk_tree_model_get_path (fileview->priv->filter_model,
filter_iter);
g_return_if_fail (path != NULL);
}
if (fileview->priv->view_type == MOO_FILE_VIEW_LIST)
{
GtkTreeSelection *selection;
GtkTreeView *treeview;
treeview = fileview->priv->treeview;
selection = gtk_tree_view_get_selection (treeview);
gtk_tree_selection_unselect_all (selection);
if (path)
{
gtk_tree_selection_select_path (selection, path);
gtk_tree_view_scroll_to_cell (treeview, path, NULL,
FALSE, 0, 0);
}
}
else
{
moo_icon_view_move_cursor (fileview->priv->iconview, path);
}
gtk_tree_path_free (path);
}
/* returns path in the fileview->priv->store */
static GtkTreePath *file_widget_get_selected (MooFileView *fileview)
{
if (fileview->priv->view_type == MOO_FILE_VIEW_LIST)
{
GtkTreeSelection *selection;
GtkTreeView *treeview;
GtkTreeIter filter_iter, iter;
GtkTreeModel *filter_model;
treeview = fileview->priv->treeview;
selection = gtk_tree_view_get_selection (treeview);
if (!gtk_tree_selection_get_selected (selection, &filter_model, &filter_iter))
return NULL;
gtk_tree_model_filter_convert_iter_to_child_iter (
GTK_TREE_MODEL_FILTER (filter_model), &iter, &filter_iter);
return gtk_tree_model_get_path (GTK_TREE_MODEL (fileview->priv->store), &iter);
}
else
{
GtkTreePath *path;
GtkTreePath *filter_path = moo_icon_view_get_selected (fileview->priv->iconview);
if (!filter_path)
return NULL;
path = gtk_tree_model_filter_convert_path_to_child_path (
GTK_TREE_MODEL_FILTER (fileview->priv->filter_model), filter_path);
gtk_tree_path_free (filter_path);
return path;
}
}
static gboolean find_match (MooFileView *fileview,
const char *text,
GtkTreeIter *iter,
gboolean exact_match)
{
GtkTreeModel *model = GTK_TREE_MODEL (fileview->priv->store);
guint len;
g_return_val_if_fail (text != NULL, FALSE);
if (!text[0])
return FALSE;
if (!gtk_tree_model_get_iter_first (model, iter))
return FALSE;
len = strlen (text);
while (TRUE)
{
char *val;
gboolean match;
gtk_tree_model_get (model, iter,
COLUMN_DISPLAY_NAME, &val, -1);
if (val)
{
if (exact_match)
match = !strcmp (text, val);
else
match = !strncmp (text, val, len);
g_free (val);
if (match)
return TRUE;
}
if (!gtk_tree_model_iter_next (model, iter))
return FALSE;
}
}
/****************************************************************************/
/* Search entry
*/
static void set_temp_visible_attributes (MooFileView *fileview,
GtkTreePath *path,
gboolean set)
{
GtkTreeIter iter;
g_assert (path != NULL);
gtk_tree_model_get_iter (GTK_TREE_MODEL (fileview->priv->store),
&iter, path);
if (set)
gtk_list_store_set (fileview->priv->store, &iter,
COLUMN_STYLE, PANGO_STYLE_ITALIC,
COLUMN_STYLE_SET, TRUE,
-1);
else
gtk_list_store_set (fileview->priv->store, &iter,
COLUMN_STYLE, PANGO_STYLE_NORMAL,
COLUMN_STYLE_SET, FALSE,
-1);
}
static gboolean try_completion (MooFileView *fileview,
const char *text);
static void completion_clear (MooFileView *fileview);
static gboolean search_entry_key_press (GtkWidget *entry,
GdkEventKey *event,
MooFileView *fileview);
static gboolean search_entry_focus_out (GtkWidget *entry,
GdkEventFocus *event,
MooFileView *fileview);
static void search_entry_activate (GtkEntry *entry,
MooFileView *fileview);
static void search_entry_changed (GtkEntry *entry,
MooFileView *fileview);
static void start_interactive_search (MooFileView *fileview)
{
GtkWidget *entry = GTK_WIDGET (fileview->priv->search_entry);
file_widget_move_selection (fileview, NULL);
gtk_widget_show (entry);
gtk_widget_grab_focus (entry);
fileview->priv->in_search = TRUE;
g_signal_connect (entry, "key-press-event",
G_CALLBACK (search_entry_key_press),
fileview);
g_signal_connect_after (entry, "focus-out-event",
G_CALLBACK (search_entry_focus_out),
fileview);
g_signal_connect (entry, "activate",
G_CALLBACK (search_entry_activate),
fileview);
g_signal_connect (entry, "changed",
G_CALLBACK (search_entry_changed),
fileview);
}
static void stop_interactive_search (MooFileView *fileview,
gboolean focus_to_file_view)
{
GtkWidget *entry = GTK_WIDGET (fileview->priv->search_entry);
fileview->priv->in_search = FALSE;
g_signal_handlers_disconnect_by_func (entry,
(gpointer) search_entry_key_press,
fileview);
g_signal_handlers_disconnect_by_func (entry,
(gpointer) search_entry_focus_out,
fileview);
g_signal_handlers_disconnect_by_func (entry,
(gpointer) search_entry_activate,
fileview);
g_signal_handlers_disconnect_by_func (entry,
(gpointer) search_entry_changed,
fileview);
gtk_widget_hide (entry);
gtk_entry_set_text (GTK_ENTRY (entry), "");
/* TODO: may it become invalid? */
if (fileview->priv->temp_visible_row &&
gtk_tree_row_reference_valid (fileview->priv->temp_visible_row))
{
GtkTreePath *temp_path =
gtk_tree_row_reference_get_path (fileview->priv->temp_visible_row);
g_assert (temp_path != NULL);
set_temp_visible_attributes (fileview, temp_path, FALSE);
}
if (fileview->priv->temp_visible_row)
{
g_assert (gtk_tree_row_reference_valid (fileview->priv->temp_visible_row));
gtk_tree_row_reference_free (fileview->priv->temp_visible_row);
fileview->priv->temp_visible_row = NULL;
g_free (fileview->priv->temp_visible);
fileview->priv->temp_visible = NULL;
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
(fileview->priv->filter_model));
}
else
{
g_assert (fileview->priv->temp_visible == NULL);
}
completion_clear (fileview);
if (focus_to_file_view)
gtk_widget_grab_focus (get_file_view_widget (fileview));
}
#define PRINT_KEY_EVENT(event) \
g_print ("%s%s%s%s\n", \
event->state & GDK_SHIFT_MASK ? "<Shift>" : "", \
event->state & GDK_CONTROL_MASK ? "<Control>" : "", \
event->state & GDK_MOD1_MASK ? "<Alt>" : "", \
gdk_keyval_name (event->keyval))
static gboolean moo_file_view_key_press (GtkWidget *widget,
GdkEventKey *event,
MooFileView *fileview)
{
GtkWidget *entry = GTK_WIDGET (fileview->priv->search_entry);
if (fileview->priv->in_search)
{
g_warning ("%s: something wrong", G_STRLOC);
stop_interactive_search (fileview, FALSE);
return FALSE;
}
if (GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS (widget))->key_press_event (widget, event))
return TRUE;
if (GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS (fileview))->key_press_event (widget, event))
return TRUE;
if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
return FALSE;
if (event->string && event->length)
{
GdkEvent *copy = gdk_event_copy ((GdkEvent*) event);
gtk_widget_realize (entry);
g_object_unref (copy->key.window);
copy->key.window = g_object_ref (entry->window);
start_interactive_search (fileview);
gtk_widget_event (entry, copy);
gdk_event_free (copy);
return TRUE;
}
return FALSE;
}
static gboolean search_entry_key_press (G_GNUC_UNUSED GtkWidget *entry,
GdkEventKey *event,
MooFileView *fileview)
{
GtkWidget *filewidget;
switch (event->keyval)
{
case GDK_Escape:
stop_interactive_search (fileview, TRUE);
return TRUE;
case GDK_Up:
case GDK_KP_Up:
case GDK_Down:
case GDK_KP_Down:
stop_interactive_search (fileview, TRUE);
filewidget = get_file_view_widget (fileview);
gtk_widget_grab_focus (filewidget);
GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS (filewidget))->
key_press_event (filewidget, event);
return TRUE;
default:
return FALSE;
}
}
static gboolean search_entry_focus_out (GtkWidget *entry,
G_GNUC_UNUSED GdkEventFocus *event,
MooFileView *fileview)
{
/* focus may be lost due to switching to other window, focus file list then */
stop_interactive_search (fileview, gtk_widget_is_focus (entry));
return FALSE;
}
static void process_entry_text (MooFileView *fileview,
GtkEntry *entry,
const char *text);
static void search_entry_changed (GtkEntry *entry,
MooFileView *fileview)
{
const char *text = gtk_entry_get_text (entry);
g_return_if_fail (text != NULL);
process_entry_text (fileview, entry, text);
}
static void process_entry_text (MooFileView *fileview,
GtkEntry *entry,
const char *text)
{
GtkTreeIter iter, filter_iter;
gboolean need_to_refilter = FALSE;
gboolean need_to_clear_completion = TRUE;
g_return_if_fail (text != NULL);
if (!strncmp (text, "./", 2))
return process_entry_text (fileview, entry, text + 2);
/* Check if some file was shown temporarily, and hide it */
if (fileview->priv->temp_visible)
{
/* if still the same filename is entered, don't do anything */
if (!strcmp (text, fileview->priv->temp_visible))
goto out;
if (fileview->priv->temp_visible_row &&
gtk_tree_row_reference_valid (fileview->priv->temp_visible_row))
{
GtkTreePath *temp_path =
gtk_tree_row_reference_get_path (fileview->priv->temp_visible_row);
g_assert (temp_path != NULL);
set_temp_visible_attributes (fileview, temp_path, FALSE);
}
gtk_tree_row_reference_free (fileview->priv->temp_visible_row);
fileview->priv->temp_visible_row = NULL;
g_free (fileview->priv->temp_visible);
fileview->priv->temp_visible = NULL;
need_to_refilter = TRUE;
}
if (!text[0])
{
file_widget_move_selection (fileview, NULL);
goto out;
}
/* first, try if this is a file in the list */
if (find_match (fileview, text, &iter, FALSE))
{
GtkTreePath *filter_path, *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (fileview->priv->store),
&iter);
g_return_if_fail (path != NULL);
filter_path = gtk_tree_model_filter_convert_child_path_to_path (
GTK_TREE_MODEL_FILTER (fileview->priv->filter_model), path);
/* file is not currently visible, check if full name was typed in,
and show the file if it's the case */
if (!filter_path)
{
MooFile *file = NULL;
gtk_tree_model_get (GTK_TREE_MODEL (fileview->priv->store),
&iter, COLUMN_FILE, &file, -1);
if (file && !strcmp (moo_file_get_display_basename (file), text))
{
/* full name of hidden file was typed in, show it */
fileview->priv->temp_visible =
g_strdup (moo_file_get_display_basename (file));
fileview->priv->temp_visible_row =
gtk_tree_row_reference_new (GTK_TREE_MODEL (fileview->priv->store), path);
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
(fileview->priv->filter_model));
need_to_refilter = FALSE;
set_temp_visible_attributes (fileview, path, TRUE);
filter_path = gtk_tree_model_filter_convert_child_path_to_path (
GTK_TREE_MODEL_FILTER (fileview->priv->filter_model), path);
g_assert (filter_path != NULL);
gtk_tree_model_get_iter (fileview->priv->filter_model,
&filter_iter, filter_path);
file_widget_move_selection (fileview, &filter_iter);
}
else
{
/* only some part of name of hidden file was typed in,
deselect currently selected file */
file_widget_move_selection (fileview, NULL);
}
moo_file_unref (file);
}
/* file is visible,just move cursor there */
else
{
gtk_tree_model_get_iter (fileview->priv->filter_model,
&filter_iter, filter_path);
file_widget_move_selection (fileview, &filter_iter);
}
gtk_tree_path_free (path);
gtk_tree_path_free (filter_path);
goto out;
}
/* typed text is not a filename in the list, try completion */
if (try_completion (fileview, text))
{
need_to_clear_completion = FALSE;
goto out;
}
/* failed, unselect everything */
file_widget_move_selection (fileview, NULL);
out:
if (need_to_refilter)
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
(fileview->priv->filter_model));
if (need_to_clear_completion)
completion_clear (fileview);
}
void moo_file_view_select_file (MooFileView *fileview,
const char *basename)
{
if (basename)
{
GtkTreeIter iter, filter_iter;
if (find_match (fileview, basename, &iter, FALSE))
{
GtkTreePath *filter_path, *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (fileview->priv->store),
&iter);
g_return_if_fail (path != NULL);
filter_path = gtk_tree_model_filter_convert_child_path_to_path (
GTK_TREE_MODEL_FILTER (fileview->priv->filter_model), path);
if (!filter_path)
{
gtk_tree_path_free (path);
if (gtk_tree_model_get_iter_first (fileview->priv->filter_model,
&filter_iter))
file_widget_move_selection (fileview, &filter_iter);
else
fileview->priv->select_first = TRUE;
}
else
{
gtk_tree_model_get_iter (fileview->priv->filter_model,
&filter_iter, filter_path);
file_widget_move_selection (fileview, &filter_iter);
gtk_tree_path_free (path);
gtk_tree_path_free (filter_path);
}
}
else
{
g_free (fileview->priv->name_to_select);
fileview->priv->name_to_select = g_strdup (basename);
}
}
else
{
file_widget_move_selection (fileview, NULL);
}
}
/*********************************************************************/
/* Completion stuff
*/
enum {
SEARCH_COLUMN_NAME,
SEARCH_COLUMN_FILE
};
struct _Completion {
GtkEntry *entry;
GtkEntryCompletion *completion;
GtkListStore *store;
MooFolder *folder;
char *text_utf8; /* what was entered into search entry */
char *basename; /* real basename */
char *basename_utf8;/* basename part of text_utf8 */
char *dirname; /* real dirname, like /home/username for ~ */
char *dirname_utf8; /* dir part of text_utf8 */
};
static void completion_connect_folder (MooFileView *fileview,
MooFolder *folder);
static void completion_disconnect_folder(MooFileView *fileview);
static void completion_clear (MooFileView *fileview)
{
Completion *cmpl = fileview->priv->completion;
gtk_list_store_clear (cmpl->store);
completion_disconnect_folder (fileview);
g_free (cmpl->basename);
g_free (cmpl->dirname);
g_free (cmpl->text_utf8);
g_free (cmpl->basename_utf8);
g_free (cmpl->dirname_utf8);
cmpl->basename = NULL;
cmpl->dirname = NULL;
cmpl->text_utf8 = NULL;
cmpl->basename_utf8 = NULL;
cmpl->dirname_utf8 = NULL;
}
static void completion_init (MooFileView *fileview)
{
Completion *cmpl;
cmpl = fileview->priv->completion = g_new0 (Completion, 1);
cmpl->entry = fileview->priv->search_entry;
cmpl->completion = gtk_entry_completion_new ();
gtk_entry_set_completion (cmpl->entry, cmpl->completion);
cmpl->store = gtk_list_store_new (2,
G_TYPE_STRING,
MOO_TYPE_FILE);
gtk_entry_completion_set_model (cmpl->completion,
GTK_TREE_MODEL (cmpl->store));
gtk_entry_completion_set_text_column (cmpl->completion,
SEARCH_COLUMN_NAME);
}
static void completion_free (MooFileView *fileview)
{
Completion *cmpl = fileview->priv->completion;
completion_clear (fileview);
g_object_unref (cmpl->completion);
g_object_unref (cmpl->store);
g_free (cmpl);
fileview->priv->completion = NULL;
}
static gboolean parse_partial_filename (const char *text_utf8,
char **dirname,
char **basename,
char **display_dirname,
char **display_basename,
GError **error);
static MooFolder *get_folder_for_dirname (MooFileView *fileview,
const char *dirname,
GError **error);
static char *filename_to_absolute (MooFileView *fileview,
const char *filename);
#if 0
#define PRINT_COMPLETION g_print
#else
static void PRINT_COMPLETION (const char *format, ...) G_GNUC_PRINTF (1, 2);
static void PRINT_COMPLETION (G_GNUC_UNUSED const char *format, ...)
{
}
#endif
static gboolean try_completion (MooFileView *fileview,
const char *text_utf8)
{
Completion *cmpl = fileview->priv->completion;
GError *error = NULL;
char *basename, *dirname, *dirname_utf8, *basename_utf8;
MooFolder *folder;
g_return_val_if_fail (text_utf8 && text_utf8[0], FALSE);
if (cmpl->text_utf8 && !strcmp (text_utf8, cmpl->text_utf8))
return TRUE;
if (!parse_partial_filename (text_utf8, &dirname, &basename,
&dirname_utf8, &basename_utf8,
&error))
{
g_message ("%s: could not parse '%s'", G_STRLOC, text_utf8);
if (error)
{
g_message ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
completion_clear (fileview);
return FALSE;
}
g_assert (dirname != NULL && basename != NULL);
g_assert (dirname_utf8 != NULL && basename_utf8 != NULL);
if (!cmpl->dirname || strcmp (cmpl->dirname, dirname))
{
folder = get_folder_for_dirname (fileview, dirname, &error);
if (!folder)
{
g_message ("%s: could not get dir for '%s'",
G_STRLOC, dirname);
if (error)
{
g_message ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
g_free (dirname);
g_free (basename);
g_free (dirname_utf8);
g_free (basename_utf8);
return FALSE;
}
}
else
{
g_assert (cmpl->folder != NULL);
folder = cmpl->folder;
/* it's unref'ed in completion_clear() */
g_object_ref (folder);
}
completion_clear (fileview);
cmpl->text_utf8 = g_strdup (text_utf8);
cmpl->basename = basename;
cmpl->basename_utf8 = basename_utf8;
cmpl->dirname = dirname;
cmpl->dirname_utf8 = dirname_utf8;
completion_connect_folder (fileview, folder);
/* it's ref'ed in completion_connect_folder() */
g_object_unref (folder);
PRINT_COMPLETION ("dirname: %s\n", dirname ? dirname : "NONE");
PRINT_COMPLETION ("basename: %s\n", basename ? basename : "NONE");
PRINT_COMPLETION ("dirname_utf8: %s\n", dirname_utf8 ? dirname_utf8 : "NONE");
PRINT_COMPLETION ("basename_utf8: %s\n", basename_utf8 ? basename_utf8 : "NONE");
return TRUE;
}
static gboolean parse_partial_filename (const char *text_utf8,
char **dirnamep,
char **basenamep,
char **display_dirnamep,
char **display_basenamep,
GError **error)
{
GError *error_here = NULL;
char *text = NULL;
const char *separator;
char *dirname = NULL, *basename = NULL;
char *display_dirname = NULL, *display_basename = NULL;
const char *single = NULL;
gsize len;
g_return_val_if_fail (text_utf8 && text_utf8[0], FALSE);
if (!strcmp (text_utf8, G_DIR_SEPARATOR_S))
single = G_DIR_SEPARATOR_S;
else if (!strcmp (text_utf8, "~"))
single = "~";
if (single)
{
display_dirname = g_strdup (single);
dirname = g_strdup (single);
basename = g_strdup ("");
display_basename = g_strdup ("");
goto success;
}
text = g_filename_from_utf8 (text_utf8, -1, NULL, &len, &error_here);
if (error_here)
{
g_propagate_error (error, error_here);
goto error_label;
}
if (len == 1 && text[0] == G_DIR_SEPARATOR)
{
display_dirname = g_strdup (G_DIR_SEPARATOR_S);
dirname = g_strdup (G_DIR_SEPARATOR_S);
basename = g_strdup ("");
display_basename = g_strdup ("");
text = NULL;
goto success;
}
if (text[len-1] == G_DIR_SEPARATOR)
{
display_dirname = g_strndup (text_utf8, strlen (text_utf8) - 1);
dirname = text;
dirname[len-1] = 0;
basename = g_strdup ("");
display_basename = g_strdup ("");
text = NULL;
goto success;
}
if (!(separator = strrchr (text, G_DIR_SEPARATOR)))
{
display_basename = g_strdup (text_utf8);
basename = text;
dirname = g_strdup ("");
display_dirname = g_strdup ("");
text = NULL;
goto success;
}
/* TODO TODO this all is broken, at least on windows */
/* TODO TODO normalize dirname */
if (separator == text)
{
basename = g_strdup (separator + 1);
dirname = g_strdup (G_DIR_SEPARATOR_S);
display_basename = g_filename_display_name (basename);
display_dirname = g_filename_display_name (dirname);
goto success;
}
else
{
basename = g_strdup (separator + 1);
dirname = g_strndup (text, separator - text);
display_basename = g_filename_display_name (basename);
display_dirname = g_filename_display_name (dirname);
goto success;
}
/* no fallthrough */
g_assert_not_reached ();
error_label:
g_free (text);
g_free (dirname);
g_free (basename);
g_free (display_dirname);
g_free (display_basename);
return FALSE;
success:
g_free (text);
*dirnamep = dirname;
*basenamep = basename;
*display_dirnamep = display_dirname;
*display_basenamep = display_basename;
return TRUE;
}
static char *filename_to_absolute (MooFileView *fileview,
const char *filename)
{
g_return_val_if_fail (filename && filename[0], g_strdup (filename));
if (filename[0] == '~')
{
const char *home = g_get_home_dir ();
g_return_val_if_fail (home != NULL, g_strdup (filename));
if (filename[1])
return g_build_filename (home, filename + 1, NULL);
else
return g_strdup (home);
}
if (g_path_is_absolute (filename))
return g_strdup (filename);
if (fileview->priv->current_dir)
return g_build_filename (moo_folder_get_path (fileview->priv->current_dir),
filename, NULL);
g_return_val_if_reached (g_strdup (filename));
}
static MooFolder *get_folder_for_dirname (MooFileView *fileview,
const char *dirname,
GError **error)
{
MooFolder *folder;
char *path = NULL;
g_assert (dirname != NULL);
g_return_val_if_fail (dirname[0] != 0, NULL);
path = filename_to_absolute (fileview, dirname);
g_return_val_if_fail (path != NULL, NULL);
folder = moo_file_system_get_folder (fileview->priv->file_system,
path, MOO_FILE_HAS_STAT, error);
g_free (path);
return folder;
}
static void completion_update_files (MooFileView *fileview);
static void completion_folder_deleted (MooFolder *folder,
MooFileView *fileview);
static void completion_files_added (MooFolder *folder,
GSList *files,
MooFileView *fileview);
static void completion_files_changed (MooFolder *folder,
GSList *files,
MooFileView *fileview);
static void completion_files_removed (MooFolder *folder,
GSList *files,
MooFileView *fileview);
static void completion_connect_folder (MooFileView *fileview,
MooFolder *folder)
{
Completion *cmpl = fileview->priv->completion;
g_assert (MOO_IS_FOLDER (folder));
g_assert (cmpl->folder == NULL);
PRINT_COMPLETION ("completion_connect_folder, folder '%s'\n",
moo_folder_get_path (folder));
cmpl->folder = g_object_ref (folder);
g_signal_connect (folder, "deleted",
G_CALLBACK (completion_folder_deleted), fileview);
g_signal_connect (folder, "files_added",
G_CALLBACK (completion_files_added), fileview);
g_signal_connect (folder, "files_removed",
G_CALLBACK (completion_files_removed), fileview);
g_signal_connect (folder, "files_changed",
G_CALLBACK (completion_files_changed), fileview);
completion_update_files (fileview);
}
static void completion_disconnect_folder(MooFileView *fileview)
{
MooFolder *folder = fileview->priv->completion->folder;
PRINT_COMPLETION ("completion_disconnect_folder, folder '%s'\n",
folder ? moo_folder_get_path (folder) : "NULL");
if (folder)
{
fileview->priv->completion->folder = NULL;
g_signal_handlers_disconnect_by_func (folder,
(gpointer) completion_folder_deleted,
fileview);
g_signal_handlers_disconnect_by_func (folder,
(gpointer) completion_files_added,
fileview);
g_signal_handlers_disconnect_by_func (folder,
(gpointer) completion_files_removed,
fileview);
g_signal_handlers_disconnect_by_func (folder,
(gpointer) completion_files_changed,
fileview);
g_object_unref (folder);
}
}
static void completion_update_files (MooFileView *fileview)
{
Completion *cmpl = fileview->priv->completion;
GtkTreeIter iter;
GSList *files, *l;
PRINT_COMPLETION ("completion_update_files, folder '%s'\n",
moo_folder_get_path (cmpl->folder));
g_assert (cmpl->folder != NULL);
g_assert (cmpl->basename != NULL);
g_assert (cmpl->basename_utf8 != NULL);
g_assert (cmpl->dirname != NULL);
g_assert (cmpl->dirname_utf8 != NULL);
files = moo_folder_list_files (cmpl->folder);
for (l = files; l != NULL; l = l->next)
{
char *name;
if (!cmpl->basename[0])
{
if (!moo_file_view_check_visible (fileview, l->data, FALSE, TRUE))
continue;
}
else
{
if (!moo_file_view_check_visible (fileview, l->data, TRUE, TRUE))
continue;
}
if (moo_file_test (l->data, MOO_FILE_IS_FOLDER))
name = g_build_filename (cmpl->dirname_utf8,
moo_file_get_display_basename (l->data),
G_DIR_SEPARATOR_S, NULL);
else
name = g_build_filename (cmpl->dirname_utf8,
moo_file_get_display_basename (l->data),
NULL);
gtk_list_store_append (cmpl->store, &iter);
gtk_list_store_set (cmpl->store, &iter,
SEARCH_COLUMN_NAME, name,
SEARCH_COLUMN_FILE, l->data,
-1);
g_free (name);
}
g_slist_foreach (files, (GFunc) moo_file_unref, NULL);
g_slist_free (files);
}
static void completion_folder_deleted (MooFolder *folder,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->completion->folder);
PRINT_COMPLETION ("completion_folder_deleted\n");
completion_clear (fileview);
}
static void completion_files_added (MooFolder *folder,
GSList *files,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->completion->folder);
PRINT_COMPLETION ("completion_files_added\n");
completion_update_files (fileview);
}
static void completion_files_changed (MooFolder *folder,
GSList *files,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->completion->folder);
PRINT_COMPLETION ("completion_files_changed\n");
completion_update_files (fileview);
}
static void completion_files_removed (MooFolder *folder,
GSList *files,
MooFileView *fileview)
{
g_assert (folder == fileview->priv->completion->folder);
PRINT_COMPLETION ("completion_files_removed\n");
completion_update_files (fileview);
}
static void activate_filename (MooFileView *fileview,
const char *filename)
{
GError *error = NULL;
char *dirname, *basename;
char *path = NULL;
path = filename_to_absolute (fileview, filename);
if (!path || !g_file_test (path, G_FILE_TEST_EXISTS))
{
g_free (path);
g_return_if_reached ();
}
if (g_file_test (path, G_FILE_TEST_IS_DIR))
{
if (!moo_file_view_chdir (fileview, path, &error))
{
g_warning ("%s: could not chdir to %s",
G_STRLOC, dirname);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
}
g_free (path);
return;
}
dirname = g_path_get_dirname (filename);
basename = g_path_get_basename (filename);
if (!dirname || !basename)
{
g_free (dirname);
g_free (basename);
g_return_if_reached ();
}
if (!moo_file_view_chdir (fileview, dirname, &error))
{
g_warning ("%s: could not chdir to %s",
G_STRLOC, dirname);
if (error)
{
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
g_free (path);
g_free (dirname);
g_free (basename);
return;
}
moo_file_view_select_file (fileview, basename);
g_signal_emit (fileview, signals[ACTIVATE], 0, path);
g_free (path);
g_free (dirname);
g_free (basename);
}
static void search_entry_activate (GtkEntry *entry,
MooFileView *fileview)
{
char *filename = NULL;
GtkTreePath *selected;
selected = file_widget_get_selected (fileview);
if (selected)
{
GtkTreeIter iter;
MooFile *file = NULL;
gtk_tree_model_get_iter (GTK_TREE_MODEL (fileview->priv->store),
&iter, selected);
gtk_tree_model_get (GTK_TREE_MODEL (fileview->priv->store), &iter,
COLUMN_FILE, &file, -1);
if (!file)
{
gtk_tree_path_free (selected);
g_return_if_fail (file != NULL);
}
filename = g_strdup (moo_file_get_basename (file));
moo_file_unref (file);
gtk_tree_path_free (selected);
}
else
{
filename = g_strdup (gtk_entry_get_text (entry));
}
stop_interactive_search (fileview, TRUE);
activate_filename (fileview, filename);
g_free (filename);
}