5704 lines
184 KiB
C
5704 lines
184 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
|
|
* moofileview.c
|
|
*
|
|
* Copyright (C) 2004-2006 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 GTK_DISABLE_DEPRECATED
|
|
#undef GTK_DISABLE_DEPRECATED
|
|
#include <gtk/gtktoolbar.h>
|
|
#define GTK_DISABLE_DEPRECATED
|
|
#endif
|
|
|
|
#define MOO_FILE_SYSTEM_COMPILATION
|
|
#include "moofileview/moofileview.h"
|
|
#include "moofileview/moofileview-dialogs.h"
|
|
#include "moofileview/moobookmarkmgr.h"
|
|
#include "moofileview/moofilesystem.h"
|
|
#include "moofileview/moofoldermodel.h"
|
|
#include "moofileview/moofileentry.h"
|
|
#include "moofileview/mooiconview.h"
|
|
#include "moofileview/moofileview-private.h"
|
|
#include "moofileview/moofileview-ui.h"
|
|
#include "moofileview/mootreeview.h"
|
|
#include "moofileview/moobookmarkview.h"
|
|
#include "mooutils/mooutils-gobject.h"
|
|
#include "mooutils/mooutils-fs.h"
|
|
#include "mooutils/mooutils-misc.h"
|
|
#include "mooutils/moodialogs.h"
|
|
#include "mooutils/moofiltermgr.h"
|
|
#include "mooutils/mootoggleaction.h"
|
|
#include "mooutils/moouixml.h"
|
|
#include "mooutils/moocmd.h"
|
|
#include "mooutils/moostock.h"
|
|
#include MOO_MARSHALS_H
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
#ifndef __WIN32__
|
|
#define TYPEAHEAD_CASE_SENSITIVE_DEFAULT FALSE
|
|
#define SORT_CASE_SENSITIVE_DEFAULT MOO_FOLDER_MODEL_SORT_CASE_SENSITIVE_DEFAULT
|
|
#define COMPLETION_CASE_SENSITIVE_DEFAULT TRUE
|
|
#else /* __WIN32__ */
|
|
#define TYPEAHEAD_CASE_SENSITIVE_DEFAULT FALSE
|
|
#define SORT_CASE_SENSITIVE_DEFAULT FALSE
|
|
#define COMPLETION_CASE_SENSITIVE_DEFAULT FALSE
|
|
#endif /* __WIN32__ */
|
|
|
|
|
|
enum {
|
|
TREEVIEW_PAGE = 0,
|
|
ICONVIEW_PAGE = 1,
|
|
BOOKMARK_PAGE = 2
|
|
};
|
|
|
|
|
|
enum {
|
|
TARGET_URI_LIST = 1,
|
|
TARGET_TEXT = 2
|
|
};
|
|
|
|
static GdkAtom moo_file_view_clipboard;
|
|
|
|
static GtkTargetEntry source_targets[] = {
|
|
{(char*) "text/uri-list", 0, TARGET_URI_LIST}
|
|
};
|
|
|
|
static GtkTargetEntry dest_targets[] = {
|
|
{(char*) "text/uri-list", 0, TARGET_URI_LIST}
|
|
};
|
|
|
|
|
|
typedef struct _History History;
|
|
typedef struct _Typeahead Typeahead;
|
|
typedef struct _Clipboard Clipboard;
|
|
|
|
struct _Clipboard {
|
|
MooFolder *folder;
|
|
GList *files;
|
|
gboolean cut;
|
|
};
|
|
|
|
struct _MooFileViewPrivate {
|
|
GtkTreeModel *model;
|
|
GtkTreeModel *filter_model;
|
|
MooFolder *current_dir;
|
|
MooFileSystem *file_system;
|
|
|
|
Clipboard *clipboard;
|
|
|
|
guint select_file_idle;
|
|
char *select_file;
|
|
|
|
GtkIconSize icon_size;
|
|
GtkNotebook *notebook;
|
|
MooFileViewType view_type;
|
|
MooFileViewType file_view_type;
|
|
|
|
MooTreeView *view;
|
|
GtkTreeView *treeview;
|
|
GtkTreeViewColumn *tree_name_column;
|
|
MooIconView *iconview;
|
|
MooBookmarkView *bkview;
|
|
|
|
GtkMenu *bookmarks_menu;
|
|
MooBookmarkMgr *bookmark_mgr;
|
|
|
|
char *home_dir;
|
|
|
|
gboolean show_hidden_files;
|
|
gboolean show_two_dots;
|
|
History *history;
|
|
GString *temp_visible; /* temporary visible name, for interactive search */
|
|
|
|
MooFilterMgr *filter_mgr;
|
|
GtkToggleButton *filter_button;
|
|
MooCombo *filter_combo;
|
|
GtkEntry *filter_entry;
|
|
GtkFileFilter *current_filter;
|
|
gboolean use_current_filter;
|
|
|
|
GtkEntry *entry;
|
|
int entry_state; /* it can be one of three: nothing, typeahead, or completion,
|
|
depending on text entered into the entry */
|
|
Typeahead *typeahead;
|
|
gboolean typeahead_case_sensitive;
|
|
gboolean sort_case_sensitive;
|
|
gboolean completion_case_sensitive;
|
|
|
|
MooActionGroup *actions;
|
|
MooUIXML *ui_xml;
|
|
gboolean has_selection;
|
|
|
|
GtkTargetList *targets;
|
|
GSList *drag_dest_widgets;
|
|
|
|
struct {
|
|
GtkWidget *button;
|
|
gboolean highlight;
|
|
GtkTreeRowReference *row;
|
|
guint timeout;
|
|
int x;
|
|
int y;
|
|
gboolean blink_clear;
|
|
guint n_blinks;
|
|
} drop_to;
|
|
};
|
|
|
|
|
|
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 void moo_file_view_hide (GtkWidget *widget);
|
|
static gboolean moo_file_view_key_press (MooFileView *fileview,
|
|
GtkWidget *widget,
|
|
GdkEventKey *event);
|
|
static gboolean moo_file_view_popup_menu (GtkWidget *widget);
|
|
static void moo_file_view_unrealize (GtkWidget *widget);
|
|
|
|
static void moo_file_view_set_filter_mgr (MooFileView *fileview,
|
|
MooFilterMgr *mgr);
|
|
static void moo_file_view_set_bookmark_mgr (MooFileView *fileview,
|
|
MooBookmarkMgr *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 bookmark_activate (MooBookmarkMgr *mgr,
|
|
MooBookmark *bookmark,
|
|
MooFileView *activated,
|
|
MooFileView *fileview);
|
|
static void bookmark_activated (MooFileView *fileview,
|
|
MooBookmark *bookmark);
|
|
|
|
static void history_init (MooFileView *fileview);
|
|
static void history_free (MooFileView *fileview);
|
|
static void history_add (MooFileView *fileview,
|
|
const char *dirname);
|
|
static void history_clear (MooFileView *fileview);
|
|
|
|
static gboolean filter_visible_func (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
MooFileView *fileview);
|
|
|
|
static gboolean moo_file_view_check_visible (MooFileView *fileview,
|
|
MooFile *file,
|
|
gboolean ignore_hidden,
|
|
gboolean ignore_two_dots);
|
|
|
|
static void icon_data_func (GObject *column_or_iconview,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
MooFileView *fileview);
|
|
static void name_data_func (GObject *column_or_iconview,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
MooFileView *fileview);
|
|
#ifdef USE_SIZE_AND_STUFF
|
|
static void date_data_func (GObject *column_or_iconview,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
MooFileView *fileview);
|
|
static void size_data_func (GObject *column_or_iconview,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
MooFileView *fileview);
|
|
#endif
|
|
|
|
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_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 GtkWidget *create_iconview (MooFileView *fileview);
|
|
static GtkWidget *create_bookmark_view (MooFileView *fileview);
|
|
|
|
static GtkWidget *get_view_widget (MooFileView *fileview);
|
|
static void file_view_move_selection (MooFileView *fileview,
|
|
GtkTreeIter *filter_iter);
|
|
static GList *file_view_get_selected_files(MooFileView *fileview);
|
|
|
|
static void path_entry_init (MooFileView *fileview);
|
|
static void path_entry_deinit (MooFileView *fileview);
|
|
static void path_entry_set_text (MooFileView *fileview,
|
|
const char *text);
|
|
static void stop_path_entry (MooFileView *fileview,
|
|
gboolean focus_file_list);
|
|
static void path_entry_delete_to_cursor (MooFileView *fileview);
|
|
static void file_view_activate_filename (MooFileView *fileview,
|
|
const char *display_name);
|
|
|
|
static void file_added (MooFileView *fileview);
|
|
|
|
static void file_view_delete_selected (MooFileView *fileview);
|
|
static void file_view_create_folder (MooFileView *fileview);
|
|
static void file_view_properties_dialog (MooFileView *fileview);
|
|
|
|
static void view_files (MooFileView *fileview);
|
|
static void view_bookmarks (MooFileView *fileview);
|
|
static void add_bookmark (MooFileView *fileview);
|
|
static void edit_bookmarks (MooFileView *fileview);
|
|
|
|
/* Dnd */
|
|
static void icon_drag_begin (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
MooIconView *iconview);
|
|
static void icon_drag_data_get (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
GtkSelectionData *data,
|
|
guint info,
|
|
guint time,
|
|
MooIconView *iconview);
|
|
static void icon_drag_end (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
MooIconView *iconview);
|
|
|
|
static void drag_data_received (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
GtkSelectionData *data,
|
|
guint info,
|
|
guint time,
|
|
GtkWidget *view);
|
|
static gboolean drag_drop (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
GtkWidget *view);
|
|
static void drag_leave (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
guint time,
|
|
GtkWidget *view);
|
|
static gboolean drag_motion (GtkWidget *view,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
MooFileView *fileview);
|
|
|
|
static gboolean moo_file_view_drop (MooFileView *fileview,
|
|
const char *path,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time);
|
|
static gboolean moo_file_view_drop_data_received
|
|
(MooFileView *fileview,
|
|
const char *path,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
GtkSelectionData *data,
|
|
guint info,
|
|
guint time);
|
|
static void cancel_drop_open (MooFileView *fileview);
|
|
|
|
static void button_drag_leave (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
guint time,
|
|
GtkWidget *button);
|
|
static gboolean button_drag_motion (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
GtkWidget *button);
|
|
static void sync_dest_targets (MooFileView *fileview);
|
|
|
|
static void moo_file_view_drop_uris (MooFileView *fileview,
|
|
char **uris,
|
|
const char *destdir,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time);
|
|
static gboolean moo_file_view_drop_text (MooFileView *fileview,
|
|
const char *text,
|
|
const char *destdir,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
gboolean *delete);
|
|
|
|
|
|
static void file_list_selection_changed (MooFileView *file_view,
|
|
MooTreeView *view);
|
|
static gboolean file_list_button_press (MooFileView *fileview,
|
|
GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
MooTreeView *view);
|
|
static void file_list_row_activated (MooFileView *fileview,
|
|
GtkTreePath *filter_path);
|
|
|
|
static Clipboard *clipboard_new (MooFolder *folder,
|
|
GList *files,
|
|
gboolean cut);
|
|
static void clipboard_free (Clipboard *cb);
|
|
static void file_view_clear_clipboard (MooFileView *fileview);
|
|
|
|
static void copy_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir);
|
|
static void move_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir);
|
|
static void link_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir);
|
|
|
|
static void file_view_cut_clipboard (MooFileView *fileview);
|
|
static void file_view_copy_clipboard (MooFileView *fileview);
|
|
static void file_view_paste_clipboard (MooFileView *fileview);
|
|
|
|
|
|
/* MOO_TYPE_FILE_VIEW */
|
|
G_DEFINE_TYPE (MooFileView, moo_file_view, GTK_TYPE_VBOX)
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CURRENT_DIRECTORY,
|
|
PROP_HOME_DIRECTORY,
|
|
PROP_FILTER_MGR,
|
|
PROP_BOOKMARK_MGR,
|
|
PROP_SORT_CASE_SENSITIVE,
|
|
PROP_TYPEAHEAD_CASE_SENSITIVE,
|
|
PROP_COMPLETION_CASE_SENSITIVE,
|
|
PROP_SHOW_HIDDEN_FILES,
|
|
PROP_SHOW_PARENT_FOLDER,
|
|
/* Aux properties */
|
|
PROP_HAS_SELECTION,
|
|
PROP_CAN_GO_BACK,
|
|
PROP_CAN_GO_FORWARD
|
|
};
|
|
|
|
enum {
|
|
CHDIR,
|
|
ACTIVATE,
|
|
POPULATE_POPUP,
|
|
GO_UP,
|
|
GO_BACK,
|
|
GO_FORWARD,
|
|
GO_HOME,
|
|
FOCUS_TO_FILTER_ENTRY,
|
|
FOCUS_TO_FILE_VIEW,
|
|
TOGGLE_SHOW_HIDDEN,
|
|
DELETE_TO_CURSOR,
|
|
PROPERTIES_DIALOG,
|
|
DELETE_SELECTED,
|
|
CREATE_FOLDER,
|
|
DROP,
|
|
DROP_DATA_RECEIVED,
|
|
CUT_CLIPBOARD,
|
|
COPY_CLIPBOARD,
|
|
PASTE_CLIPBOARD,
|
|
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;
|
|
|
|
moo_file_view_clipboard = gdk_atom_intern ("MOO_FILE_VIEW_CLIPBOARD", FALSE);
|
|
|
|
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;
|
|
|
|
widget_class->hide = moo_file_view_hide;
|
|
widget_class->popup_menu = moo_file_view_popup_menu;
|
|
widget_class->unrealize = moo_file_view_unrealize;
|
|
|
|
klass->chdir = moo_file_view_chdir_real;
|
|
klass->drop = moo_file_view_drop;
|
|
klass->drop_data_received = moo_file_view_drop_data_received;
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_HAS_SELECTION,
|
|
g_param_spec_boolean ("has-selection",
|
|
"has-selection", "has-selection",
|
|
FALSE, G_PARAM_READABLE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_CAN_GO_BACK,
|
|
g_param_spec_boolean ("can-go-back",
|
|
"can-go-back", "can-go-back",
|
|
FALSE, G_PARAM_READABLE));
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_CAN_GO_FORWARD,
|
|
g_param_spec_boolean ("can-go-forward",
|
|
"can-go-forward", "can-go-forward",
|
|
FALSE, G_PARAM_READABLE));
|
|
|
|
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_HOME_DIRECTORY,
|
|
g_param_spec_string ("home-directory",
|
|
"home-directory",
|
|
"home-directory",
|
|
#ifndef __WIN32__
|
|
g_get_home_dir (),
|
|
#else
|
|
"C:\\",
|
|
#endif
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_FILTER_MGR,
|
|
g_param_spec_object ("filter-mgr",
|
|
"filter-mgr",
|
|
"filter-mgr",
|
|
MOO_TYPE_FILTER_MGR,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_BOOKMARK_MGR,
|
|
g_param_spec_object ("bookmark-mgr",
|
|
"bookmark-mgr",
|
|
"bookmark-mgr",
|
|
MOO_TYPE_BOOKMARK_MGR,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_TYPEAHEAD_CASE_SENSITIVE,
|
|
g_param_spec_boolean ("typeahead-case-sensitive",
|
|
"typeahead-case-sensitive",
|
|
"typeahead-case-sensitive",
|
|
TYPEAHEAD_CASE_SENSITIVE_DEFAULT,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SORT_CASE_SENSITIVE,
|
|
g_param_spec_boolean ("sort-case-sensitive",
|
|
"sort-case-sensitive",
|
|
"sort-case-sensitive",
|
|
SORT_CASE_SENSITIVE_DEFAULT,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_COMPLETION_CASE_SENSITIVE,
|
|
g_param_spec_boolean ("completion-case-sensitive",
|
|
"completion-case-sensitive",
|
|
"completion-case-sensitive",
|
|
COMPLETION_CASE_SENSITIVE_DEFAULT,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SHOW_HIDDEN_FILES,
|
|
g_param_spec_boolean ("show-hidden-files",
|
|
"show-hidden-files",
|
|
"show-hidden-files",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SHOW_PARENT_FOLDER,
|
|
g_param_spec_boolean ("show-parent-folder",
|
|
"show-parent-folder",
|
|
"show-parent-folder",
|
|
FALSE,
|
|
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] =
|
|
g_signal_new ("activate",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_STRUCT_OFFSET (MooFileViewClass, activate),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
signals[POPULATE_POPUP] =
|
|
g_signal_new ("populate-popup",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (MooFileViewClass, populate_popup),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__POINTER_OBJECT,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_POINTER,
|
|
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);
|
|
|
|
signals[DELETE_TO_CURSOR] =
|
|
moo_signal_new_cb ("delete-to-cursor",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (path_entry_delete_to_cursor),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[DELETE_SELECTED] =
|
|
moo_signal_new_cb ("delete-selected",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (file_view_delete_selected),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[CREATE_FOLDER] =
|
|
moo_signal_new_cb ("create-folder",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (file_view_create_folder),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[PROPERTIES_DIALOG] =
|
|
moo_signal_new_cb ("properties-dialog",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (file_view_properties_dialog),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[DROP] =
|
|
g_signal_new ("drop",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (MooFileViewClass, drop),
|
|
g_signal_accumulator_true_handled, NULL,
|
|
_moo_marshal_BOOLEAN__STRING_OBJECT_OBJECT_INT_INT_UINT,
|
|
G_TYPE_BOOLEAN, 6,
|
|
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
|
|
GTK_TYPE_WIDGET,
|
|
GDK_TYPE_DRAG_CONTEXT,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT,
|
|
G_TYPE_UINT);
|
|
|
|
signals[DROP_DATA_RECEIVED] =
|
|
g_signal_new ("drop-data-received",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (MooFileViewClass, drop_data_received),
|
|
g_signal_accumulator_true_handled, NULL,
|
|
_moo_marshal_BOOLEAN__STRING_OBJECT_OBJECT_INT_INT_POINTER_UINT_UINT,
|
|
G_TYPE_BOOLEAN, 8,
|
|
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
|
|
GTK_TYPE_WIDGET,
|
|
GDK_TYPE_DRAG_CONTEXT,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT,
|
|
G_TYPE_POINTER,
|
|
G_TYPE_UINT,
|
|
G_TYPE_UINT);
|
|
|
|
signals[CUT_CLIPBOARD] =
|
|
moo_signal_new_cb ("cut-clipboard",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (file_view_cut_clipboard),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[COPY_CLIPBOARD] =
|
|
moo_signal_new_cb ("copy-clipboard",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (file_view_copy_clipboard),
|
|
NULL, NULL,
|
|
_moo_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[PASTE_CLIPBOARD] =
|
|
moo_signal_new_cb ("paste-clipboard",
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
G_CALLBACK (file_view_paste_clipboard),
|
|
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_u, GDK_CONTROL_MASK,
|
|
"delete-to-cursor", 0);
|
|
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_Return, GDK_MOD1_MASK,
|
|
"properties-dialog", 0);
|
|
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_Delete, GDK_MOD1_MASK,
|
|
"delete-selected", 0);
|
|
|
|
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);
|
|
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_c, GDK_CONTROL_MASK,
|
|
"copy-clipboard", 0);
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_Insert, GDK_CONTROL_MASK,
|
|
"copy-clipboard", 0);
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_x, GDK_CONTROL_MASK,
|
|
"cut-clipboard", 0);
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_Delete, GDK_SHIFT_MASK,
|
|
"cut-clipboard", 0);
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_v, GDK_CONTROL_MASK,
|
|
"paste-clipboard", 0);
|
|
gtk_binding_entry_add_signal (binding_set,
|
|
GDK_Insert, GDK_SHIFT_MASK,
|
|
"paste-clipboard", 0);
|
|
}
|
|
|
|
|
|
static void moo_file_view_init (MooFileView *fileview)
|
|
{
|
|
fileview->priv = g_new0 (MooFileViewPrivate, 1);
|
|
fileview->priv->show_hidden_files = FALSE;
|
|
fileview->priv->file_view_type = fileview->priv->view_type = MOO_FILE_VIEW_ICON;
|
|
fileview->priv->use_current_filter = FALSE;
|
|
fileview->priv->icon_size = GTK_ICON_SIZE_MENU;
|
|
|
|
fileview->priv->typeahead_case_sensitive = TYPEAHEAD_CASE_SENSITIVE_DEFAULT;
|
|
fileview->priv->sort_case_sensitive = SORT_CASE_SENSITIVE_DEFAULT;
|
|
fileview->priv->completion_case_sensitive = COMPLETION_CASE_SENSITIVE_DEFAULT;
|
|
|
|
history_init (fileview);
|
|
|
|
fileview->priv->model = g_object_new (MOO_TYPE_FOLDER_MODEL,
|
|
"sort-case-sensitive",
|
|
fileview->priv->sort_case_sensitive,
|
|
NULL);
|
|
g_signal_connect_swapped (fileview->priv->model, "row-inserted",
|
|
G_CALLBACK (file_added), fileview);
|
|
fileview->priv->filter_model =
|
|
moo_folder_filter_new (MOO_FOLDER_MODEL (fileview->priv->model));
|
|
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (fileview->priv->filter_model),
|
|
(GtkTreeModelFilterVisibleFunc) filter_visible_func,
|
|
fileview, NULL);
|
|
|
|
fileview->priv->file_system = moo_file_system_create ();
|
|
|
|
fileview->priv->targets = gtk_target_list_new (dest_targets, G_N_ELEMENTS (dest_targets));
|
|
gtk_target_list_add_text_targets (fileview->priv->targets, TARGET_TEXT);
|
|
|
|
init_gui (fileview);
|
|
path_entry_init (fileview);
|
|
}
|
|
|
|
|
|
static void moo_file_view_finalize (GObject *object)
|
|
{
|
|
MooFileView *fileview = MOO_FILE_VIEW (object);
|
|
|
|
path_entry_deinit (fileview);
|
|
|
|
if (fileview->priv->select_file_idle)
|
|
g_source_remove (fileview->priv->select_file_idle);
|
|
g_free (fileview->priv->select_file);
|
|
|
|
g_object_unref (fileview->priv->model);
|
|
g_object_unref (fileview->priv->filter_model);
|
|
history_free (fileview);
|
|
|
|
if (fileview->priv->bookmark_mgr)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (fileview->priv->bookmark_mgr,
|
|
(gpointer) bookmark_activate,
|
|
fileview);
|
|
moo_bookmark_mgr_remove_user (fileview->priv->bookmark_mgr,
|
|
fileview);
|
|
g_object_unref (fileview->priv->bookmark_mgr);
|
|
}
|
|
|
|
if (fileview->priv->filter_mgr)
|
|
g_object_unref (fileview->priv->filter_mgr);
|
|
if (fileview->priv->current_filter)
|
|
g_object_unref (fileview->priv->current_filter);
|
|
|
|
if (fileview->priv->temp_visible)
|
|
g_string_free (fileview->priv->temp_visible, TRUE);
|
|
|
|
if (fileview->priv->current_dir)
|
|
g_object_unref (fileview->priv->current_dir);
|
|
g_object_unref (fileview->priv->file_system);
|
|
|
|
g_free (fileview->priv->home_dir);
|
|
|
|
g_object_unref (fileview->priv->actions);
|
|
g_object_unref (fileview->priv->ui_xml);
|
|
|
|
gtk_target_list_unref (fileview->priv->targets);
|
|
g_slist_free (fileview->priv->drag_dest_widgets);
|
|
|
|
g_free (fileview->priv);
|
|
fileview->priv = NULL;
|
|
|
|
G_OBJECT_CLASS (moo_file_view_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_hide (GtkWidget *widget)
|
|
{
|
|
cancel_drop_open (MOO_FILE_VIEW (widget));
|
|
GTK_WIDGET_CLASS(moo_file_view_parent_class)->hide (widget);
|
|
}
|
|
|
|
|
|
GtkWidget *moo_file_view_new (void)
|
|
{
|
|
return GTK_WIDGET (g_object_new (MOO_TYPE_FILE_VIEW, NULL));
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_set_current_dir (MooFileView *fileview,
|
|
MooFolder *folder)
|
|
{
|
|
GtkTreeIter filter_iter;
|
|
char *path;
|
|
|
|
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;
|
|
|
|
cancel_drop_open (fileview);
|
|
|
|
if (fileview->priv->temp_visible)
|
|
{
|
|
g_string_free (fileview->priv->temp_visible, TRUE);
|
|
fileview->priv->temp_visible = NULL;
|
|
}
|
|
|
|
if (!folder)
|
|
{
|
|
if (fileview->priv->current_dir)
|
|
{
|
|
g_object_unref (fileview->priv->current_dir);
|
|
fileview->priv->current_dir = NULL;
|
|
moo_folder_model_set_folder (MOO_FOLDER_MODEL (fileview->priv->model),
|
|
NULL);
|
|
}
|
|
|
|
history_clear (fileview);
|
|
path_entry_set_text (fileview, "");
|
|
g_object_set (MOO_FILE_ENTRY (fileview->priv->entry),
|
|
"current-dir", NULL, NULL);
|
|
g_object_notify (G_OBJECT (fileview), "current-directory");
|
|
return;
|
|
}
|
|
|
|
if (fileview->priv->current_dir)
|
|
g_object_unref (fileview->priv->current_dir);
|
|
|
|
fileview->priv->current_dir = g_object_ref (folder);
|
|
moo_folder_model_set_folder (MOO_FOLDER_MODEL (fileview->priv->model),
|
|
folder);
|
|
|
|
if (gtk_tree_model_get_iter_first (fileview->priv->filter_model, &filter_iter))
|
|
file_view_move_selection (fileview, &filter_iter);
|
|
|
|
if (gtk_widget_is_focus (GTK_WIDGET (fileview->priv->entry)))
|
|
focus_to_file_view (fileview);
|
|
|
|
path = g_filename_display_name (moo_folder_get_path (folder));
|
|
path_entry_set_text (fileview, path);
|
|
g_free (path);
|
|
|
|
g_object_set (MOO_FILE_ENTRY(fileview->priv->entry)->completion,
|
|
"current-dir", moo_folder_get_path (folder), NULL);
|
|
|
|
history_add (fileview, moo_folder_get_path (folder));
|
|
g_object_notify (G_OBJECT (fileview), "current-directory");
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
view_files (fileview);
|
|
|
|
moo_file_view_set_current_dir (fileview, folder);
|
|
g_object_unref (folder);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
init_actions (MooFileView *fileview)
|
|
{
|
|
MooAction *action;
|
|
|
|
fileview->priv->actions = moo_action_group_new ("File Selector");
|
|
fileview->priv->ui_xml = moo_ui_xml_new ();
|
|
moo_ui_xml_add_ui_from_string (fileview->priv->ui_xml,
|
|
MOO_FILE_VIEW_UI, -1);
|
|
|
|
moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "GoUp",
|
|
"label", "Parent Folder",
|
|
"tooltip", "Parent Folder",
|
|
"icon-stock-id", GTK_STOCK_GO_UP,
|
|
"accel", "<alt>Up",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-signal", "go-up",
|
|
NULL);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "GoBack",
|
|
"label", "Go Back",
|
|
"tooltip", "Go Back",
|
|
"icon-stock-id", GTK_STOCK_GO_BACK,
|
|
"accel", "<alt>Left",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-signal", "go-back",
|
|
NULL);
|
|
moo_bind_bool_property (action, "sensitive", fileview, "can-go-back", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "GoForward",
|
|
"label", "Go Forward",
|
|
"tooltip", "Go Forward",
|
|
"icon-stock-id", GTK_STOCK_GO_FORWARD,
|
|
"accel", "<alt>Right",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-signal", "go-forward",
|
|
NULL);
|
|
moo_bind_bool_property (action, "sensitive", fileview, "can-go-forward", FALSE);
|
|
|
|
moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "GoHome",
|
|
"label", "Home Folder",
|
|
"tooltip", "Home Folder",
|
|
"icon-stock-id", GTK_STOCK_HOME,
|
|
"accel", "<alt>Home",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-signal", "go-home",
|
|
NULL);
|
|
|
|
moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "NewFolder",
|
|
"label", "New Folder...",
|
|
"tooltip", "New Folder...",
|
|
"icon-stock-id", GTK_STOCK_DIRECTORY,
|
|
"closure-object", fileview,
|
|
"closure-callback", file_view_create_folder,
|
|
NULL);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "Delete",
|
|
"label", "Delete...",
|
|
"tooltip", "Delete...",
|
|
"icon-stock-id", GTK_STOCK_DELETE,
|
|
"accel", "<alt>Delete",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-callback", file_view_delete_selected,
|
|
NULL);
|
|
moo_bind_bool_property (action, "sensitive", fileview, "has-selection", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"action-type::", MOO_TYPE_TOGGLE_ACTION,
|
|
"id", "ShowHiddenFiles",
|
|
"label", "Show Hidden Files",
|
|
"tooltip", "Show Hidden Files",
|
|
"accel", "<alt><shift>H",
|
|
"force-accel-label", TRUE,
|
|
NULL);
|
|
moo_sync_bool_property (action, "active", fileview, "show-hidden-files", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"action-type::", MOO_TYPE_TOGGLE_ACTION,
|
|
"id", "ShowParentFolder",
|
|
"label", "Show Parent Folder",
|
|
"tooltip", "Show Parent Folder",
|
|
NULL);
|
|
moo_sync_bool_property (action, "active", fileview, "show-parent-folder", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"action-type::", MOO_TYPE_TOGGLE_ACTION,
|
|
"id", "CaseSensitiveSort",
|
|
"label", "Case Sensitive Sort",
|
|
"tooltip", "Case Sensitive Sort",
|
|
NULL);
|
|
moo_sync_bool_property (action, "active", fileview, "sort-case-sensitive", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "Properties",
|
|
"label", "Properties",
|
|
"tooltip", "Properties",
|
|
"icon-stock-id", GTK_STOCK_PROPERTIES,
|
|
"accel", "<alt>Return",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-callback", file_view_properties_dialog,
|
|
NULL);
|
|
moo_bind_bool_property (action, "sensitive", fileview, "has-selection", FALSE);
|
|
|
|
moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "BookmarksMenu",
|
|
"label", "Bookmarks",
|
|
"tooltip", "Bookmarks",
|
|
"icon-stock-id", GTK_STOCK_ABOUT,
|
|
"closure-object", fileview,
|
|
"closure-callback", view_bookmarks,
|
|
NULL);
|
|
|
|
moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "AddBookmark",
|
|
"label", "Add Bookmark",
|
|
"tooltip", "Add Bookmark",
|
|
"icon-stock-id", GTK_STOCK_ADD,
|
|
"closure-object", fileview,
|
|
"closure-callback", add_bookmark,
|
|
NULL);
|
|
|
|
moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "EditBookmarks",
|
|
"label", "Edit Bookmarks...",
|
|
"tooltip", "Edit Bookmarks...",
|
|
"icon-stock-id", GTK_STOCK_EDIT,
|
|
"closure-object", fileview,
|
|
"closure-callback", edit_bookmarks,
|
|
NULL);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "Cut",
|
|
"label", "Cut",
|
|
"tooltip", "Cut",
|
|
"icon-stock-id", GTK_STOCK_CUT,
|
|
"accel", "<control>X",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-callback", file_view_cut_clipboard,
|
|
NULL);
|
|
moo_bind_bool_property (action, "sensitive", fileview, "has-selection", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "Copy",
|
|
"label", "Copy",
|
|
"tooltip", "Copy",
|
|
"icon-stock-id", GTK_STOCK_COPY,
|
|
"force-accel-label", TRUE,
|
|
"accel", "<control>C",
|
|
"closure-object", fileview,
|
|
"closure-callback", file_view_copy_clipboard,
|
|
NULL);
|
|
moo_bind_bool_property (action, "sensitive", fileview, "has-selection", FALSE);
|
|
|
|
action = moo_action_group_add_action (fileview->priv->actions,
|
|
"id", "Paste",
|
|
"label", "Paste",
|
|
"tooltip", "Paste",
|
|
"icon-stock-id", GTK_STOCK_PASTE,
|
|
"accel", "<control>V",
|
|
"force-accel-label", TRUE,
|
|
"closure-object", fileview,
|
|
"closure-callback", file_view_paste_clipboard,
|
|
NULL);
|
|
}
|
|
|
|
|
|
MooUIXML*
|
|
moo_file_view_get_ui_xml (MooFileView *fileview)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FILE_VIEW (fileview), NULL);
|
|
return fileview->priv->ui_xml;
|
|
}
|
|
|
|
|
|
MooActionGroup*
|
|
moo_file_view_get_actions (MooFileView *fileview)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FILE_VIEW (fileview), NULL);
|
|
return fileview->priv->actions;
|
|
}
|
|
|
|
|
|
static void
|
|
init_gui (MooFileView *fileview)
|
|
{
|
|
GtkBox *box;
|
|
GtkWidget *toolbar, *notebook, *filter_combo, *entry;
|
|
|
|
init_actions (fileview);
|
|
|
|
box = GTK_BOX (fileview);
|
|
toolbar = create_toolbar (fileview);
|
|
|
|
if (toolbar)
|
|
{
|
|
gtk_widget_show (toolbar);
|
|
gtk_box_pack_start (box, toolbar, FALSE, FALSE, 0);
|
|
}
|
|
else
|
|
{
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
}
|
|
|
|
entry = moo_file_entry_new ();
|
|
g_object_set_data (G_OBJECT (entry), "moo-file-view", fileview);
|
|
gtk_widget_show (entry);
|
|
gtk_box_pack_start (box, entry, FALSE, FALSE, 0);
|
|
fileview->priv->entry = GTK_ENTRY (entry);
|
|
|
|
notebook = create_notebook (fileview);
|
|
gtk_widget_show (notebook);
|
|
gtk_box_pack_start (box, notebook, TRUE, TRUE, 0);
|
|
fileview->priv->notebook = GTK_NOTEBOOK (notebook);
|
|
|
|
filter_combo = create_filter_combo (fileview);
|
|
gtk_widget_show (filter_combo);
|
|
gtk_box_pack_start (box, filter_combo, FALSE, FALSE, 0);
|
|
|
|
switch (fileview->priv->view_type)
|
|
{
|
|
case MOO_FILE_VIEW_ICON:
|
|
fileview->priv->file_view_type = MOO_FILE_VIEW_ICON;
|
|
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
|
|
ICONVIEW_PAGE);
|
|
moo_tree_view_set_active (fileview->priv->view,
|
|
GTK_WIDGET (fileview->priv->iconview));
|
|
break;
|
|
|
|
case MOO_FILE_VIEW_LIST:
|
|
fileview->priv->file_view_type = MOO_FILE_VIEW_LIST;
|
|
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
|
|
TREEVIEW_PAGE);
|
|
moo_tree_view_set_active (fileview->priv->view,
|
|
GTK_WIDGET (fileview->priv->treeview));
|
|
|
|
case MOO_FILE_VIEW_BOOKMARK:
|
|
fileview->priv->file_view_type = MOO_FILE_VIEW_ICON;
|
|
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
|
|
BOOKMARK_PAGE);
|
|
}
|
|
|
|
focus_to_file_view (fileview);
|
|
}
|
|
|
|
|
|
static void
|
|
focus_to_file_view (MooFileView *fileview)
|
|
{
|
|
gtk_widget_grab_focus (get_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)
|
|
{
|
|
if (fileview->priv->view_type == type)
|
|
return;
|
|
|
|
cancel_drop_open (fileview);
|
|
|
|
switch (type)
|
|
{
|
|
case MOO_FILE_VIEW_LIST:
|
|
fileview->priv->file_view_type = MOO_FILE_VIEW_LIST;
|
|
moo_tree_view_set_active (fileview->priv->view,
|
|
GTK_WIDGET (fileview->priv->treeview));
|
|
gtk_notebook_set_current_page (fileview->priv->notebook,
|
|
TREEVIEW_PAGE);
|
|
break;
|
|
|
|
case MOO_FILE_VIEW_ICON:
|
|
fileview->priv->file_view_type = MOO_FILE_VIEW_ICON;
|
|
moo_tree_view_set_active (fileview->priv->view,
|
|
GTK_WIDGET (fileview->priv->iconview));
|
|
gtk_notebook_set_current_page (fileview->priv->notebook,
|
|
ICONVIEW_PAGE);
|
|
break;
|
|
|
|
case MOO_FILE_VIEW_BOOKMARK:
|
|
fileview->priv->file_view_type = fileview->priv->view_type;
|
|
gtk_notebook_set_current_page (fileview->priv->notebook,
|
|
BOOKMARK_PAGE);
|
|
break;
|
|
}
|
|
|
|
fileview->priv->view_type = type;
|
|
}
|
|
|
|
|
|
static void
|
|
setup_button_drag_dest (MooFileView *fileview,
|
|
const char *xml_path,
|
|
const char *sig_name)
|
|
{
|
|
GtkWidget *button;
|
|
|
|
button = moo_ui_xml_get_widget (fileview->priv->ui_xml,
|
|
fileview->toolbar, xml_path);
|
|
|
|
g_return_if_fail (button != NULL);
|
|
|
|
g_object_set_data_full (G_OBJECT (button), "moo-fileview-signal",
|
|
g_strdup (sig_name), g_free);
|
|
|
|
gtk_drag_dest_set (button, 0, NULL, 0,
|
|
GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
|
|
gtk_drag_dest_set_target_list (button, fileview->priv->targets);
|
|
fileview->priv->drag_dest_widgets =
|
|
g_slist_prepend (fileview->priv->drag_dest_widgets, button);
|
|
|
|
g_signal_connect_swapped (button, "drag-motion",
|
|
G_CALLBACK (button_drag_motion), fileview);
|
|
g_signal_connect_swapped (button, "drag-leave",
|
|
G_CALLBACK (button_drag_leave), fileview);
|
|
}
|
|
|
|
static GtkWidget*
|
|
create_toolbar (MooFileView *fileview)
|
|
{
|
|
GtkToolbar *toolbar;
|
|
|
|
toolbar = moo_ui_xml_create_widget (fileview->priv->ui_xml,
|
|
MOO_UI_TOOLBAR,
|
|
"MooFileView/Toolbar",
|
|
fileview->priv->actions,
|
|
NULL);
|
|
g_return_val_if_fail (toolbar != NULL, NULL);
|
|
fileview->toolbar = GTK_WIDGET (toolbar);
|
|
|
|
gtk_toolbar_set_tooltips (toolbar, TRUE);
|
|
gtk_toolbar_set_style (toolbar, GTK_TOOLBAR_ICONS);
|
|
gtk_toolbar_set_icon_size (toolbar, GTK_ICON_SIZE_MENU);
|
|
|
|
setup_button_drag_dest (fileview, "MooFileView/Toolbar/GoUp", "go-up");
|
|
setup_button_drag_dest (fileview, "MooFileView/Toolbar/GoBack", "go-back");
|
|
setup_button_drag_dest (fileview, "MooFileView/Toolbar/GoForward", "go-forward");
|
|
setup_button_drag_dest (fileview, "MooFileView/Toolbar/GoHome", "go-home");
|
|
|
|
return fileview->toolbar;
|
|
}
|
|
|
|
|
|
static void
|
|
create_tree_view_proxy (MooFileView *fileview)
|
|
{
|
|
fileview->priv->view = moo_tree_view_new (fileview->priv->filter_model);
|
|
|
|
g_signal_connect_swapped (fileview->priv->view, "key-press-event",
|
|
G_CALLBACK (moo_file_view_key_press),
|
|
fileview);
|
|
g_signal_connect_swapped (fileview->priv->view, "selection-changed",
|
|
G_CALLBACK (file_list_selection_changed), fileview);
|
|
g_signal_connect_swapped (fileview->priv->view, "button-press-event",
|
|
G_CALLBACK (file_list_button_press), fileview);
|
|
g_signal_connect_swapped (fileview->priv->view, "row-activated",
|
|
G_CALLBACK (file_list_row_activated), fileview);
|
|
}
|
|
|
|
|
|
static GtkWidget *
|
|
create_notebook (MooFileView *fileview)
|
|
{
|
|
GtkWidget *notebook, *swin, *treeview, *iconview, *bkview;
|
|
|
|
notebook = gtk_notebook_new ();
|
|
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
|
|
|
|
create_tree_view_proxy (fileview);
|
|
|
|
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);
|
|
/* gtk+ #313719 */
|
|
g_signal_connect_swapped (treeview, "popup-menu",
|
|
G_CALLBACK (moo_file_view_popup_menu),
|
|
fileview);
|
|
moo_tree_view_add (fileview->priv->view, treeview);
|
|
|
|
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);
|
|
moo_tree_view_add (fileview->priv->view, iconview);
|
|
|
|
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);
|
|
bkview = create_bookmark_view (fileview);
|
|
gtk_widget_show (bkview);
|
|
gtk_container_add (GTK_CONTAINER (swin), bkview);
|
|
fileview->priv->bkview = MOO_BOOKMARK_VIEW (bkview);
|
|
|
|
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 = moo_combo_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 = MOO_COMBO (combo);
|
|
fileview->priv->filter_entry = GTK_ENTRY (MOO_COMBO(combo)->entry);
|
|
|
|
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 (MOO_COMBO(combo)->entry, "activate",
|
|
G_CALLBACK (filter_entry_activate),
|
|
fileview);
|
|
|
|
return hbox;
|
|
}
|
|
|
|
|
|
static GtkWidget *create_treeview (MooFileView *fileview)
|
|
{
|
|
GtkWidget *treeview;
|
|
GtkTreeViewColumn *column;
|
|
GtkTreeSelection *selection;
|
|
GtkCellRenderer *cell;
|
|
|
|
treeview = gtk_tree_view_new ();
|
|
|
|
#if 0
|
|
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview),
|
|
GDK_CONTROL_MASK,
|
|
source_targets,
|
|
G_N_ELEMENTS (source_targets),
|
|
GDK_ACTION_COPY | GDK_ACTION_MOVE |
|
|
GDK_ACTION_LINK);
|
|
#endif
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
|
|
gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
|
|
|
|
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_cell_data_func (column, cell,
|
|
(GtkTreeCellDataFunc) icon_data_func,
|
|
fileview, NULL);
|
|
|
|
cell = gtk_cell_renderer_text_new ();
|
|
gtk_tree_view_column_pack_start (column, cell, TRUE);
|
|
gtk_tree_view_column_set_cell_data_func (column, cell,
|
|
(GtkTreeCellDataFunc) name_data_func,
|
|
fileview, NULL);
|
|
|
|
#ifdef USE_SIZE_AND_STUFF
|
|
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_cell_data_func (column, cell,
|
|
(GtkTreeCellDataFunc) size_data_func,
|
|
fileview, 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_cell_data_func (column, cell,
|
|
(GtkTreeCellDataFunc) date_data_func,
|
|
fileview, NULL);
|
|
#endif
|
|
|
|
return treeview;
|
|
}
|
|
|
|
|
|
static void
|
|
file_list_selection_changed (MooFileView *fileview,
|
|
MooTreeView *view)
|
|
{
|
|
gboolean has_selection;
|
|
|
|
if (fileview->priv->select_file_idle)
|
|
g_source_remove (fileview->priv->select_file_idle);
|
|
fileview->priv->select_file_idle = 0;
|
|
g_free (fileview->priv->select_file);
|
|
fileview->priv->select_file = NULL;
|
|
|
|
has_selection = !moo_tree_view_selection_is_empty (view);
|
|
|
|
if (fileview->priv->has_selection != has_selection)
|
|
{
|
|
fileview->priv->has_selection = has_selection;
|
|
g_object_notify (G_OBJECT (fileview), "has-selection");
|
|
}
|
|
}
|
|
|
|
|
|
static GtkWidget*
|
|
create_iconview (MooFileView *fileview)
|
|
{
|
|
GtkWidget *iconview;
|
|
GtkCellRenderer *cell;
|
|
|
|
iconview = moo_icon_view_new ();
|
|
moo_icon_view_set_selection_mode (MOO_ICON_VIEW (iconview),
|
|
GTK_SELECTION_MULTIPLE);
|
|
|
|
moo_icon_view_set_cell_data_func (MOO_ICON_VIEW (iconview),
|
|
MOO_ICON_VIEW_CELL_PIXBUF,
|
|
(MooIconCellDataFunc) icon_data_func,
|
|
fileview, NULL);
|
|
moo_icon_view_set_cell_data_func (MOO_ICON_VIEW (iconview),
|
|
MOO_ICON_VIEW_CELL_TEXT,
|
|
(MooIconCellDataFunc) name_data_func,
|
|
fileview, 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);
|
|
|
|
moo_icon_view_enable_drag_source (MOO_ICON_VIEW (iconview),
|
|
GDK_BUTTON1_MASK,
|
|
source_targets,
|
|
G_N_ELEMENTS (source_targets),
|
|
GDK_ACTION_ASK | GDK_ACTION_COPY |
|
|
GDK_ACTION_MOVE | GDK_ACTION_LINK);
|
|
moo_icon_view_enable_drag_dest (MOO_ICON_VIEW (iconview), NULL, 0,
|
|
GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
|
|
moo_icon_view_set_dest_targets (MOO_ICON_VIEW (iconview),
|
|
fileview->priv->targets);
|
|
|
|
g_signal_connect_swapped (iconview, "drag-begin",
|
|
G_CALLBACK (icon_drag_begin),
|
|
fileview);
|
|
g_signal_connect_swapped (iconview, "drag-data-get",
|
|
G_CALLBACK (icon_drag_data_get),
|
|
fileview);
|
|
g_signal_connect_swapped (iconview, "drag-end",
|
|
G_CALLBACK (icon_drag_end),
|
|
fileview);
|
|
g_signal_connect_swapped (iconview, "drag-data-received",
|
|
G_CALLBACK (drag_data_received),
|
|
fileview);
|
|
g_signal_connect_swapped (iconview, "drag-drop",
|
|
G_CALLBACK (drag_drop),
|
|
fileview);
|
|
g_signal_connect_swapped (iconview, "drag-leave",
|
|
G_CALLBACK (drag_leave),
|
|
fileview);
|
|
g_signal_connect_after (iconview, "drag-motion",
|
|
G_CALLBACK (drag_motion),
|
|
fileview);
|
|
|
|
return iconview;
|
|
}
|
|
|
|
|
|
static GtkWidget *
|
|
create_bookmark_view (MooFileView *fileview)
|
|
{
|
|
GtkWidget *bkview;
|
|
|
|
bkview = moo_bookmark_view_new (NULL);
|
|
|
|
// gtk_tree_view_enable_drag_source (GTK_TREE_VIEW (bkview),
|
|
// GDK_BUTTON1_MASK,
|
|
// source_targets,
|
|
// G_N_ELEMENTS (source_targets),
|
|
// GDK_ACTION_ASK | GDK_ACTION_COPY |
|
|
// GDK_ACTION_MOVE | GDK_ACTION_LINK);
|
|
gtk_drag_dest_set (bkview, 0, NULL, 0,
|
|
GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
|
|
gtk_drag_dest_set_target_list (bkview, fileview->priv->targets);
|
|
|
|
g_signal_connect_swapped (bkview, "drag-data-received",
|
|
G_CALLBACK (drag_data_received),
|
|
fileview);
|
|
g_signal_connect_swapped (bkview, "drag-drop",
|
|
G_CALLBACK (drag_drop),
|
|
fileview);
|
|
g_signal_connect_swapped (bkview, "drag-leave",
|
|
G_CALLBACK (drag_leave),
|
|
fileview);
|
|
g_signal_connect_after (bkview, "drag-motion",
|
|
G_CALLBACK (drag_motion),
|
|
fileview);
|
|
|
|
g_signal_connect_swapped (bkview, "bookmark-activated",
|
|
G_CALLBACK (bookmark_activated), fileview);
|
|
|
|
return bkview;
|
|
}
|
|
|
|
|
|
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_name (file), ".."))
|
|
{
|
|
if (!ignore_two_dots)
|
|
return fileview->priv->show_two_dots;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ignore_hidden && MOO_FILE_IS_HIDDEN (file))
|
|
return fileview->priv->show_hidden_files;
|
|
|
|
if (MOO_FILE_IS_DIR (file))
|
|
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_name (file);
|
|
filter_info.uri = NULL;
|
|
filter_info.display_name = moo_file_display_name (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 &&
|
|
!strncmp (moo_file_name (file),
|
|
fileview->priv->temp_visible->str,
|
|
fileview->priv->temp_visible->len))
|
|
{
|
|
visible = moo_file_view_check_visible (fileview, file, TRUE, TRUE);
|
|
}
|
|
else
|
|
{
|
|
visible = moo_file_view_check_visible (fileview, file, FALSE, FALSE);
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
return visible;
|
|
}
|
|
|
|
|
|
static void icon_data_func (G_GNUC_UNUSED GObject *column_or_iconview,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
MooFileView *fileview)
|
|
{
|
|
MooFile *file = NULL;
|
|
GdkPixbuf *pixbuf = NULL;
|
|
|
|
gtk_tree_model_get (model, iter, COLUMN_FILE, &file, -1);
|
|
|
|
if (file)
|
|
pixbuf = moo_file_get_icon (file, GTK_WIDGET (fileview),
|
|
fileview->priv->icon_size);
|
|
|
|
g_object_set (cell, "pixbuf", pixbuf, NULL);
|
|
moo_file_unref (file);
|
|
}
|
|
|
|
|
|
static void name_data_func (G_GNUC_UNUSED GObject *column_or_iconview,
|
|
GtkCellRenderer *cell,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
G_GNUC_UNUSED MooFileView *fileview)
|
|
{
|
|
MooFile *file = NULL;
|
|
const char *name = NULL;
|
|
|
|
gtk_tree_model_get (model, iter, COLUMN_FILE, &file, -1);
|
|
|
|
if (file)
|
|
name = moo_file_display_name (file);
|
|
|
|
g_object_set (cell, "text", name, NULL);
|
|
moo_file_unref (file);
|
|
}
|
|
|
|
|
|
#ifdef USE_SIZE_AND_STUFF
|
|
static void date_data_func (G_GNUC_UNUSED GObject *column_or_iconview,
|
|
G_GNUC_UNUSED GtkCellRenderer *cell,
|
|
G_GNUC_UNUSED GtkTreeModel *model,
|
|
G_GNUC_UNUSED GtkTreeIter *iter,
|
|
G_GNUC_UNUSED MooFileView *fileview)
|
|
{
|
|
}
|
|
|
|
static void size_data_func (G_GNUC_UNUSED GObject *column_or_iconview,
|
|
G_GNUC_UNUSED GtkCellRenderer *cell,
|
|
G_GNUC_UNUSED GtkTreeModel *model,
|
|
G_GNUC_UNUSED GtkTreeIter *iter,
|
|
G_GNUC_UNUSED MooFileView *fileview)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/* TODO */
|
|
static void
|
|
moo_file_view_go_up (MooFileView *fileview)
|
|
{
|
|
MooFolder *parent = NULL;
|
|
|
|
if (fileview->priv->entry_state)
|
|
stop_path_entry (fileview, TRUE);
|
|
|
|
if (fileview->priv->view_type == MOO_FILE_VIEW_BOOKMARK)
|
|
{
|
|
view_files (fileview);
|
|
return;
|
|
}
|
|
|
|
if (fileview->priv->current_dir)
|
|
parent = moo_folder_get_parent (fileview->priv->current_dir,
|
|
MOO_FILE_HAS_ICON);
|
|
if (!parent)
|
|
parent = moo_file_system_get_root_folder (fileview->priv->file_system,
|
|
MOO_FILE_HAS_ICON);
|
|
|
|
g_return_if_fail (parent != NULL);
|
|
|
|
if (parent != fileview->priv->current_dir)
|
|
{
|
|
const char *path = moo_folder_get_path (parent);
|
|
char *name = g_path_get_basename (
|
|
moo_folder_get_path (fileview->priv->current_dir));
|
|
|
|
if (moo_file_view_chdir (fileview, path, NULL))
|
|
moo_file_view_select_name (fileview, name);
|
|
|
|
g_free (name);
|
|
}
|
|
|
|
g_object_unref (parent);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_go_home (MooFileView *fileview)
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (fileview->priv->entry_state)
|
|
stop_path_entry (fileview, TRUE);
|
|
|
|
if (!moo_file_view_chdir (fileview, fileview->priv->home_dir, &error))
|
|
{
|
|
g_warning ("%s: could not go home", G_STRLOC);
|
|
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
file_list_row_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_name (file), ".."))
|
|
{
|
|
g_signal_emit_by_name (fileview, "go-up");
|
|
}
|
|
else if (MOO_FILE_IS_DIR (file))
|
|
{
|
|
GError *error = NULL;
|
|
|
|
if (!moo_file_view_chdir (fileview, moo_file_name (file), &error))
|
|
{
|
|
g_warning ("%s: could not go into '%s'",
|
|
G_STRLOC, moo_file_name (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_name (file), NULL);
|
|
g_signal_emit (fileview, signals[ACTIVATE], 0, path);
|
|
g_free (path);
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
}
|
|
|
|
|
|
struct _History {
|
|
GSList *back;
|
|
GSList *fwd;
|
|
char *current;
|
|
guint block;
|
|
};
|
|
|
|
|
|
static void
|
|
history_init (MooFileView *fileview)
|
|
{
|
|
fileview->priv->history = g_new0 (History, 1);
|
|
}
|
|
|
|
|
|
static void
|
|
history_clear (MooFileView *fileview)
|
|
{
|
|
g_slist_foreach (fileview->priv->history->back, (GFunc) g_free, NULL);
|
|
g_slist_foreach (fileview->priv->history->fwd, (GFunc) g_free, NULL);
|
|
g_slist_free (fileview->priv->history->back);
|
|
g_slist_free (fileview->priv->history->fwd);
|
|
fileview->priv->history->back = NULL;
|
|
fileview->priv->history->fwd = NULL;
|
|
g_free (fileview->priv->history->current);
|
|
fileview->priv->history->current = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
history_free (MooFileView *fileview)
|
|
{
|
|
history_clear (fileview);
|
|
g_free (fileview->priv->history);
|
|
fileview->priv->history = NULL;
|
|
}
|
|
|
|
|
|
static const char*
|
|
history_get (MooFileView *fileview,
|
|
int where)
|
|
{
|
|
History *hist = fileview->priv->history;
|
|
|
|
g_assert (where == 1 || where == -1);
|
|
|
|
if (where == 1)
|
|
{
|
|
if (!hist->fwd)
|
|
return NULL;
|
|
else
|
|
return hist->fwd->data;
|
|
}
|
|
else
|
|
{
|
|
if (!hist->back)
|
|
return NULL;
|
|
else
|
|
return hist->back->data;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
history_go (MooFileView *fileview,
|
|
int where)
|
|
{
|
|
History *hist = fileview->priv->history;
|
|
char *tmp;
|
|
GSList **pop_from, **push_to;
|
|
gboolean could_go_forward, could_go_back;
|
|
gboolean can_go_forward, can_go_back;
|
|
|
|
g_assert (where == 1 || where == -1);
|
|
g_assert (hist->current != NULL);
|
|
|
|
could_go_forward = hist->fwd != NULL;
|
|
could_go_back = hist->back != NULL;
|
|
|
|
if (where == 1)
|
|
{
|
|
if (!hist->fwd)
|
|
return;
|
|
pop_from = &hist->fwd;
|
|
push_to = &hist->back;
|
|
}
|
|
else
|
|
{
|
|
if (!hist->back)
|
|
return;
|
|
pop_from = &hist->back;
|
|
push_to = &hist->fwd;
|
|
}
|
|
|
|
tmp = (*pop_from)->data;
|
|
*pop_from = g_slist_delete_link (*pop_from, *pop_from);
|
|
*push_to = g_slist_prepend (*push_to, hist->current);
|
|
hist->current = tmp;
|
|
|
|
can_go_forward = hist->fwd != NULL;
|
|
can_go_back = hist->back != NULL;
|
|
|
|
g_object_freeze_notify (G_OBJECT (fileview));
|
|
if (could_go_forward != can_go_forward)
|
|
g_object_notify (G_OBJECT (fileview), "can-go-forward");
|
|
if (could_go_back != can_go_back)
|
|
g_object_notify (G_OBJECT (fileview), "can-go-back");
|
|
g_object_thaw_notify (G_OBJECT (fileview));
|
|
}
|
|
|
|
|
|
static void
|
|
history_add (MooFileView *fileview,
|
|
const char *dirname)
|
|
{
|
|
History *hist = fileview->priv->history;
|
|
gboolean could_go_forward = FALSE, could_go_back, can_go_back;
|
|
|
|
g_return_if_fail (dirname != NULL);
|
|
|
|
if (hist->block)
|
|
return;
|
|
|
|
if (hist->current && !strcmp (dirname, hist->current))
|
|
return;
|
|
|
|
if (hist->fwd)
|
|
{
|
|
could_go_forward = TRUE;
|
|
g_slist_foreach (hist->fwd, (GFunc) g_free, NULL);
|
|
g_slist_free (hist->fwd);
|
|
hist->fwd = NULL;
|
|
}
|
|
|
|
could_go_back = hist->back != NULL;
|
|
|
|
if (hist->current)
|
|
{
|
|
hist->back = g_slist_prepend (hist->back, hist->current);
|
|
hist->current = g_strdup (dirname);
|
|
}
|
|
else
|
|
{
|
|
hist->current = g_strdup (dirname);
|
|
}
|
|
|
|
can_go_back = hist->back != NULL;
|
|
|
|
g_object_freeze_notify (G_OBJECT (fileview));
|
|
if (could_go_back != can_go_back)
|
|
g_object_notify (G_OBJECT (fileview), "can-go-back");
|
|
if (could_go_forward)
|
|
g_object_notify (G_OBJECT (fileview), "can-go-forward");
|
|
g_object_thaw_notify (G_OBJECT (fileview));
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_go (MooFileView *fileview,
|
|
int where)
|
|
{
|
|
const char *dir;
|
|
GError *error = NULL;
|
|
|
|
g_assert (where == 1 || where == -1);
|
|
|
|
if (fileview->priv->entry_state)
|
|
stop_path_entry (fileview, TRUE);
|
|
|
|
if (fileview->priv->view_type == MOO_FILE_VIEW_BOOKMARK)
|
|
{
|
|
view_files (fileview);
|
|
return;
|
|
}
|
|
|
|
dir = history_get (fileview, where);
|
|
|
|
if (dir)
|
|
{
|
|
fileview->priv->history->block++;
|
|
|
|
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);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
history_go (fileview, where);
|
|
}
|
|
|
|
fileview->priv->history->block--;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_go_back (MooFileView *fileview)
|
|
{
|
|
moo_file_view_go (fileview, -1);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_go_forward (MooFileView *fileview)
|
|
{
|
|
moo_file_view_go (fileview, 1);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MooFileView *fileview = MOO_FILE_VIEW (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CURRENT_DIRECTORY:
|
|
moo_file_view_chdir (fileview, g_value_get_string (value), NULL);
|
|
break;
|
|
|
|
case PROP_HOME_DIRECTORY:
|
|
g_free (fileview->priv->home_dir);
|
|
fileview->priv->home_dir = g_strdup (g_value_get_string (value));
|
|
g_object_notify (object, "home-directory");
|
|
break;
|
|
|
|
case PROP_FILTER_MGR:
|
|
moo_file_view_set_filter_mgr (fileview, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_BOOKMARK_MGR:
|
|
moo_file_view_set_bookmark_mgr (fileview, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_SORT_CASE_SENSITIVE:
|
|
moo_file_view_set_sort_case_sensitive (fileview, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_TYPEAHEAD_CASE_SENSITIVE:
|
|
moo_file_view_set_typeahead_case_sensitive (fileview, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_COMPLETION_CASE_SENSITIVE:
|
|
g_object_set (MOO_FILE_ENTRY(fileview->priv->entry)->completion,
|
|
"case-sensitive", g_value_get_boolean (value), NULL);
|
|
g_object_notify (object, "completion-case-sensitive");
|
|
break;
|
|
|
|
case PROP_SHOW_HIDDEN_FILES:
|
|
moo_file_view_set_show_hidden (fileview, g_value_get_boolean (value));
|
|
break;
|
|
|
|
case PROP_SHOW_PARENT_FOLDER:
|
|
moo_file_view_set_show_parent (fileview, g_value_get_boolean (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);
|
|
gboolean val;
|
|
|
|
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_HOME_DIRECTORY:
|
|
g_value_set_string (value, fileview->priv->home_dir);
|
|
break;
|
|
|
|
case PROP_FILTER_MGR:
|
|
g_value_set_object (value, fileview->priv->filter_mgr);
|
|
break;
|
|
|
|
case PROP_BOOKMARK_MGR:
|
|
g_value_set_object (value, fileview->priv->bookmark_mgr);
|
|
break;
|
|
|
|
case PROP_SORT_CASE_SENSITIVE:
|
|
g_value_set_boolean (value, fileview->priv->sort_case_sensitive);
|
|
break;
|
|
|
|
case PROP_TYPEAHEAD_CASE_SENSITIVE:
|
|
g_value_set_boolean (value, fileview->priv->typeahead_case_sensitive);
|
|
break;
|
|
|
|
case PROP_COMPLETION_CASE_SENSITIVE:
|
|
g_object_get (MOO_FILE_ENTRY(fileview->priv->entry)->completion,
|
|
"case-sensitive", &val, NULL);
|
|
g_value_set_boolean (value, val);
|
|
break;
|
|
|
|
case PROP_HAS_SELECTION:
|
|
if (fileview->priv->current_dir)
|
|
g_value_set_boolean (value,
|
|
!moo_tree_view_selection_is_empty (fileview->priv->view));
|
|
else
|
|
g_value_set_boolean (value, FALSE);
|
|
break;
|
|
|
|
case PROP_CAN_GO_BACK:
|
|
g_value_set_boolean (value, history_get (fileview, -1) != NULL);
|
|
break;
|
|
|
|
case PROP_CAN_GO_FORWARD:
|
|
g_value_set_boolean (value, history_get (fileview, 1) != NULL);
|
|
break;
|
|
|
|
case PROP_SHOW_HIDDEN_FILES:
|
|
g_value_set_boolean (value, fileview->priv->show_hidden_files);
|
|
break;
|
|
|
|
case PROP_SHOW_PARENT_FOLDER:
|
|
g_value_set_boolean (value, fileview->priv->show_two_dots);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Clipboard
|
|
*/
|
|
|
|
enum {
|
|
CB_TARGET_CLIPBOARD = 1,
|
|
CB_TARGET_URI_LIST = 2
|
|
};
|
|
|
|
static GtkTargetEntry clipboard_targets[] = {
|
|
{(char*) "MOO_FILE_VIEW_CLIPBOARD", GTK_TARGET_SAME_APP, CB_TARGET_CLIPBOARD},
|
|
{(char*) "text/uri-list", 0, CB_TARGET_URI_LIST},
|
|
};
|
|
|
|
static void
|
|
moo_file_view_unrealize (GtkWidget *widget)
|
|
{
|
|
file_view_clear_clipboard (MOO_FILE_VIEW (widget));
|
|
GTK_WIDGET_CLASS(moo_file_view_parent_class)->unrealize (widget);
|
|
}
|
|
|
|
|
|
static Clipboard *
|
|
clipboard_new (MooFolder *folder,
|
|
GList *files,
|
|
gboolean cut)
|
|
{
|
|
Clipboard *cb;
|
|
|
|
g_return_val_if_fail (MOO_IS_FOLDER (folder), NULL);
|
|
|
|
cb = g_new0 (Clipboard, 1);
|
|
cb->folder = g_object_ref (folder);
|
|
cb->files = g_list_copy (files);
|
|
g_list_foreach (cb->files, (GFunc) moo_file_ref, NULL);
|
|
cb->cut = cut != 0;
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
static void
|
|
clipboard_free (Clipboard *cb)
|
|
{
|
|
if (cb)
|
|
{
|
|
g_object_unref (cb->folder);
|
|
g_list_foreach (cb->files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (cb->files);
|
|
g_free (cb);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
file_view_clear_clipboard (MooFileView *fileview)
|
|
{
|
|
if (fileview->priv->clipboard)
|
|
{
|
|
GtkClipboard *cb = gtk_widget_get_clipboard (GTK_WIDGET (fileview),
|
|
GDK_SELECTION_CLIPBOARD);
|
|
|
|
if (gtk_clipboard_get_owner (cb) == G_OBJECT (fileview))
|
|
gtk_clipboard_clear (cb);
|
|
|
|
if (fileview->priv->clipboard)
|
|
{
|
|
clipboard_free (fileview->priv->clipboard);
|
|
fileview->priv->clipboard = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
clear_clipboard_cb (G_GNUC_UNUSED GtkClipboard *clipboard,
|
|
MooFileView *fileview)
|
|
{
|
|
clipboard_free (fileview->priv->clipboard);
|
|
fileview->priv->clipboard = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
set_selection_data_from_clipboard (GtkSelectionData *data,
|
|
Clipboard *cb)
|
|
{
|
|
GList *l;
|
|
char **uris;
|
|
int n_uris, i;
|
|
|
|
n_uris = g_list_length (cb->files);
|
|
g_return_if_fail (n_uris > 0);
|
|
|
|
uris = g_new0 (char*, n_uris + 1);
|
|
|
|
for (i = 0, l = cb->files; l != NULL; l = l->next, ++i)
|
|
uris[i] = moo_folder_get_file_uri (cb->folder, l->data);
|
|
|
|
gtk_selection_data_set_uris (data, uris);
|
|
|
|
g_strfreev (uris);
|
|
}
|
|
|
|
|
|
static void
|
|
get_clipboard_cb (G_GNUC_UNUSED GtkClipboard *clipboard,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
MooFileView *fileview)
|
|
{
|
|
g_return_if_fail (fileview->priv->clipboard != NULL);
|
|
|
|
switch (info)
|
|
{
|
|
case CB_TARGET_CLIPBOARD:
|
|
moo_selection_data_set_pointer (selection_data,
|
|
moo_file_view_clipboard,
|
|
fileview);
|
|
break;
|
|
|
|
case CB_TARGET_URI_LIST:
|
|
set_selection_data_from_clipboard (selection_data,
|
|
fileview->priv->clipboard);
|
|
break;
|
|
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
file_view_cut_or_copy_clipboard (MooFileView *fileview,
|
|
gboolean cut)
|
|
{
|
|
GList *files;
|
|
MooFolder *folder;
|
|
gboolean result;
|
|
GtkClipboard *clipboard;
|
|
|
|
g_return_if_fail (GTK_WIDGET_REALIZED (fileview));
|
|
|
|
folder = fileview->priv->current_dir;
|
|
files = file_view_get_selected_files (fileview);
|
|
|
|
if (!folder || !files)
|
|
return;
|
|
|
|
if (fileview->priv->clipboard)
|
|
{
|
|
clipboard_free (fileview->priv->clipboard);
|
|
fileview->priv->clipboard = NULL;
|
|
}
|
|
|
|
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (fileview),
|
|
GDK_SELECTION_CLIPBOARD);
|
|
result = gtk_clipboard_set_with_owner (clipboard, clipboard_targets,
|
|
G_N_ELEMENTS (clipboard_targets),
|
|
(GtkClipboardGetFunc) get_clipboard_cb,
|
|
(GtkClipboardClearFunc) clear_clipboard_cb,
|
|
G_OBJECT (fileview));
|
|
|
|
if (result)
|
|
fileview->priv->clipboard = clipboard_new (folder, files, cut);
|
|
|
|
g_list_foreach (files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (files);
|
|
}
|
|
|
|
|
|
static void
|
|
file_view_cut_clipboard (MooFileView *fileview)
|
|
{
|
|
file_view_cut_or_copy_clipboard (fileview, TRUE);
|
|
}
|
|
|
|
|
|
static void
|
|
file_view_copy_clipboard (MooFileView *fileview)
|
|
{
|
|
file_view_cut_or_copy_clipboard (fileview, FALSE);
|
|
}
|
|
|
|
|
|
static void
|
|
file_view_paste_clipboard (MooFileView *fileview)
|
|
{
|
|
GtkClipboard *clipboard;
|
|
GtkSelectionData *data = NULL;
|
|
|
|
g_return_if_fail (fileview->priv->current_dir != NULL);
|
|
|
|
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (fileview),
|
|
GDK_SELECTION_CLIPBOARD);
|
|
|
|
data = gtk_clipboard_wait_for_contents (clipboard, moo_file_view_clipboard);
|
|
|
|
if (data)
|
|
{
|
|
Clipboard *cb;
|
|
MooFileView *remote;
|
|
GList *filenames, *l;
|
|
const char *destdir;
|
|
|
|
remote = moo_selection_data_get_pointer (data, moo_file_view_clipboard);
|
|
g_return_if_fail (remote != NULL);
|
|
|
|
cb = remote->priv->clipboard;
|
|
|
|
if (cb->folder == fileview->priv->current_dir)
|
|
goto out;
|
|
|
|
for (filenames = NULL, l = cb->files; l != NULL; l = l->next)
|
|
{
|
|
MooFile *file = l->data;
|
|
char *path = moo_folder_get_file_path (cb->folder, file);
|
|
|
|
if (path)
|
|
filenames = g_list_prepend (filenames, path);
|
|
}
|
|
|
|
filenames = g_list_reverse (filenames);
|
|
destdir = moo_folder_get_path (fileview->priv->current_dir);
|
|
|
|
if (cb->cut)
|
|
{
|
|
remote->priv->clipboard = NULL;
|
|
gtk_clipboard_clear (clipboard);
|
|
|
|
move_files (fileview, filenames, destdir);
|
|
|
|
clipboard_free (cb);
|
|
}
|
|
else
|
|
{
|
|
copy_files (fileview, filenames, destdir);
|
|
}
|
|
|
|
g_list_foreach (filenames, (GFunc) g_free, NULL);
|
|
g_list_free (filenames);
|
|
|
|
goto out;
|
|
}
|
|
|
|
data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern ("text/uri-list", FALSE));
|
|
|
|
if (data)
|
|
{
|
|
char **uris = gtk_selection_data_get_uris (data);
|
|
GList *filenames;
|
|
char **u;
|
|
const char *destdir;
|
|
|
|
for (u = uris, filenames = NULL; u && *u; u++)
|
|
{
|
|
char *name = g_filename_from_uri (*u, NULL, NULL);
|
|
|
|
if (name)
|
|
filenames = g_list_prepend (filenames, name);
|
|
}
|
|
|
|
filenames = g_list_reverse (filenames);
|
|
destdir = moo_folder_get_path (fileview->priv->current_dir);
|
|
|
|
if (filenames)
|
|
copy_files (fileview, filenames, destdir);
|
|
|
|
g_strfreev (uris);
|
|
g_list_foreach (filenames, (GFunc) g_free, NULL);
|
|
g_list_free (filenames);
|
|
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (data)
|
|
gtk_selection_data_free (data);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Sorting and stuff
|
|
*/
|
|
|
|
void moo_file_view_set_sort_case_sensitive (MooFileView *fileview,
|
|
gboolean case_sensitive)
|
|
{
|
|
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
|
|
|
|
if (case_sensitive != fileview->priv->sort_case_sensitive)
|
|
{
|
|
fileview->priv->sort_case_sensitive = case_sensitive;
|
|
g_object_set (fileview->priv->model,
|
|
"sort-case-sensitive",
|
|
case_sensitive, NULL);
|
|
g_object_notify (G_OBJECT (fileview), "sort-case-sensitive");
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Filters
|
|
*/
|
|
|
|
static void moo_file_view_set_filter_mgr (MooFileView *fileview,
|
|
MooFilterMgr *mgr)
|
|
{
|
|
if (!mgr)
|
|
{
|
|
mgr = moo_filter_mgr_new ();
|
|
moo_file_view_set_filter_mgr (fileview, mgr);
|
|
g_object_unref (mgr);
|
|
g_object_notify (G_OBJECT (fileview), "filter-mgr");
|
|
return;
|
|
}
|
|
|
|
if (mgr == fileview->priv->filter_mgr)
|
|
return;
|
|
|
|
if (fileview->priv->filter_mgr)
|
|
g_object_unref (fileview->priv->filter_mgr);
|
|
fileview->priv->filter_mgr = g_object_ref (mgr);
|
|
|
|
init_filter_combo (fileview);
|
|
g_object_notify (G_OBJECT (fileview), "filter-mgr");
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
MooFilterMgr *mgr = fileview->priv->filter_mgr;
|
|
GtkFileFilter *filter;
|
|
|
|
block_filter_signals (fileview);
|
|
|
|
moo_filter_mgr_init_filter_combo (mgr, fileview->priv->filter_combo,
|
|
"MooFileView");
|
|
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_filter_mgr_get_last_filter (mgr, "MooFileView");
|
|
|
|
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;
|
|
|
|
/* TODO check entry content */
|
|
fileview_set_use_filter (fileview, active, TRUE);
|
|
focus_to_file_view (fileview);
|
|
}
|
|
|
|
|
|
static void filter_combo_changed (MooFileView *fileview)
|
|
{
|
|
GtkTreeIter iter;
|
|
GtkFileFilter *filter;
|
|
MooFilterMgr *mgr = fileview->priv->filter_mgr;
|
|
MooCombo *combo = fileview->priv->filter_combo;
|
|
|
|
if (!moo_combo_get_active_iter (combo, &iter))
|
|
return;
|
|
|
|
filter = moo_filter_mgr_get_filter (mgr, &iter, "MooFileView");
|
|
g_return_if_fail (filter != NULL);
|
|
moo_filter_mgr_set_last_filter (mgr, &iter, "MooFileView");
|
|
|
|
fileview_set_filter (fileview, filter);
|
|
focus_to_file_view (fileview);
|
|
}
|
|
|
|
|
|
static void filter_entry_activate (MooFileView *fileview)
|
|
{
|
|
const char *text;
|
|
GtkFileFilter *filter;
|
|
MooFilterMgr *mgr = fileview->priv->filter_mgr;
|
|
|
|
text = gtk_entry_get_text (fileview->priv->filter_entry);
|
|
|
|
if (text && text[0])
|
|
filter = moo_filter_mgr_new_user_filter (mgr, text, "MooFileView");
|
|
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_filter_mgr_get_null_filter (fileview->priv->filter_mgr);
|
|
g_return_if_fail (null_filter != NULL);
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* File manager functionality
|
|
*/
|
|
|
|
GList *
|
|
moo_file_view_get_filenames (MooFileView *fileview)
|
|
{
|
|
GList *files, *names = NULL;
|
|
MooFolder *folder;
|
|
|
|
g_return_val_if_fail (MOO_IS_FILE_VIEW (fileview), NULL);
|
|
|
|
folder = fileview->priv->current_dir;
|
|
g_return_val_if_fail (folder != NULL, NULL);
|
|
|
|
files = file_view_get_selected_files (fileview);
|
|
|
|
while (files)
|
|
{
|
|
names = g_list_prepend (names, moo_folder_get_file_path (folder, files->data));
|
|
moo_file_unref (files->data);
|
|
files = g_list_delete_link (files, files);
|
|
}
|
|
|
|
return g_list_reverse (names);
|
|
}
|
|
|
|
|
|
/* path in the filter model; result must be unrefed */
|
|
static MooFile *
|
|
file_view_get_file_at_path (MooFileView *fileview,
|
|
GtkTreePath *path)
|
|
{
|
|
GtkTreeIter iter;
|
|
MooFile *file = NULL;
|
|
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
|
|
gtk_tree_model_get_iter (fileview->priv->filter_model, &iter, path);
|
|
gtk_tree_model_get (fileview->priv->filter_model, &iter, COLUMN_FILE, &file, -1);
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
static void
|
|
prepend_file (GtkTreeModel *model,
|
|
G_GNUC_UNUSED GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gpointer user_data)
|
|
{
|
|
GList **list = user_data;
|
|
MooFile *file = NULL;
|
|
|
|
gtk_tree_model_get (model, iter, COLUMN_FILE, &file, -1);
|
|
|
|
if (file)
|
|
*list = g_list_prepend (*list, file);
|
|
}
|
|
|
|
/* returned files must be unrefed and list freed */
|
|
static GList *
|
|
file_view_get_selected_files (MooFileView *fileview)
|
|
{
|
|
GList *files = NULL;
|
|
moo_tree_view_selected_foreach (fileview->priv->view,
|
|
prepend_file, &files);
|
|
return files;
|
|
}
|
|
|
|
|
|
static void file_view_delete_selected (MooFileView *fileview)
|
|
{
|
|
GError *error = NULL;
|
|
GList *files, *l;
|
|
gboolean one;
|
|
char *message, *path;
|
|
GtkWidget *dialog;
|
|
int response;
|
|
|
|
if (!fileview->priv->current_dir)
|
|
return;
|
|
|
|
files = file_view_get_selected_files (fileview);
|
|
|
|
if (!files)
|
|
return;
|
|
|
|
one = (files->next == NULL);
|
|
|
|
if (one)
|
|
{
|
|
if (MOO_FILE_IS_DIR (files->data) && !MOO_FILE_IS_LINK (files->data))
|
|
message = g_strdup_printf ("Delete folder %s and all its content?",
|
|
moo_file_display_name (files->data));
|
|
else
|
|
message = g_strdup_printf ("Delete file %s?",
|
|
moo_file_display_name (files->data));
|
|
}
|
|
else
|
|
{
|
|
message = g_strdup ("Delete selected files?");
|
|
}
|
|
|
|
dialog = gtk_message_dialog_new (NULL,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_BUTTONS_NONE,
|
|
"%s", message);
|
|
moo_position_window (dialog, GTK_WIDGET (fileview), FALSE, FALSE, 0, 0);
|
|
|
|
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
GTK_STOCK_DELETE, GTK_RESPONSE_OK, NULL);
|
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
|
|
|
|
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
|
|
if (response == GTK_RESPONSE_OK)
|
|
{
|
|
for (l = files; l != NULL; l = l->next)
|
|
{
|
|
path = g_build_filename (moo_folder_get_path (fileview->priv->current_dir),
|
|
moo_file_name (l->data), NULL);
|
|
|
|
if (!moo_file_system_delete_file (fileview->priv->file_system, path, TRUE, &error))
|
|
{
|
|
dialog = gtk_message_dialog_new (NULL,
|
|
GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_NONE,
|
|
"Could not delete %s '%s'",
|
|
MOO_FILE_IS_DIR (l->data) ? "folder" : "file",
|
|
path);
|
|
|
|
moo_position_window (dialog, GTK_WIDGET (fileview), FALSE, FALSE, 0, 0);
|
|
|
|
if (error)
|
|
{
|
|
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
|
"%s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
gtk_dialog_add_buttons (GTK_DIALOG (dialog),
|
|
GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL);
|
|
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
|
|
g_free (path);
|
|
}
|
|
}
|
|
|
|
g_list_foreach (files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (files);
|
|
}
|
|
|
|
|
|
static void file_view_create_folder (MooFileView *fileview)
|
|
{
|
|
GError *error = NULL;
|
|
char *path, *name;
|
|
|
|
if (!fileview->priv->current_dir)
|
|
return;
|
|
|
|
name = moo_create_folder_dialog (GTK_WIDGET (fileview),
|
|
fileview->priv->current_dir);
|
|
|
|
if (!name || !name[0])
|
|
{
|
|
g_free (name);
|
|
return;
|
|
}
|
|
|
|
path = moo_file_system_make_path (fileview->priv->file_system,
|
|
moo_folder_get_path (fileview->priv->current_dir),
|
|
name, &error);
|
|
|
|
if (!path)
|
|
{
|
|
g_message ("%s: could not make path for '%s'", G_STRLOC, name);
|
|
|
|
if (error)
|
|
{
|
|
g_message ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (!moo_file_system_create_folder (fileview->priv->file_system, path, &error))
|
|
{
|
|
g_message ("%s: could not create folder '%s'", G_STRLOC, name);
|
|
|
|
if (error)
|
|
{
|
|
g_message ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
moo_file_view_select_display_name (fileview, name);
|
|
|
|
out:
|
|
g_free (path);
|
|
g_free (name);
|
|
}
|
|
|
|
|
|
/* XXX */
|
|
static void
|
|
file_view_properties_dialog (MooFileView *fileview)
|
|
{
|
|
GtkWidget *dialog;
|
|
GList *files;
|
|
|
|
if (!fileview->priv->current_dir)
|
|
return;
|
|
|
|
files = file_view_get_selected_files (fileview);
|
|
|
|
if (!files)
|
|
{
|
|
g_print ("no files\n");
|
|
return;
|
|
}
|
|
|
|
if (files->next)
|
|
{
|
|
g_print ("many files\n");
|
|
g_list_foreach (files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (files);
|
|
return;
|
|
}
|
|
|
|
dialog = g_object_get_data (G_OBJECT (fileview),
|
|
"moo-file-view-properties-dialog");
|
|
|
|
if (!dialog)
|
|
{
|
|
dialog = moo_file_props_dialog_new (GTK_WIDGET (fileview));
|
|
gtk_object_sink (GTK_OBJECT (g_object_ref (dialog)));
|
|
g_object_set_data_full (G_OBJECT (fileview),
|
|
"moo-file-view-properties-dialog",
|
|
dialog, g_object_unref);
|
|
g_signal_connect (dialog, "delete-event",
|
|
G_CALLBACK (gtk_widget_hide_on_delete), NULL);
|
|
}
|
|
|
|
moo_file_props_dialog_set_file (MOO_FILE_PROPS_DIALOG (dialog),
|
|
files->data, fileview->priv->current_dir);
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
|
|
g_list_foreach (files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (files);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Popup menu
|
|
*/
|
|
|
|
static gboolean really_destroy_menu (GtkWidget *menu)
|
|
{
|
|
g_object_unref (menu);
|
|
return FALSE;
|
|
}
|
|
|
|
static void destroy_menu (GtkWidget *menu)
|
|
{
|
|
g_idle_add ((GSourceFunc) really_destroy_menu, menu);
|
|
}
|
|
|
|
/* TODO */
|
|
static void menu_position_func (G_GNUC_UNUSED GtkMenu *menu,
|
|
gint *x,
|
|
gint *y,
|
|
gboolean *push_in,
|
|
gpointer user_data)
|
|
{
|
|
GdkWindow *window;
|
|
|
|
struct {
|
|
MooFileView *fileview;
|
|
GList *rows;
|
|
} *data = user_data;
|
|
|
|
window = GTK_WIDGET(data->fileview)->window;
|
|
gdk_window_get_origin (window, x, y);
|
|
|
|
*push_in = TRUE;
|
|
}
|
|
|
|
static void do_popup (MooFileView *fileview,
|
|
GdkEventButton *event,
|
|
GList *selected)
|
|
{
|
|
GtkWidget *menu;
|
|
GList *files = NULL, *l;
|
|
struct {
|
|
MooFileView *fileview;
|
|
GList *rows;
|
|
} position_data = {fileview, selected};
|
|
|
|
for (l = selected; l != NULL; l = l->next)
|
|
{
|
|
GtkTreeIter iter;
|
|
MooFile *file = NULL;
|
|
gtk_tree_model_get_iter (fileview->priv->filter_model, &iter, l->data);
|
|
gtk_tree_model_get (fileview->priv->filter_model, &iter,
|
|
COLUMN_FILE, &file, -1);
|
|
if (file)
|
|
files = g_list_prepend (files, file);
|
|
}
|
|
|
|
menu = moo_ui_xml_create_widget (fileview->priv->ui_xml,
|
|
MOO_UI_MENU, "MooFileView/Menubar",
|
|
fileview->priv->actions,
|
|
NULL);
|
|
gtk_object_sink (GTK_OBJECT (g_object_ref (menu)));
|
|
g_signal_connect (menu, "deactivate", G_CALLBACK (destroy_menu), NULL);
|
|
|
|
g_signal_emit (fileview, signals[POPULATE_POPUP], 0, files, menu);
|
|
|
|
if (event)
|
|
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
|
|
event->button, event->time);
|
|
else
|
|
gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
|
|
menu_position_func,
|
|
&position_data, 0,
|
|
gtk_get_current_event_time ());
|
|
|
|
g_list_foreach (files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (files);
|
|
}
|
|
|
|
|
|
static gboolean moo_file_view_popup_menu (GtkWidget *widget)
|
|
{
|
|
GList *selected;
|
|
MooFileView *fileview = MOO_FILE_VIEW (widget);
|
|
|
|
selected = moo_tree_view_get_selected_rows (fileview->priv->view);
|
|
do_popup (fileview, NULL, selected);
|
|
g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
|
|
g_list_free (selected);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
file_list_button_press (MooFileView *fileview,
|
|
G_GNUC_UNUSED GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
MooTreeView *view)
|
|
{
|
|
GtkTreePath *filter_path = NULL;
|
|
GList *selected;
|
|
|
|
if (event->button != 3)
|
|
return FALSE;
|
|
|
|
if (moo_tree_view_get_path_at_pos (view, event->x, event->y, &filter_path))
|
|
{
|
|
if (!moo_tree_view_path_is_selected (view, filter_path))
|
|
{
|
|
moo_tree_view_unselect_all (view);
|
|
moo_tree_view_set_cursor (view, filter_path, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
moo_tree_view_unselect_all (view);
|
|
}
|
|
|
|
selected = moo_tree_view_get_selected_rows (view);
|
|
do_popup (fileview, event, selected);
|
|
gtk_tree_path_free (filter_path);
|
|
g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
|
|
g_list_free (selected);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
moo_file_view_set_show_hidden (MooFileView *fileview,
|
|
gboolean show)
|
|
{
|
|
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
|
|
|
|
show = show ? TRUE : FALSE;
|
|
|
|
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));
|
|
g_object_notify (G_OBJECT (fileview), "show-hidden-files");
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
moo_file_view_set_show_parent (MooFileView *fileview,
|
|
gboolean show)
|
|
{
|
|
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
|
|
|
|
show = show ? TRUE : FALSE;
|
|
|
|
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));
|
|
g_object_notify (G_OBJECT (fileview), "show-parent-folder");
|
|
}
|
|
}
|
|
|
|
|
|
static void toggle_show_hidden (MooFileView *fileview)
|
|
{
|
|
moo_file_view_set_show_hidden (fileview,
|
|
!fileview->priv->show_hidden_files);
|
|
}
|
|
|
|
|
|
static GtkWidget *
|
|
get_view_widget (MooFileView *fileview)
|
|
{
|
|
switch (fileview->priv->view_type)
|
|
{
|
|
case MOO_FILE_VIEW_ICON:
|
|
return GTK_WIDGET(fileview->priv->iconview);
|
|
case MOO_FILE_VIEW_LIST:
|
|
return GTK_WIDGET(fileview->priv->treeview);
|
|
case MOO_FILE_VIEW_BOOKMARK:
|
|
return GTK_WIDGET(fileview->priv->bkview);
|
|
}
|
|
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Bookmarks menu
|
|
*/
|
|
|
|
static void
|
|
moo_file_view_set_bookmark_mgr (MooFileView *fileview,
|
|
MooBookmarkMgr *mgr)
|
|
{
|
|
if (!mgr)
|
|
{
|
|
mgr = moo_bookmark_mgr_new ();
|
|
moo_file_view_set_bookmark_mgr (fileview, mgr);
|
|
g_object_unref (mgr);
|
|
return;
|
|
}
|
|
|
|
if (fileview->priv->bookmark_mgr == mgr)
|
|
return;
|
|
|
|
if (fileview->priv->bookmark_mgr)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (fileview->priv->bookmark_mgr,
|
|
(gpointer) bookmark_activate,
|
|
fileview);
|
|
moo_bookmark_mgr_remove_user (fileview->priv->bookmark_mgr,
|
|
fileview);
|
|
g_object_unref (fileview->priv->bookmark_mgr);
|
|
}
|
|
|
|
fileview->priv->bookmark_mgr = g_object_ref (mgr);
|
|
moo_bookmark_mgr_add_user (fileview->priv->bookmark_mgr, fileview,
|
|
fileview->priv->actions, fileview->priv->ui_xml,
|
|
"MooFileView/Toolbar/BookmarksMenu/Bookmarks");
|
|
g_signal_connect (fileview->priv->bookmark_mgr,
|
|
"activate", G_CALLBACK (bookmark_activate),
|
|
fileview);
|
|
|
|
moo_bookmark_view_set_mgr (fileview->priv->bkview, mgr);
|
|
|
|
g_object_set_data (G_OBJECT (fileview),
|
|
"moo-file-view-bookmarks-editor",
|
|
NULL);
|
|
g_object_notify (G_OBJECT (fileview), "bookmark-mgr");
|
|
}
|
|
|
|
|
|
static void
|
|
view_bookmarks (MooFileView *fileview)
|
|
{
|
|
MooFileViewType new_type;
|
|
|
|
if (fileview->priv->view_type == MOO_FILE_VIEW_BOOKMARK)
|
|
new_type = fileview->priv->file_view_type;
|
|
else
|
|
new_type = MOO_FILE_VIEW_BOOKMARK;
|
|
|
|
moo_file_view_set_view_type (fileview, new_type);
|
|
}
|
|
|
|
static void
|
|
view_files (MooFileView *fileview)
|
|
{
|
|
if (fileview->priv->view_type == MOO_FILE_VIEW_BOOKMARK)
|
|
moo_file_view_set_view_type (fileview, fileview->priv->file_view_type);
|
|
}
|
|
|
|
|
|
static void
|
|
add_bookmark (MooFileView *fileview)
|
|
{
|
|
const char *path;
|
|
char *display_path;
|
|
MooBookmark *bookmark;
|
|
|
|
g_return_if_fail (fileview->priv->current_dir != NULL);
|
|
|
|
path = moo_folder_get_path (fileview->priv->current_dir);
|
|
display_path = g_filename_display_name (path);
|
|
bookmark = moo_bookmark_new (display_path, path,
|
|
GTK_STOCK_DIRECTORY);
|
|
|
|
moo_bookmark_mgr_add (fileview->priv->bookmark_mgr,
|
|
bookmark);
|
|
|
|
moo_bookmark_free (bookmark);
|
|
g_free (display_path);
|
|
}
|
|
|
|
|
|
static void
|
|
edit_bookmarks (MooFileView *fileview)
|
|
{
|
|
GtkWidget *dialog;
|
|
|
|
dialog = g_object_get_data (G_OBJECT (fileview),
|
|
"moo-file-view-bookmarks-editor");
|
|
|
|
if (!dialog)
|
|
{
|
|
dialog = moo_bookmark_mgr_get_editor (fileview->priv->bookmark_mgr);
|
|
gtk_object_sink (GTK_OBJECT (g_object_ref (dialog)));
|
|
g_object_set_data_full (G_OBJECT (fileview),
|
|
"moo-file-view-bookmarks-editor",
|
|
dialog, g_object_unref);
|
|
g_signal_connect (dialog, "delete-event",
|
|
G_CALLBACK (gtk_widget_hide_on_delete), NULL);
|
|
}
|
|
|
|
moo_position_window (dialog, GTK_WIDGET (fileview), FALSE, FALSE, 0, 0);
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
}
|
|
|
|
|
|
static void
|
|
bookmark_activate (G_GNUC_UNUSED MooBookmarkMgr *mgr,
|
|
MooBookmark *bookmark,
|
|
MooFileView *activated,
|
|
MooFileView *fileview)
|
|
{
|
|
if (activated != fileview)
|
|
return;
|
|
|
|
moo_file_view_chdir (fileview, bookmark->path, NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
bookmark_activated (MooFileView *fileview,
|
|
MooBookmark *bookmark)
|
|
{
|
|
g_return_if_fail (bookmark != NULL && bookmark->path != NULL);
|
|
moo_file_view_chdir (fileview, bookmark->path, NULL);
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Auxiliary stuff
|
|
*/
|
|
|
|
static void
|
|
file_view_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);
|
|
}
|
|
|
|
moo_tree_view_unselect_all (fileview->priv->view);
|
|
|
|
if (path)
|
|
{
|
|
moo_tree_view_set_cursor (fileview->priv->view, path, FALSE);
|
|
moo_tree_view_scroll_to_cell (fileview->priv->view, path);
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
|
|
static void
|
|
file_view_select_iter (MooFileView *fileview,
|
|
GtkTreeIter *iter)
|
|
{
|
|
if (iter)
|
|
{
|
|
GtkTreeIter filter_iter;
|
|
GtkTreePath *filter_path, *path;
|
|
|
|
path = gtk_tree_model_get_path (fileview->priv->model, 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 && moo_tree_view_selection_is_empty (fileview->priv->view))
|
|
{
|
|
if (gtk_tree_model_get_iter_first (fileview->priv->filter_model, &filter_iter))
|
|
file_view_move_selection (fileview, &filter_iter);
|
|
}
|
|
else
|
|
{
|
|
gtk_tree_model_get_iter (fileview->priv->filter_model,
|
|
&filter_iter, filter_path);
|
|
file_view_move_selection (fileview, &filter_iter);
|
|
gtk_tree_path_free (filter_path);
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else
|
|
{
|
|
file_view_move_selection (fileview, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
moo_file_view_select_name (MooFileView *fileview,
|
|
const char *name)
|
|
{
|
|
GtkTreeIter iter;
|
|
MooFolderModel *model;
|
|
|
|
if (!name)
|
|
{
|
|
file_view_select_iter (fileview, NULL);
|
|
return;
|
|
}
|
|
|
|
model = MOO_FOLDER_MODEL (fileview->priv->model);
|
|
|
|
if (moo_folder_model_get_iter_by_name (model, name, &iter))
|
|
{
|
|
file_view_select_iter (fileview, &iter);
|
|
return;
|
|
}
|
|
|
|
g_free (fileview->priv->select_file);
|
|
fileview->priv->select_file = g_strdup (name);
|
|
}
|
|
|
|
|
|
void
|
|
moo_file_view_select_display_name (MooFileView *fileview,
|
|
const char *name)
|
|
{
|
|
GtkTreeIter iter;
|
|
MooFolderModel *model;
|
|
|
|
if (!name)
|
|
{
|
|
file_view_select_iter (fileview, NULL);
|
|
return;
|
|
}
|
|
|
|
model = MOO_FOLDER_MODEL (fileview->priv->model);
|
|
|
|
if (moo_folder_model_get_iter_by_display_name (model, name, &iter))
|
|
{
|
|
file_view_select_iter (fileview, &iter);
|
|
return;
|
|
}
|
|
|
|
g_free (fileview->priv->select_file);
|
|
fileview->priv->select_file = g_filename_from_utf8 (name, -1, NULL, NULL, NULL);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
do_select_name (MooFileView *fileview)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
fileview->priv->select_file_idle = 0;
|
|
|
|
if (!fileview->priv->select_file)
|
|
return FALSE;
|
|
|
|
if (moo_folder_model_get_iter_by_name (MOO_FOLDER_MODEL (fileview->priv->model),
|
|
fileview->priv->select_file, &iter))
|
|
{
|
|
g_free (fileview->priv->select_file);
|
|
fileview->priv->select_file = NULL;
|
|
file_view_select_iter (fileview, &iter);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
file_added (MooFileView *fileview)
|
|
{
|
|
if (fileview->priv->select_file && !fileview->priv->select_file_idle)
|
|
fileview->priv->select_file_idle =
|
|
g_idle_add ((GSourceFunc) do_select_name, fileview);
|
|
}
|
|
|
|
|
|
static const char*
|
|
get_selected_display_name (MooFileView *fileview)
|
|
{
|
|
GtkTreeModel *model = fileview->priv->filter_model;
|
|
GtkTreePath *filter_path;
|
|
GtkTreeIter filter_iter;
|
|
const char *name;
|
|
MooFile *file = NULL;
|
|
|
|
filter_path = moo_tree_view_get_selected_path (fileview->priv->view);
|
|
|
|
if (!filter_path)
|
|
return NULL;
|
|
|
|
gtk_tree_model_get_iter (model, &filter_iter, filter_path);
|
|
gtk_tree_path_free (filter_path);
|
|
|
|
gtk_tree_model_get (model, &filter_iter, COLUMN_FILE, &file, -1);
|
|
g_return_val_if_fail (file != NULL, NULL);
|
|
name = moo_file_display_name (file);
|
|
moo_file_unref (file);
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Path entry
|
|
*/
|
|
|
|
enum {
|
|
ENTRY_STATE_NONE = 0,
|
|
ENTRY_STATE_TYPEAHEAD = 1,
|
|
ENTRY_STATE_COMPLETION = 2
|
|
};
|
|
|
|
static void typeahead_create (MooFileView *fileview);
|
|
static void typeahead_destroy (MooFileView *fileview);
|
|
static void typeahead_try (MooFileView *fileview,
|
|
gboolean need_to_refilter);
|
|
static void typeahead_tab_key (MooFileView *fileview);
|
|
static gboolean typeahead_stop_tab_cycle(MooFileView *fileview);
|
|
|
|
static void entry_changed (GtkEntry *entry,
|
|
MooFileView *fileview);
|
|
static gboolean entry_key_press (GtkEntry *entry,
|
|
GdkEventKey *event,
|
|
MooFileView *fileview);
|
|
static gboolean entry_focus_out (GtkWidget *entry,
|
|
GdkEventFocus *event,
|
|
MooFileView *fileview);
|
|
static void entry_activate (GtkEntry *entry,
|
|
MooFileView *fileview);
|
|
|
|
static gboolean entry_stop_tab_cycle (MooFileView *fileview);
|
|
static gboolean entry_tab_key (GtkEntry *entry,
|
|
MooFileView *fileview);
|
|
|
|
static gboolean looks_like_path (const char *text);
|
|
|
|
|
|
static void path_entry_init (MooFileView *fileview)
|
|
{
|
|
GtkEntry *entry = fileview->priv->entry;
|
|
|
|
/* XXX after? */
|
|
g_signal_connect (entry, "changed",
|
|
G_CALLBACK (entry_changed), fileview);
|
|
g_signal_connect (entry, "key-press-event",
|
|
G_CALLBACK (entry_key_press), fileview);
|
|
g_signal_connect (entry, "focus-out-event",
|
|
G_CALLBACK (entry_focus_out), fileview);
|
|
g_signal_connect (entry, "activate",
|
|
G_CALLBACK (entry_activate), fileview);
|
|
|
|
typeahead_create (fileview);
|
|
}
|
|
|
|
|
|
static void path_entry_deinit (MooFileView *fileview)
|
|
{
|
|
typeahead_destroy (fileview);
|
|
}
|
|
|
|
|
|
static void entry_changed (GtkEntry *entry,
|
|
MooFileView *fileview)
|
|
{
|
|
gboolean need_to_refilter = FALSE;
|
|
|
|
const char *text = gtk_entry_get_text (entry);
|
|
|
|
/*
|
|
First, decide what's going on (doesn't look like something can be
|
|
going on, but, anyway).
|
|
Then:
|
|
1) If text typed in is a beginning of some file in the list,
|
|
select that file (like treeview interactive search).
|
|
2) If text is a beginning of some hidden files (but not filtered
|
|
out, those are always ignored), show those hidden files and
|
|
select first of them.
|
|
3) Otherwise, parse text as <path>/<file> (<file> may be empty),
|
|
and do the entry completion stuff.
|
|
|
|
Tab completion doesn't interfere with this code - tab completion sets
|
|
entry text while this handler is blocked.
|
|
*/
|
|
|
|
/* Check if some file was shown temporarily, and hide it */
|
|
if (fileview->priv->temp_visible)
|
|
{
|
|
g_string_free (fileview->priv->temp_visible, TRUE);
|
|
fileview->priv->temp_visible = NULL;
|
|
need_to_refilter = TRUE;
|
|
}
|
|
|
|
if (!text[0])
|
|
{
|
|
if (need_to_refilter)
|
|
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
|
|
(fileview->priv->filter_model));
|
|
|
|
file_view_move_selection (fileview, NULL);
|
|
return;
|
|
}
|
|
|
|
/* TODO take ~file into account */
|
|
/* If entered text looks like path, do completion stuff */
|
|
if (looks_like_path (text))
|
|
{
|
|
if (need_to_refilter)
|
|
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
|
|
(fileview->priv->filter_model));
|
|
|
|
file_view_move_selection (fileview, NULL);
|
|
fileview->priv->entry_state = ENTRY_STATE_COMPLETION;
|
|
/* XXX call complete() or something, for automatic popup */
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
fileview->priv->entry_state = ENTRY_STATE_TYPEAHEAD;
|
|
return typeahead_try (fileview, need_to_refilter);
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean entry_key_press (GtkEntry *entry,
|
|
GdkEventKey *event,
|
|
MooFileView *fileview)
|
|
{
|
|
GtkWidget *filewidget;
|
|
const char *name;
|
|
|
|
if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
|
|
return FALSE;
|
|
|
|
if (event->keyval != GDK_Tab)
|
|
{
|
|
if (entry_stop_tab_cycle (fileview))
|
|
g_signal_emit_by_name (entry, "changed");
|
|
}
|
|
|
|
switch (event->keyval)
|
|
{
|
|
case GDK_Escape:
|
|
stop_path_entry (fileview, TRUE);
|
|
return TRUE;
|
|
|
|
case GDK_Up:
|
|
case GDK_KP_Up:
|
|
case GDK_Down:
|
|
case GDK_KP_Down:
|
|
filewidget = get_view_widget (fileview);
|
|
GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS (filewidget))->
|
|
key_press_event (filewidget, event);
|
|
name = get_selected_display_name (fileview);
|
|
g_return_val_if_fail (name != NULL, TRUE);
|
|
path_entry_set_text (fileview, name);
|
|
gtk_editable_set_position (GTK_EDITABLE (entry), -1);
|
|
return TRUE;
|
|
|
|
case GDK_Tab:
|
|
return entry_tab_key (entry, fileview);
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean entry_stop_tab_cycle (MooFileView *fileview)
|
|
{
|
|
switch (fileview->priv->entry_state)
|
|
{
|
|
case ENTRY_STATE_TYPEAHEAD:
|
|
return typeahead_stop_tab_cycle (fileview);
|
|
case ENTRY_STATE_COMPLETION:
|
|
fileview->priv->entry_state = 0;
|
|
return FALSE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean entry_tab_key (GtkEntry *entry,
|
|
MooFileView *fileview)
|
|
{
|
|
const char *text;
|
|
|
|
if (!fileview->priv->entry_state)
|
|
{
|
|
text = gtk_entry_get_text (entry);
|
|
|
|
if (text[0])
|
|
{
|
|
g_signal_emit_by_name (entry, "changed");
|
|
g_return_val_if_fail (fileview->priv->entry_state != 0, FALSE);
|
|
return entry_tab_key (entry, fileview);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fileview->priv->entry_state == ENTRY_STATE_TYPEAHEAD)
|
|
{
|
|
typeahead_tab_key (fileview);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void path_entry_set_text (MooFileView *fileview,
|
|
const char *text)
|
|
{
|
|
GtkEntry *entry = fileview->priv->entry;
|
|
g_signal_handlers_block_by_func (entry, (gpointer) entry_changed,
|
|
fileview);
|
|
gtk_entry_set_text (entry, text);
|
|
gtk_editable_set_position (GTK_EDITABLE (entry), -1);
|
|
g_signal_handlers_unblock_by_func (entry, (gpointer) entry_changed,
|
|
fileview);
|
|
}
|
|
|
|
|
|
static void path_entry_delete_to_cursor (MooFileView *fileview)
|
|
{
|
|
GtkEditable *entry = GTK_EDITABLE (fileview->priv->entry);
|
|
if (gtk_widget_is_focus (GTK_WIDGET (entry)))
|
|
gtk_editable_delete_text (entry, 0,
|
|
gtk_editable_get_position (entry));
|
|
}
|
|
|
|
|
|
static void entry_activate (GtkEntry *entry,
|
|
MooFileView *fileview)
|
|
{
|
|
char *filename = NULL;
|
|
GtkTreePath *selected;
|
|
|
|
selected = moo_tree_view_get_selected_path (fileview->priv->view);
|
|
|
|
if (selected)
|
|
{
|
|
GtkTreeIter iter;
|
|
MooFile *file = NULL;
|
|
gtk_tree_model_get_iter (fileview->priv->filter_model, &iter, selected);
|
|
gtk_tree_model_get (fileview->priv->filter_model, &iter, COLUMN_FILE, &file, -1);
|
|
gtk_tree_path_free (selected);
|
|
g_return_if_fail (file != NULL);
|
|
|
|
/* XXX display_name() */
|
|
filename = g_strdup (moo_file_display_name (file));
|
|
moo_file_unref (file);
|
|
}
|
|
else
|
|
{
|
|
filename = g_strdup (gtk_entry_get_text (entry));
|
|
}
|
|
|
|
stop_path_entry (fileview, TRUE);
|
|
file_view_activate_filename (fileview, filename);
|
|
g_free (filename);
|
|
}
|
|
|
|
|
|
#if 0
|
|
#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))
|
|
#else
|
|
#define PRINT_KEY_EVENT(event)
|
|
#endif
|
|
|
|
|
|
static gboolean
|
|
moo_file_view_key_press (MooFileView *fileview,
|
|
GtkWidget *widget,
|
|
GdkEventKey *event)
|
|
{
|
|
if (fileview->priv->entry_state)
|
|
{
|
|
g_warning ("%s: something wrong", G_STRLOC);
|
|
stop_path_entry (fileview, FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
/* return immediately if event doesn't look like text typed in */
|
|
|
|
if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
|
|
return FALSE;
|
|
|
|
switch (event->keyval)
|
|
{
|
|
case GDK_VoidSymbol:
|
|
case GDK_BackSpace:
|
|
case GDK_Tab:
|
|
case GDK_Linefeed:
|
|
case GDK_Clear:
|
|
case GDK_Return:
|
|
case GDK_Pause:
|
|
case GDK_Scroll_Lock:
|
|
case GDK_Sys_Req:
|
|
case GDK_Escape:
|
|
case GDK_Delete:
|
|
case GDK_Multi_key:
|
|
case GDK_Codeinput:
|
|
case GDK_SingleCandidate:
|
|
case GDK_MultipleCandidate:
|
|
case GDK_PreviousCandidate:
|
|
case GDK_Kanji:
|
|
case GDK_Muhenkan:
|
|
case GDK_Henkan_Mode:
|
|
case GDK_Romaji:
|
|
case GDK_Hiragana:
|
|
case GDK_Katakana:
|
|
case GDK_Hiragana_Katakana:
|
|
case GDK_Zenkaku:
|
|
case GDK_Hankaku:
|
|
case GDK_Zenkaku_Hankaku:
|
|
case GDK_Touroku:
|
|
case GDK_Massyo:
|
|
case GDK_Kana_Lock:
|
|
case GDK_Kana_Shift:
|
|
case GDK_Eisu_Shift:
|
|
case GDK_Eisu_toggle:
|
|
case GDK_Home:
|
|
case GDK_Left:
|
|
case GDK_Up:
|
|
case GDK_Right:
|
|
case GDK_Down:
|
|
case GDK_Page_Up:
|
|
case GDK_Page_Down:
|
|
case GDK_End:
|
|
case GDK_Begin:
|
|
case GDK_Select:
|
|
case GDK_Print:
|
|
case GDK_Execute:
|
|
case GDK_Insert:
|
|
case GDK_Undo:
|
|
case GDK_Redo:
|
|
case GDK_Menu:
|
|
case GDK_Find:
|
|
case GDK_Cancel:
|
|
case GDK_Help:
|
|
case GDK_Break:
|
|
case GDK_Mode_switch:
|
|
case GDK_Num_Lock:
|
|
case GDK_KP_Tab:
|
|
case GDK_KP_Enter:
|
|
case GDK_KP_F1:
|
|
case GDK_KP_F2:
|
|
case GDK_KP_F3:
|
|
case GDK_KP_F4:
|
|
case GDK_KP_Home:
|
|
case GDK_KP_Left:
|
|
case GDK_KP_Up:
|
|
case GDK_KP_Right:
|
|
case GDK_KP_Down:
|
|
case GDK_KP_Page_Up:
|
|
case GDK_KP_Page_Down:
|
|
case GDK_KP_End:
|
|
case GDK_KP_Begin:
|
|
case GDK_KP_Insert:
|
|
case GDK_KP_Delete:
|
|
case GDK_F1:
|
|
case GDK_F2:
|
|
case GDK_F3:
|
|
case GDK_F4:
|
|
case GDK_F5:
|
|
case GDK_F6:
|
|
case GDK_F7:
|
|
case GDK_F8:
|
|
case GDK_F9:
|
|
case GDK_F10:
|
|
case GDK_F11:
|
|
case GDK_F12:
|
|
case GDK_F13:
|
|
case GDK_F14:
|
|
case GDK_F15:
|
|
case GDK_F16:
|
|
case GDK_F17:
|
|
case GDK_F18:
|
|
case GDK_F19:
|
|
case GDK_F20:
|
|
case GDK_F21:
|
|
case GDK_F22:
|
|
case GDK_F23:
|
|
case GDK_F24:
|
|
case GDK_F25:
|
|
case GDK_F26:
|
|
case GDK_F27:
|
|
case GDK_F28:
|
|
case GDK_F29:
|
|
case GDK_F30:
|
|
case GDK_F31:
|
|
case GDK_F32:
|
|
case GDK_F33:
|
|
case GDK_F34:
|
|
case GDK_F35:
|
|
case GDK_Shift_L:
|
|
case GDK_Shift_R:
|
|
case GDK_Control_L:
|
|
case GDK_Control_R:
|
|
case GDK_Caps_Lock:
|
|
case GDK_Shift_Lock:
|
|
case GDK_Meta_L:
|
|
case GDK_Meta_R:
|
|
case GDK_Alt_L:
|
|
case GDK_Alt_R:
|
|
case GDK_Super_L:
|
|
case GDK_Super_R:
|
|
case GDK_Hyper_L:
|
|
case GDK_Hyper_R:
|
|
case GDK_ISO_Lock:
|
|
case GDK_ISO_Level2_Latch:
|
|
case GDK_ISO_Level3_Shift:
|
|
case GDK_ISO_Level3_Latch:
|
|
case GDK_ISO_Level3_Lock:
|
|
case GDK_ISO_Group_Latch:
|
|
case GDK_ISO_Group_Lock:
|
|
case GDK_ISO_Next_Group:
|
|
case GDK_ISO_Next_Group_Lock:
|
|
case GDK_ISO_Prev_Group:
|
|
case GDK_ISO_Prev_Group_Lock:
|
|
case GDK_ISO_First_Group:
|
|
case GDK_ISO_First_Group_Lock:
|
|
case GDK_ISO_Last_Group:
|
|
case GDK_ISO_Last_Group_Lock:
|
|
case GDK_ISO_Left_Tab:
|
|
case GDK_ISO_Move_Line_Up:
|
|
case GDK_ISO_Move_Line_Down:
|
|
case GDK_ISO_Partial_Line_Up:
|
|
case GDK_ISO_Partial_Line_Down:
|
|
case GDK_ISO_Partial_Space_Left:
|
|
case GDK_ISO_Partial_Space_Right:
|
|
case GDK_ISO_Set_Margin_Left:
|
|
case GDK_ISO_Set_Margin_Right:
|
|
case GDK_ISO_Release_Margin_Left:
|
|
case GDK_ISO_Release_Margin_Right:
|
|
case GDK_ISO_Release_Both_Margins:
|
|
case GDK_ISO_Fast_Cursor_Left:
|
|
case GDK_ISO_Fast_Cursor_Right:
|
|
case GDK_ISO_Fast_Cursor_Up:
|
|
case GDK_ISO_Fast_Cursor_Down:
|
|
case GDK_ISO_Continuous_Underline:
|
|
case GDK_ISO_Discontinuous_Underline:
|
|
case GDK_ISO_Emphasize:
|
|
case GDK_ISO_Center_Object:
|
|
case GDK_ISO_Enter:
|
|
case GDK_First_Virtual_Screen:
|
|
case GDK_Prev_Virtual_Screen:
|
|
case GDK_Next_Virtual_Screen:
|
|
case GDK_Last_Virtual_Screen:
|
|
case GDK_Terminate_Server:
|
|
case GDK_AccessX_Enable:
|
|
case GDK_AccessX_Feedback_Enable:
|
|
case GDK_RepeatKeys_Enable:
|
|
case GDK_SlowKeys_Enable:
|
|
case GDK_BounceKeys_Enable:
|
|
case GDK_StickyKeys_Enable:
|
|
case GDK_MouseKeys_Enable:
|
|
case GDK_MouseKeys_Accel_Enable:
|
|
case GDK_Overlay1_Enable:
|
|
case GDK_Overlay2_Enable:
|
|
case GDK_AudibleBell_Enable:
|
|
case GDK_Pointer_Left:
|
|
case GDK_Pointer_Right:
|
|
case GDK_Pointer_Up:
|
|
case GDK_Pointer_Down:
|
|
case GDK_Pointer_UpLeft:
|
|
case GDK_Pointer_UpRight:
|
|
case GDK_Pointer_DownLeft:
|
|
case GDK_Pointer_DownRight:
|
|
case GDK_Pointer_Button_Dflt:
|
|
case GDK_Pointer_Button1:
|
|
case GDK_Pointer_Button2:
|
|
case GDK_Pointer_Button3:
|
|
case GDK_Pointer_Button4:
|
|
case GDK_Pointer_Button5:
|
|
case GDK_Pointer_DblClick_Dflt:
|
|
case GDK_Pointer_DblClick1:
|
|
case GDK_Pointer_DblClick2:
|
|
case GDK_Pointer_DblClick3:
|
|
case GDK_Pointer_DblClick4:
|
|
case GDK_Pointer_DblClick5:
|
|
case GDK_Pointer_Drag_Dflt:
|
|
case GDK_Pointer_Drag1:
|
|
case GDK_Pointer_Drag2:
|
|
case GDK_Pointer_Drag3:
|
|
case GDK_Pointer_Drag4:
|
|
case GDK_Pointer_Drag5:
|
|
case GDK_Pointer_EnableKeys:
|
|
case GDK_Pointer_Accelerate:
|
|
case GDK_Pointer_DfltBtnNext:
|
|
case GDK_Pointer_DfltBtnPrev:
|
|
case GDK_3270_Duplicate:
|
|
case GDK_3270_FieldMark:
|
|
case GDK_3270_Right2:
|
|
case GDK_3270_Left2:
|
|
case GDK_3270_BackTab:
|
|
case GDK_3270_EraseEOF:
|
|
case GDK_3270_EraseInput:
|
|
case GDK_3270_Reset:
|
|
case GDK_3270_Quit:
|
|
case GDK_3270_PA1:
|
|
case GDK_3270_PA2:
|
|
case GDK_3270_PA3:
|
|
case GDK_3270_Test:
|
|
case GDK_3270_Attn:
|
|
case GDK_3270_CursorBlink:
|
|
case GDK_3270_AltCursor:
|
|
case GDK_3270_KeyClick:
|
|
case GDK_3270_Jump:
|
|
case GDK_3270_Ident:
|
|
case GDK_3270_Rule:
|
|
case GDK_3270_Copy:
|
|
case GDK_3270_Play:
|
|
case GDK_3270_Setup:
|
|
case GDK_3270_Record:
|
|
case GDK_3270_ChangeScreen:
|
|
case GDK_3270_DeleteWord:
|
|
case GDK_3270_ExSelect:
|
|
case GDK_3270_CursorSelect:
|
|
case GDK_3270_PrintScreen:
|
|
case GDK_3270_Enter:
|
|
return FALSE;
|
|
}
|
|
|
|
PRINT_KEY_EVENT (event);
|
|
|
|
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->string && event->length)
|
|
{
|
|
GdkEvent *copy;
|
|
GtkWidget *entry = GTK_WIDGET (fileview->priv->entry);
|
|
|
|
g_return_val_if_fail (event != NULL, FALSE);
|
|
g_return_val_if_fail (GTK_WIDGET_REALIZED (entry), FALSE);
|
|
|
|
copy = gdk_event_copy ((GdkEvent*) event);
|
|
g_object_unref (copy->key.window);
|
|
copy->key.window = g_object_ref (entry->window);
|
|
|
|
gtk_widget_grab_focus (entry);
|
|
|
|
path_entry_set_text (fileview, "");
|
|
gtk_widget_event (entry, copy);
|
|
|
|
gdk_event_free (copy);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean entry_focus_out (GtkWidget *entry,
|
|
G_GNUC_UNUSED GdkEventFocus *event,
|
|
MooFileView *fileview)
|
|
{
|
|
/* focus may be lost due to switching to other window, do nothing then */
|
|
if (!gtk_widget_is_focus (entry))
|
|
stop_path_entry (fileview, TRUE);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void stop_path_entry (MooFileView *fileview,
|
|
gboolean focus_file_list)
|
|
{
|
|
char *text;
|
|
|
|
typeahead_stop_tab_cycle (fileview);
|
|
|
|
fileview->priv->entry_state = 0;
|
|
|
|
if (fileview->priv->current_dir)
|
|
text = g_filename_display_name (moo_folder_get_path (fileview->priv->current_dir));
|
|
else
|
|
text = g_strdup ("");
|
|
|
|
path_entry_set_text (fileview, text);
|
|
|
|
if (focus_file_list)
|
|
focus_to_file_view (fileview);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
|
|
/* WIN32_XXX */
|
|
static void file_view_activate_filename (MooFileView *fileview,
|
|
const char *display_name)
|
|
{
|
|
GError *error = NULL;
|
|
char *dirname, *basename;
|
|
char *path = NULL;
|
|
const char *current_dir = NULL;
|
|
|
|
if (fileview->priv->current_dir)
|
|
current_dir = moo_folder_get_path (fileview->priv->current_dir);
|
|
|
|
path = moo_file_system_get_absolute_path (fileview->priv->file_system,
|
|
display_name, current_dir);
|
|
|
|
if (!path || !g_file_test (path, G_FILE_TEST_EXISTS))
|
|
{
|
|
g_free (path);
|
|
return;
|
|
}
|
|
|
|
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, path);
|
|
if (error)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
g_free (path);
|
|
return;
|
|
}
|
|
|
|
dirname = g_path_get_dirname (path);
|
|
basename = g_path_get_basename (path);
|
|
|
|
if (!dirname || !basename)
|
|
{
|
|
g_free (path);
|
|
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_name (fileview, basename);
|
|
g_signal_emit (fileview, signals[ACTIVATE], 0, path);
|
|
|
|
g_free (path);
|
|
g_free (dirname);
|
|
g_free (basename);
|
|
}
|
|
|
|
|
|
static gboolean looks_like_path (const char *text)
|
|
{
|
|
if (strchr (text, '/'))
|
|
return TRUE;
|
|
#ifdef __WIN32__
|
|
else if (strchr (text, '\\'))
|
|
return TRUE;
|
|
#endif
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
/* Typeahead
|
|
*/
|
|
|
|
struct _Typeahead {
|
|
MooFileView *fileview;
|
|
GtkEntry *entry;
|
|
GString *matched_prefix;
|
|
int matched_prefix_char_len;
|
|
TextFuncs text_funcs;
|
|
gboolean case_sensitive;
|
|
};
|
|
|
|
static gboolean typeahead_find_match_visible (MooFileView *fileview,
|
|
const char *text,
|
|
GtkTreeIter *filter_iter,
|
|
gboolean exact_match);
|
|
static gboolean typeahead_find_match_hidden (MooFileView *fileview,
|
|
const char *text,
|
|
GtkTreeIter *iter,
|
|
gboolean exact_match);
|
|
|
|
|
|
static void typeahead_try (MooFileView *fileview,
|
|
gboolean need_to_refilter)
|
|
{
|
|
const char *text;
|
|
Typeahead *stuff = fileview->priv->typeahead;
|
|
GtkEntry *entry = stuff->entry;
|
|
GtkTreeIter filter_iter, iter;
|
|
|
|
g_assert (fileview->priv->entry_state == ENTRY_STATE_TYPEAHEAD);
|
|
|
|
if (stuff->matched_prefix)
|
|
{
|
|
g_string_free (stuff->matched_prefix, TRUE);
|
|
stuff->matched_prefix = NULL;
|
|
}
|
|
|
|
text = gtk_entry_get_text (entry);
|
|
g_assert (text[0] != 0);
|
|
|
|
/* TODO windows */
|
|
|
|
if (fileview->priv->show_hidden_files || text[0] != '.' || !text[1])
|
|
{
|
|
if (need_to_refilter)
|
|
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
|
|
(fileview->priv->filter_model));
|
|
|
|
if (typeahead_find_match_visible (fileview, text, &filter_iter, FALSE))
|
|
file_view_move_selection (fileview, &filter_iter);
|
|
else
|
|
file_view_move_selection (fileview, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
/* check if partial name of hidden file was typed in */
|
|
if (typeahead_find_match_hidden (fileview, text, &iter, FALSE))
|
|
{
|
|
fileview->priv->temp_visible = g_string_new (text);
|
|
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
|
|
(fileview->priv->filter_model));
|
|
|
|
gtk_tree_model_filter_convert_child_iter_to_iter (
|
|
GTK_TREE_MODEL_FILTER (fileview->priv->filter_model),
|
|
&filter_iter, &iter);
|
|
file_view_move_selection (fileview, &filter_iter);
|
|
}
|
|
else
|
|
{
|
|
if (need_to_refilter)
|
|
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER
|
|
(fileview->priv->filter_model));
|
|
|
|
file_view_move_selection (fileview, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
static void typeahead_tab_key (MooFileView *fileview)
|
|
{
|
|
const char *text;
|
|
Typeahead *stuff = fileview->priv->typeahead;
|
|
GtkEntry *entry = stuff->entry;
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
MooFile *file = NULL;
|
|
const char *name;
|
|
|
|
g_assert (fileview->priv->entry_state == ENTRY_STATE_TYPEAHEAD);
|
|
|
|
model = fileview->priv->filter_model;
|
|
|
|
if (!gtk_tree_model_get_iter_first (model, &iter))
|
|
return;
|
|
|
|
/* see if it's cycling over matched names */
|
|
if (stuff->matched_prefix)
|
|
{
|
|
gboolean found = FALSE;
|
|
|
|
path = moo_tree_view_get_selected_path (fileview->priv->view);
|
|
|
|
/* check if there is next name in the list to jump to */
|
|
if (path && gtk_tree_model_get_iter (model, &iter, path) &&
|
|
gtk_tree_model_iter_next (model, &iter))
|
|
{
|
|
found = model_find_next_match (model, &iter,
|
|
stuff->matched_prefix->str,
|
|
stuff->matched_prefix->len,
|
|
&stuff->text_funcs,
|
|
FALSE);
|
|
}
|
|
|
|
/* if nothing found, start cycling again */
|
|
if (!found)
|
|
found = typeahead_find_match_visible (fileview,
|
|
stuff->matched_prefix->str,
|
|
&iter, FALSE);
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
if (!found)
|
|
goto error;
|
|
else
|
|
file_view_move_selection (fileview, &iter);
|
|
}
|
|
else
|
|
{
|
|
gboolean unique;
|
|
|
|
text = gtk_entry_get_text (entry);
|
|
|
|
if (!text[0])
|
|
return;
|
|
|
|
stuff->matched_prefix =
|
|
model_find_max_prefix (fileview->priv->filter_model,
|
|
text, &stuff->text_funcs, &unique, &iter);
|
|
|
|
if (!stuff->matched_prefix)
|
|
return;
|
|
|
|
stuff->matched_prefix_char_len =
|
|
g_utf8_strlen (stuff->matched_prefix->str, stuff->matched_prefix->len);
|
|
|
|
/* if match is unique and it's a directory, append a slash */
|
|
if (unique)
|
|
{
|
|
file = NULL;
|
|
gtk_tree_model_get (model, &iter, COLUMN_FILE, &file, -1);
|
|
|
|
if (!file)
|
|
goto error;
|
|
|
|
name = moo_file_display_name (file);
|
|
|
|
// if (!file || stuff->strcmp_func (stuff->matched_prefix->str, file))
|
|
// goto error;
|
|
|
|
if (MOO_FILE_IS_DIR (file))
|
|
{
|
|
char *new_text = g_strdup_printf ("%s%c", name, G_DIR_SEPARATOR);
|
|
g_string_free (stuff->matched_prefix, TRUE);
|
|
stuff->matched_prefix = NULL;
|
|
path_entry_set_text (fileview, new_text);
|
|
g_free (new_text);
|
|
moo_file_unref (file);
|
|
g_signal_emit_by_name (entry, "changed");
|
|
return;
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
file = NULL;
|
|
}
|
|
|
|
path = moo_tree_view_get_selected_path (fileview->priv->view);
|
|
|
|
if (!path && !typeahead_find_match_visible (fileview, stuff->matched_prefix->str, &iter, FALSE))
|
|
goto error;
|
|
|
|
if (path)
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
gtk_tree_model_get (model, &iter, COLUMN_FILE, &file, -1);
|
|
|
|
if (!file)
|
|
goto error;
|
|
|
|
// if (stuff->strncmp_func (stuff->matched_prefix->str, file, stuff->matched_prefix->len))
|
|
// goto error;
|
|
|
|
name = moo_file_display_name (file);
|
|
path_entry_set_text (fileview, name);
|
|
gtk_editable_select_region (GTK_EDITABLE (entry),
|
|
stuff->matched_prefix_char_len,
|
|
-1);
|
|
|
|
moo_file_unref (file);
|
|
return;
|
|
|
|
error:
|
|
if (stuff->matched_prefix)
|
|
g_string_free (stuff->matched_prefix, TRUE);
|
|
stuff->matched_prefix = NULL;
|
|
moo_file_unref (file);
|
|
g_return_if_reached ();
|
|
}
|
|
|
|
|
|
static gboolean typeahead_stop_tab_cycle(MooFileView *fileview)
|
|
{
|
|
Typeahead *stuff = fileview->priv->typeahead;
|
|
|
|
if (stuff->matched_prefix)
|
|
{
|
|
g_string_free (stuff->matched_prefix, TRUE);
|
|
stuff->matched_prefix = NULL;
|
|
stuff->matched_prefix_char_len = 0;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static void typeahead_create (MooFileView *fileview)
|
|
{
|
|
Typeahead *stuff = g_new0 (Typeahead, 1);
|
|
|
|
stuff->fileview = fileview;
|
|
stuff->entry = fileview->priv->entry;
|
|
stuff->case_sensitive = fileview->priv->typeahead_case_sensitive;
|
|
|
|
if (stuff->case_sensitive)
|
|
{
|
|
stuff->text_funcs.strcmp_func = strcmp_func;
|
|
stuff->text_funcs.strncmp_func = strncmp_func;
|
|
stuff->text_funcs.normalize_func = normalize_func;
|
|
}
|
|
else
|
|
{
|
|
stuff->text_funcs.strcmp_func = case_strcmp_func;
|
|
stuff->text_funcs.strncmp_func = case_strncmp_func;
|
|
stuff->text_funcs.normalize_func = case_normalize_func;
|
|
}
|
|
|
|
fileview->priv->typeahead = stuff;
|
|
}
|
|
|
|
|
|
static void typeahead_destroy (MooFileView *fileview)
|
|
{
|
|
Typeahead *stuff = fileview->priv->typeahead;
|
|
if (stuff->matched_prefix)
|
|
g_string_free (stuff->matched_prefix, TRUE);
|
|
g_free (stuff);
|
|
fileview->priv->typeahead = NULL;
|
|
}
|
|
|
|
|
|
void moo_file_view_set_typeahead_case_sensitive (MooFileView *fileview,
|
|
gboolean case_sensitive)
|
|
{
|
|
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
|
|
|
|
if (case_sensitive != fileview->priv->typeahead_case_sensitive)
|
|
{
|
|
Typeahead *stuff = fileview->priv->typeahead;
|
|
|
|
fileview->priv->typeahead_case_sensitive = case_sensitive;
|
|
stuff->case_sensitive = case_sensitive;
|
|
|
|
if (case_sensitive)
|
|
{
|
|
stuff->text_funcs.strcmp_func = strcmp_func;
|
|
stuff->text_funcs.strncmp_func = strncmp_func;
|
|
stuff->text_funcs.normalize_func = normalize_func;
|
|
}
|
|
else
|
|
{
|
|
stuff->text_funcs.strcmp_func = case_strcmp_func;
|
|
stuff->text_funcs.strncmp_func = case_strncmp_func;
|
|
stuff->text_funcs.normalize_func = case_normalize_func;
|
|
}
|
|
|
|
g_object_notify (G_OBJECT (fileview), "typeahead-case-sensitive");
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean typeahead_find_match_visible (MooFileView *fileview,
|
|
const char *text,
|
|
GtkTreeIter *filter_iter,
|
|
gboolean exact_match)
|
|
{
|
|
guint len;
|
|
|
|
g_return_val_if_fail (text && text[0], FALSE);
|
|
|
|
if (!gtk_tree_model_get_iter_first (fileview->priv->filter_model, filter_iter))
|
|
return FALSE;
|
|
|
|
len = strlen (text);
|
|
|
|
return model_find_next_match (fileview->priv->filter_model,
|
|
filter_iter, text, len,
|
|
&fileview->priv->typeahead->text_funcs,
|
|
exact_match);
|
|
}
|
|
|
|
|
|
static gboolean typeahead_find_match_hidden (MooFileView *fileview,
|
|
const char *text,
|
|
GtkTreeIter *iter,
|
|
gboolean exact)
|
|
{
|
|
guint len;
|
|
GtkTreeModel *model = fileview->priv->model;
|
|
|
|
g_return_val_if_fail (text && text[0], FALSE);
|
|
|
|
if (!gtk_tree_model_get_iter_first (model, iter))
|
|
return FALSE;
|
|
|
|
len = strlen (text);
|
|
|
|
while (TRUE)
|
|
{
|
|
MooFile *file = NULL;
|
|
|
|
if (!model_find_next_match (model, iter, text, len,
|
|
&fileview->priv->typeahead->text_funcs,
|
|
exact))
|
|
return FALSE;
|
|
|
|
gtk_tree_model_get (model, iter, COLUMN_FILE, &file, -1);
|
|
|
|
if (file && moo_file_view_check_visible (fileview, file, TRUE, TRUE))
|
|
{
|
|
moo_file_unref (file);
|
|
return TRUE;
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
|
|
if (!gtk_tree_model_iter_next (model, iter))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Drag source
|
|
*/
|
|
|
|
static void
|
|
icon_drag_begin (MooFileView *fileview,
|
|
G_GNUC_UNUSED GdkDragContext *context,
|
|
G_GNUC_UNUSED MooIconView *iconview)
|
|
{
|
|
GList *files, *l;
|
|
char **uris;
|
|
guint n_files, i;
|
|
MooFolder *folder;
|
|
|
|
folder = fileview->priv->current_dir;
|
|
g_return_if_fail (folder != NULL);
|
|
|
|
files = file_view_get_selected_files (fileview);
|
|
g_return_if_fail (files != NULL);
|
|
|
|
n_files = g_list_length (files);
|
|
uris = g_new0 (char*, n_files + 1);
|
|
|
|
for (l = files, i = 0; l != NULL; i++, l = l->next)
|
|
{
|
|
MooFile *file = l->data;
|
|
uris[i] = moo_folder_get_file_uri (folder, file);
|
|
}
|
|
|
|
g_object_set_data_full (G_OBJECT (fileview), "moo-file-view-source-uris",
|
|
uris, (GDestroyNotify) g_strfreev);
|
|
g_object_set_data_full (G_OBJECT (fileview), "moo-file-view-source-dir",
|
|
g_object_ref (folder), g_object_unref);
|
|
|
|
g_list_foreach (files, (GFunc) moo_file_unref, NULL);
|
|
g_list_free (files);
|
|
}
|
|
|
|
|
|
static void
|
|
icon_drag_data_get (MooFileView *fileview,
|
|
G_GNUC_UNUSED GdkDragContext *context,
|
|
GtkSelectionData *data,
|
|
G_GNUC_UNUSED guint info,
|
|
G_GNUC_UNUSED guint time,
|
|
G_GNUC_UNUSED MooIconView *iconview)
|
|
{
|
|
char **uris;
|
|
|
|
uris = g_object_get_data (G_OBJECT (fileview), "moo-file-view-source-uris");
|
|
g_return_if_fail (uris && *uris);
|
|
|
|
gtk_selection_data_set_uris (data, uris);
|
|
}
|
|
|
|
|
|
static void
|
|
icon_drag_end (MooFileView *fileview,
|
|
G_GNUC_UNUSED GdkDragContext *context,
|
|
G_GNUC_UNUSED MooIconView *iconview)
|
|
{
|
|
g_object_set_data (G_OBJECT (fileview), "moo-file-view-source-uris", NULL);
|
|
g_object_set_data (G_OBJECT (fileview), "moo-file-view-source-dir", NULL);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Drag destination
|
|
*/
|
|
|
|
#define DROP_OPEN_TIMEOUT 500
|
|
#define DROP_OPEN_BLINK_TIME 80
|
|
|
|
|
|
static gboolean
|
|
drop_open_timeout_func2 (MooFileView *fileview)
|
|
{
|
|
GtkTreeRowReference *ref;
|
|
GtkWidget *button;
|
|
|
|
ref = fileview->priv->drop_to.row;
|
|
fileview->priv->drop_to.row = NULL;
|
|
button = fileview->priv->drop_to.button;
|
|
|
|
cancel_drop_open (fileview);
|
|
|
|
if (ref)
|
|
{
|
|
MooFile *file = NULL;
|
|
const char *goto_dir = NULL;
|
|
GtkTreePath *path;
|
|
MooFolder *current_dir;
|
|
GtkTreeIter iter;
|
|
|
|
path = gtk_tree_row_reference_get_path (ref);
|
|
gtk_tree_row_reference_free (ref);
|
|
|
|
g_return_val_if_fail (path != NULL, FALSE);
|
|
|
|
if (!gtk_tree_model_get_iter (fileview->priv->filter_model, &iter, path))
|
|
{
|
|
gtk_tree_path_free (path);
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
current_dir = fileview->priv->current_dir;
|
|
g_return_val_if_fail (current_dir != NULL, FALSE);
|
|
|
|
gtk_tree_model_get (fileview->priv->filter_model,
|
|
&iter, COLUMN_FILE, &file, -1);
|
|
g_return_val_if_fail (file != NULL, FALSE);
|
|
|
|
if (!strcmp (moo_file_name (file), "..") || MOO_FILE_IS_DIR (file))
|
|
{
|
|
goto_dir = moo_file_name (file);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("%s: oops", G_STRLOC);
|
|
}
|
|
|
|
if (goto_dir && moo_file_view_chdir (fileview, goto_dir, NULL))
|
|
moo_file_view_select_name (fileview, NULL);
|
|
|
|
if (file)
|
|
moo_file_unref (file);
|
|
}
|
|
else if (button)
|
|
{
|
|
g_signal_emit_by_name (button, "clicked");
|
|
moo_file_view_select_name (fileview, NULL);
|
|
}
|
|
else
|
|
{
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
drop_open_timeout_func (MooFileView *fileview)
|
|
{
|
|
fileview->priv->drop_to.timeout = 0;
|
|
|
|
if (fileview->priv->drop_to.row)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_row_reference_get_path (fileview->priv->drop_to.row);
|
|
|
|
if (!path)
|
|
{
|
|
cancel_drop_open (fileview);
|
|
moo_icon_view_set_drag_dest_row (fileview->priv->iconview, NULL);
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
if (fileview->priv->drop_to.blink_clear)
|
|
{
|
|
fileview->priv->drop_to.blink_clear = FALSE;
|
|
moo_icon_view_set_drag_dest_row (fileview->priv->iconview, path);
|
|
|
|
if (++fileview->priv->drop_to.n_blinks > 1)
|
|
fileview->priv->drop_to.timeout =
|
|
g_timeout_add (DROP_OPEN_BLINK_TIME,
|
|
(GSourceFunc) drop_open_timeout_func2,
|
|
fileview);
|
|
}
|
|
else
|
|
{
|
|
fileview->priv->drop_to.blink_clear = TRUE;
|
|
moo_icon_view_set_drag_dest_row (fileview->priv->iconview, NULL);
|
|
}
|
|
}
|
|
else if (fileview->priv->drop_to.button)
|
|
{
|
|
if (!fileview->priv->drop_to.highlight)
|
|
{
|
|
fileview->priv->drop_to.highlight = TRUE;
|
|
gtk_drag_highlight (fileview->priv->drop_to.button);
|
|
|
|
if (++fileview->priv->drop_to.n_blinks > 1)
|
|
fileview->priv->drop_to.timeout =
|
|
g_timeout_add (DROP_OPEN_BLINK_TIME,
|
|
(GSourceFunc) drop_open_timeout_func2,
|
|
fileview);
|
|
}
|
|
else
|
|
{
|
|
fileview->priv->drop_to.highlight = FALSE;
|
|
gtk_drag_unhighlight (fileview->priv->drop_to.button);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
if (!fileview->priv->drop_to.timeout)
|
|
fileview->priv->drop_to.timeout =
|
|
g_timeout_add (DROP_OPEN_BLINK_TIME,
|
|
(GSourceFunc) drop_open_timeout_func,
|
|
fileview);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
drag_data_received (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
GtkSelectionData *data,
|
|
guint info,
|
|
guint time,
|
|
GtkWidget *widget)
|
|
{
|
|
gboolean dummy;
|
|
char *dir = NULL;
|
|
GtkTreePath *path = NULL;
|
|
MooFolder *current_dir;
|
|
|
|
cancel_drop_open (fileview);
|
|
|
|
current_dir = fileview->priv->current_dir;
|
|
|
|
if (!current_dir)
|
|
{
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
moo_file_view_drag_finish (fileview, context, FALSE, FALSE, time);
|
|
return;
|
|
}
|
|
|
|
if (moo_icon_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
|
|
{
|
|
MooFile *file = file_view_get_file_at_path (fileview, path);
|
|
|
|
if (MOO_FILE_IS_DIR (file))
|
|
{
|
|
dir = moo_folder_get_file_path (current_dir, file);
|
|
moo_icon_view_set_drag_dest_row (widget, path);
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
}
|
|
|
|
if (!dir)
|
|
dir = g_strdup (moo_folder_get_path (current_dir));
|
|
|
|
g_signal_emit (fileview, signals[DROP_DATA_RECEIVED], 0,
|
|
dir, widget, context, x, y, data, info, time, &dummy);
|
|
|
|
gtk_tree_path_free (path);
|
|
g_free (dir);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
drag_drop (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
GtkWidget *widget)
|
|
{
|
|
gboolean dummy;
|
|
|
|
char *dir = NULL;
|
|
GtkTreePath *path = NULL;
|
|
MooFolder *current_dir;
|
|
|
|
cancel_drop_open (fileview);
|
|
|
|
current_dir = fileview->priv->current_dir;
|
|
|
|
if (!current_dir)
|
|
{
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
moo_file_view_drag_finish (fileview, context, FALSE, FALSE, time);
|
|
return FALSE;
|
|
}
|
|
|
|
if (moo_icon_view_get_path_at_pos (widget, x, y, &path, NULL, NULL, NULL))
|
|
{
|
|
MooFile *file = file_view_get_file_at_path (fileview, path);
|
|
|
|
if (MOO_FILE_IS_DIR (file))
|
|
{
|
|
dir = moo_folder_get_file_path (current_dir, file);
|
|
moo_icon_view_set_drag_dest_row (widget, path);
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
}
|
|
|
|
if (!dir)
|
|
dir = g_strdup (moo_folder_get_path (current_dir));
|
|
|
|
g_signal_emit (fileview, signals[DROP], 0, dir, widget, context, x, y, time, &dummy);
|
|
|
|
gtk_tree_path_free (path);
|
|
g_free (dir);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_file_view_drop (MooFileView *fileview,
|
|
G_GNUC_UNUSED const char *path,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
G_GNUC_UNUSED int x,
|
|
G_GNUC_UNUSED int y,
|
|
guint time)
|
|
{
|
|
GdkAtom target;
|
|
|
|
target = gtk_drag_dest_find_target (widget, context, fileview->priv->targets);
|
|
|
|
if (target != GDK_NONE)
|
|
{
|
|
gtk_drag_get_data (widget, context, target, time);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_warning ("%s: oops", G_STRLOC);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_file_view_drop_data_received (MooFileView *fileview,
|
|
const char *path,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
GtkSelectionData *data,
|
|
G_GNUC_UNUSED guint info,
|
|
guint time)
|
|
{
|
|
gboolean success = FALSE;
|
|
gboolean delete = FALSE;
|
|
|
|
if (data->target == gdk_atom_intern ("text/uri-list", FALSE))
|
|
{
|
|
char **uris = gtk_selection_data_get_uris (data);
|
|
|
|
if (uris)
|
|
{
|
|
/* it has to call drag_finish in order to be able to show menu */
|
|
moo_file_view_drop_uris (fileview, uris, path, widget,
|
|
context, x, y, time);
|
|
g_strfreev (uris);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
}
|
|
|
|
g_strfreev (uris);
|
|
}
|
|
else
|
|
{
|
|
char *text = (char*) gtk_selection_data_get_text (data);
|
|
|
|
if (text)
|
|
success = moo_file_view_drop_text (fileview, text, path, widget,
|
|
context, x, y, time, &delete);
|
|
else
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
moo_file_view_drag_finish (fileview, context, success, delete, time);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
moo_file_view_drag_finish (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
gboolean success,
|
|
gboolean delete_data,
|
|
guint time)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
|
|
|
|
path = moo_icon_view_get_drag_dest_row (fileview->priv->iconview);
|
|
moo_icon_view_set_drag_dest_row (fileview->priv->iconview, NULL);
|
|
|
|
gtk_drag_finish (context, success, delete_data, time);
|
|
|
|
if (success && path)
|
|
{
|
|
moo_icon_view_unselect_all (fileview->priv->iconview);
|
|
moo_icon_view_set_cursor (fileview->priv->iconview, path, FALSE);
|
|
}
|
|
|
|
if (path)
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
|
|
static void
|
|
drag_leave (MooFileView *fileview,
|
|
G_GNUC_UNUSED GdkDragContext *context,
|
|
G_GNUC_UNUSED guint time,
|
|
GtkWidget *widget)
|
|
{
|
|
moo_icon_view_set_drag_dest_row (widget, NULL);
|
|
cancel_drop_open (fileview);
|
|
}
|
|
|
|
|
|
static void
|
|
cancel_drop_open (MooFileView *fileview)
|
|
{
|
|
if (fileview->priv->drop_to.row)
|
|
gtk_tree_row_reference_free (fileview->priv->drop_to.row);
|
|
|
|
if (fileview->priv->drop_to.timeout)
|
|
g_source_remove (fileview->priv->drop_to.timeout);
|
|
|
|
if (fileview->priv->drop_to.highlight && fileview->priv->drop_to.button)
|
|
gtk_drag_unhighlight (fileview->priv->drop_to.button);
|
|
|
|
fileview->priv->drop_to.button = NULL;
|
|
fileview->priv->drop_to.highlight = FALSE;
|
|
fileview->priv->drop_to.blink_clear = FALSE;
|
|
fileview->priv->drop_to.n_blinks = 0;
|
|
fileview->priv->drop_to.row = NULL;
|
|
fileview->priv->drop_to.timeout = 0;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
check_drop_targets (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
GtkWidget *widget)
|
|
{
|
|
GdkAtom target;
|
|
target = gtk_drag_dest_find_target (widget, context, fileview->priv->targets);
|
|
return target != GDK_NONE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
drag_motion (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
MooFileView *fileview)
|
|
{
|
|
MooFolder *source_dir;
|
|
MooFolder *current_dir;
|
|
GtkTreePath *path = NULL;
|
|
gboolean highlight_target = FALSE;
|
|
gboolean can_drop = FALSE;
|
|
int abs_x, abs_y;
|
|
|
|
if (!check_drop_targets (fileview, context, widget))
|
|
{
|
|
moo_tree_view_set_drag_dest_row (widget, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
moo_tree_view_widget_to_abs_coords (widget, x, y, &abs_x, &abs_y);
|
|
moo_tree_view_get_path_at_pos (widget, x, y, &path);
|
|
|
|
if (MOO_IS_ICON_VIEW (widget))
|
|
{
|
|
current_dir = fileview->priv->current_dir;
|
|
source_dir = g_object_get_data (G_OBJECT (fileview), "moo-file-view-source-dir");
|
|
|
|
if (!current_dir)
|
|
goto out;
|
|
|
|
if (path)
|
|
{
|
|
MooFile *file = file_view_get_file_at_path (fileview, path);
|
|
|
|
if (MOO_FILE_IS_DIR (file))
|
|
{
|
|
highlight_target = TRUE;
|
|
can_drop = TRUE;
|
|
}
|
|
|
|
moo_file_unref (file);
|
|
}
|
|
|
|
if (source_dir != current_dir)
|
|
can_drop = TRUE;
|
|
}
|
|
else if (MOO_IS_BOOKMARK_VIEW (widget))
|
|
{
|
|
highlight_target = TRUE;
|
|
can_drop = path != NULL;
|
|
}
|
|
|
|
if (highlight_target && path)
|
|
moo_tree_view_set_drag_dest_row (widget, path);
|
|
else
|
|
moo_tree_view_set_drag_dest_row (widget, NULL);
|
|
|
|
if (can_drop)
|
|
gdk_drag_status (context, context->actions & GDK_ACTION_MOVE ?
|
|
GDK_ACTION_MOVE : context->suggested_action, time);
|
|
else
|
|
gdk_drag_status (context, 0, time);
|
|
|
|
if (highlight_target)
|
|
{
|
|
gboolean new_timeout = TRUE;
|
|
|
|
if (fileview->priv->drop_to.row)
|
|
{
|
|
GtkTreePath *old_path = gtk_tree_row_reference_get_path (fileview->priv->drop_to.row);
|
|
|
|
if (old_path && !gtk_tree_path_compare (path, old_path) &&
|
|
!gtk_drag_check_threshold (widget,
|
|
fileview->priv->drop_to.x,
|
|
fileview->priv->drop_to.y,
|
|
abs_x,
|
|
abs_y))
|
|
{
|
|
new_timeout = FALSE;
|
|
g_assert (fileview->priv->drop_to.timeout != 0);
|
|
}
|
|
}
|
|
|
|
if (new_timeout)
|
|
{
|
|
cancel_drop_open (fileview);
|
|
|
|
fileview->priv->drop_to.row =
|
|
gtk_tree_row_reference_new (moo_tree_view_get_model (widget), path);
|
|
|
|
fileview->priv->drop_to.timeout =
|
|
g_timeout_add (DROP_OPEN_TIMEOUT,
|
|
(GSourceFunc) drop_open_timeout_func,
|
|
fileview);
|
|
|
|
fileview->priv->drop_to.x = abs_x;
|
|
fileview->priv->drop_to.y = abs_y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cancel_drop_open (fileview);
|
|
}
|
|
|
|
out:
|
|
if (path)
|
|
gtk_tree_path_free (path);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
button_drag_leave (MooFileView *fileview,
|
|
G_GNUC_UNUSED GdkDragContext *context,
|
|
G_GNUC_UNUSED guint time,
|
|
G_GNUC_UNUSED GtkWidget *button)
|
|
{
|
|
cancel_drop_open (fileview);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
button_drag_motion (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
G_GNUC_UNUSED int x,
|
|
G_GNUC_UNUSED int y,
|
|
guint time,
|
|
GtkWidget *button)
|
|
{
|
|
gboolean new_timeout = TRUE;
|
|
|
|
if (!check_drop_targets (fileview, context, button))
|
|
{
|
|
cancel_drop_open (fileview);
|
|
return FALSE;
|
|
}
|
|
|
|
if (fileview->priv->drop_to.button == button)
|
|
{
|
|
new_timeout = FALSE;
|
|
g_assert (fileview->priv->drop_to.timeout != 0);
|
|
}
|
|
|
|
if (new_timeout)
|
|
{
|
|
cancel_drop_open (fileview);
|
|
|
|
if (!fileview->priv->drop_to.highlight)
|
|
{
|
|
gtk_drag_highlight (button);
|
|
fileview->priv->drop_to.highlight = TRUE;
|
|
}
|
|
|
|
fileview->priv->drop_to.button = button;
|
|
fileview->priv->drop_to.timeout =
|
|
g_timeout_add (DROP_OPEN_TIMEOUT,
|
|
(GSourceFunc) drop_open_timeout_func,
|
|
fileview);
|
|
}
|
|
|
|
gdk_drag_status (context, context->suggested_action, time);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
moo_file_view_add_target (MooFileView *fileview,
|
|
GdkAtom target,
|
|
guint flags,
|
|
guint info)
|
|
{
|
|
g_return_if_fail (MOO_IS_FILE_VIEW (fileview));
|
|
gtk_target_list_add (fileview->priv->targets, target, flags, info);
|
|
sync_dest_targets (fileview);
|
|
}
|
|
|
|
|
|
static void
|
|
sync_dest_targets (MooFileView *fileview)
|
|
{
|
|
GSList *l;
|
|
|
|
moo_icon_view_set_dest_targets (fileview->priv->iconview,
|
|
fileview->priv->targets);
|
|
gtk_drag_dest_set_target_list (GTK_WIDGET (fileview->priv->bkview),
|
|
fileview->priv->targets);
|
|
|
|
for (l = fileview->priv->drag_dest_widgets; l != NULL; l = l->next)
|
|
gtk_drag_dest_set_target_list (l->data, fileview->priv->targets);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Dropping stuff
|
|
*/
|
|
|
|
static void
|
|
run_command_on_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir,
|
|
const char **first_args,
|
|
int n_first_args)
|
|
{
|
|
GError *error = NULL;
|
|
MooCmd *cmd;
|
|
char **argv;
|
|
int list_len, n_args, i;
|
|
GList *l;
|
|
|
|
g_return_if_fail (filenames != NULL);
|
|
g_return_if_fail (destdir != NULL);
|
|
g_return_if_fail (n_first_args > 0);
|
|
|
|
list_len = g_list_length (filenames);
|
|
|
|
n_args = list_len + n_first_args + 1;
|
|
argv = g_new (char*, n_args + 1);
|
|
|
|
for (i = 0; i < n_first_args; ++i)
|
|
{
|
|
g_assert (first_args[i] != NULL);
|
|
argv[i] = (char*) first_args[i];
|
|
}
|
|
|
|
argv[n_args-1] = (char*) destdir;
|
|
argv[n_args] = NULL;
|
|
|
|
for (i = 0, l = filenames; l != NULL; l = l->next, i++)
|
|
argv[n_first_args + i] = l->data;
|
|
|
|
cmd = moo_cmd_new_full (NULL, argv, NULL,
|
|
G_SPAWN_SEARCH_PATH,
|
|
MOO_CMD_STDOUT_TO_PARENT | MOO_CMD_STDERR_TO_PARENT,
|
|
NULL, NULL, &error);
|
|
|
|
if (!cmd)
|
|
{
|
|
g_critical ("%s: could not spawn '%s'",
|
|
G_STRLOC, first_args[0]);
|
|
g_critical ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
goto out;
|
|
}
|
|
|
|
if (fileview->priv->current_dir &&
|
|
!strcmp (destdir, moo_folder_get_path (fileview->priv->current_dir)) &&
|
|
list_len == 1)
|
|
{
|
|
char *basename = g_path_get_basename (filenames->data);
|
|
|
|
if (basename)
|
|
moo_file_view_select_name (fileview, basename);
|
|
|
|
g_free (basename);
|
|
}
|
|
|
|
g_signal_connect (cmd, "cmd-exit", G_CALLBACK (g_object_unref), NULL);
|
|
|
|
out:
|
|
g_free (argv);
|
|
}
|
|
|
|
|
|
static void
|
|
copy_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir)
|
|
{
|
|
const char *args[] = {"cp", "-R", "--"};
|
|
run_command_on_files (fileview, filenames, destdir,
|
|
args, G_N_ELEMENTS (args));
|
|
}
|
|
|
|
|
|
static void
|
|
move_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir)
|
|
{
|
|
const char *args[] = {"mv", "--"};
|
|
run_command_on_files (fileview, filenames, destdir,
|
|
args, G_N_ELEMENTS (args));
|
|
}
|
|
|
|
|
|
static void
|
|
link_files (MooFileView *fileview,
|
|
GList *filenames,
|
|
const char *destdir)
|
|
{
|
|
const char *args[] = {"ln", "-s", "--"};
|
|
run_command_on_files (fileview, filenames, destdir,
|
|
args, G_N_ELEMENTS (args));
|
|
}
|
|
|
|
|
|
static void
|
|
free_string_list (GList *list)
|
|
{
|
|
g_list_foreach (list, (GFunc) g_free, NULL);
|
|
g_list_free (list);
|
|
}
|
|
|
|
|
|
static void
|
|
drop_item_activated (GObject *item,
|
|
MooFileView *fileview)
|
|
{
|
|
GdkDragAction action;
|
|
gpointer data;
|
|
GList *filenames;
|
|
char *destdir;
|
|
|
|
data = g_object_get_data (item, "moo-file-view-drop-action");
|
|
filenames = g_object_get_data (item, "moo-file-view-drop-files");
|
|
destdir = g_object_get_data (item, "moo-file-view-drop-dir");
|
|
g_return_if_fail (filenames != NULL && destdir != NULL);
|
|
|
|
action = GPOINTER_TO_INT (data);
|
|
|
|
switch (action)
|
|
{
|
|
case GDK_ACTION_COPY:
|
|
copy_files (fileview, filenames, destdir);
|
|
break;
|
|
case GDK_ACTION_MOVE:
|
|
move_files (fileview, filenames, destdir);
|
|
break;
|
|
case GDK_ACTION_LINK:
|
|
link_files (fileview, filenames, destdir);
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
moo_file_view_drop_uris (MooFileView *fileview,
|
|
char **uris,
|
|
const char *destdir,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time)
|
|
{
|
|
char **u;
|
|
GList *filenames = NULL;
|
|
GError *error = NULL;
|
|
GdkModifierType mask;
|
|
GdkDragAction action;
|
|
gboolean success = FALSE;
|
|
|
|
g_assert (uris != NULL);
|
|
|
|
for (u = uris; *u; ++u)
|
|
{
|
|
char *file = g_filename_from_uri (*u, NULL, &error);
|
|
|
|
if (!file)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
goto out;
|
|
}
|
|
|
|
filenames = g_list_prepend (filenames, file);
|
|
}
|
|
|
|
if (!filenames)
|
|
{
|
|
g_warning ("%s: got empty uri list", G_STRLOC);
|
|
goto out;
|
|
}
|
|
|
|
mask = moo_get_modifiers (widget);
|
|
|
|
#if 0
|
|
#define ACTION_NAME(ac) (ac == GDK_ACTION_DEFAULT ? "DEFAULT" : \
|
|
(ac == GDK_ACTION_COPY ? "COPY" : \
|
|
(ac == GDK_ACTION_MOVE ? "MOVE" : \
|
|
(ac == GDK_ACTION_LINK ? "LINK" : \
|
|
(ac == GDK_ACTION_PRIVATE ? "PRIVATE" : \
|
|
(ac == GDK_ACTION_ASK ? "ASK" : "???"))))))
|
|
|
|
g_print ("suggested: %s\naction: %s\n",
|
|
ACTION_NAME (context->suggested_action),
|
|
ACTION_NAME (context->action));
|
|
|
|
g_print ("actions: %s%s%s%s%s%s\n",
|
|
context->actions & GDK_ACTION_DEFAULT ? "DEFAULT " : "",
|
|
context->actions & GDK_ACTION_COPY ? "COPY " : "",
|
|
context->actions & GDK_ACTION_MOVE ? "MOVE " : "",
|
|
context->actions & GDK_ACTION_LINK ? "LINK " : "",
|
|
context->actions & GDK_ACTION_PRIVATE ? "PRIVATE " : "",
|
|
context->actions & GDK_ACTION_ASK ? "ASK " : "");
|
|
|
|
g_print ("modifiers: %s%s%s\n",
|
|
mask & GDK_SHIFT_MASK ? "SHIFT " : "",
|
|
mask & GDK_CONTROL_MASK ? "CONTROL " : "",
|
|
mask & GDK_MOD1_MASK ? "MOD1 " : "");
|
|
#endif
|
|
|
|
if (mask & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK))
|
|
action = context->suggested_action;
|
|
else
|
|
action = GDK_ACTION_ASK;
|
|
|
|
switch (action)
|
|
{
|
|
case GDK_ACTION_COPY:
|
|
case GDK_ACTION_MOVE:
|
|
case GDK_ACTION_LINK:
|
|
case GDK_ACTION_ASK:
|
|
break;
|
|
|
|
default:
|
|
g_warning ("%s: oops", G_STRLOC);
|
|
action = GDK_ACTION_ASK;
|
|
}
|
|
|
|
if (action == GDK_ACTION_ASK)
|
|
{
|
|
GtkWidget *menu, *item;
|
|
char *dir_copy = g_strdup (destdir);
|
|
|
|
menu = gtk_menu_new ();
|
|
gtk_object_sink (g_object_ref (menu));
|
|
g_signal_connect (menu, "deactivate", G_CALLBACK (destroy_menu), NULL);
|
|
g_object_set_data_full (G_OBJECT (menu), "moo-file-view-drop-files",
|
|
filenames, (GDestroyNotify) free_string_list);
|
|
g_object_set_data_full (G_OBJECT (menu), "moo-file-view-drop-dir",
|
|
dir_copy, g_free);
|
|
|
|
#define CREATE_IT(stock,action,accel_label) \
|
|
item = gtk_image_menu_item_new_from_stock (stock, NULL); \
|
|
g_object_set_data (G_OBJECT (item), "moo-file-view-drop-files", filenames); \
|
|
g_object_set_data (G_OBJECT (item), "moo-file-view-drop-dir", dir_copy); \
|
|
g_object_set_data (G_OBJECT (item), "moo-file-view-drop-action", \
|
|
GINT_TO_POINTER (action)); \
|
|
g_signal_connect (item, "activate", G_CALLBACK (drop_item_activated), fileview); \
|
|
gtk_widget_show (item); \
|
|
moo_menu_item_set_accel_label (item, accel_label); \
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
|
|
|
CREATE_IT (MOO_STOCK_FILE_MOVE, GDK_ACTION_MOVE, "Shift");
|
|
CREATE_IT (MOO_STOCK_FILE_COPY, GDK_ACTION_COPY, "Control");
|
|
CREATE_IT (MOO_STOCK_FILE_LINK, GDK_ACTION_LINK, "Control+Shift");
|
|
#undef CREATE_IT
|
|
|
|
item = gtk_separator_menu_item_new ();
|
|
gtk_widget_show (item);
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
|
|
|
item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CANCEL, NULL);
|
|
gtk_widget_show (item);
|
|
moo_menu_item_set_accel_label (item, "Escape");
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
|
|
|
moo_file_view_drag_finish (fileview, context, TRUE, FALSE, time);
|
|
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
switch (action)
|
|
{
|
|
case GDK_ACTION_COPY:
|
|
copy_files (fileview, filenames, destdir);
|
|
break;
|
|
case GDK_ACTION_MOVE:
|
|
move_files (fileview, filenames, destdir);
|
|
break;
|
|
case GDK_ACTION_LINK:
|
|
link_files (fileview, filenames, destdir);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
success = TRUE;
|
|
|
|
out:
|
|
free_string_list (filenames);
|
|
moo_file_view_drag_finish (fileview, context, success, FALSE, time);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
moo_file_view_drop_text (MooFileView *fileview,
|
|
const char *text,
|
|
const char *destdir,
|
|
GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
gboolean *delete)
|
|
{
|
|
char *name = NULL;
|
|
gboolean result = FALSE;
|
|
GError *error = NULL;
|
|
|
|
g_return_val_if_fail (text != NULL, FALSE);
|
|
|
|
while (TRUE)
|
|
{
|
|
name = moo_file_view_save_drop_dialog (widget, destdir);
|
|
|
|
if (!name)
|
|
return FALSE;
|
|
|
|
if (g_file_test (name, G_FILE_TEST_EXISTS))
|
|
{
|
|
g_critical ("%s: oops", G_STRLOC);
|
|
goto out;
|
|
}
|
|
|
|
if (!moo_save_file_utf8 (name, text, -1, &error))
|
|
{
|
|
char *utf8_name = g_filename_display_name (name);
|
|
char *err_text = g_strdup_printf ("Could not save file\n%s", utf8_name);
|
|
|
|
moo_error_dialog (widget, err_text, error->message);
|
|
|
|
g_free (err_text);
|
|
g_free (utf8_name);
|
|
|
|
g_free (name);
|
|
g_error_free (error);
|
|
name = NULL;
|
|
error = NULL;
|
|
}
|
|
else
|
|
{
|
|
result = TRUE;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
g_free (name);
|
|
return result;
|
|
}
|
|
|
|
|
|
#if 0
|
|
static void
|
|
bookmark_drag_data_received (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
GtkSelectionData *data,
|
|
guint info,
|
|
guint time,
|
|
MooBookmarkView *bkview);
|
|
|
|
|
|
static gboolean
|
|
bookmark_drag_drop (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
MooBookmarkView *bkview);
|
|
|
|
|
|
static void
|
|
bookmark_drag_leave (MooFileView *fileview,
|
|
GdkDragContext *context,
|
|
guint time,
|
|
MooBookmarkView *bkview);
|
|
|
|
|
|
static gboolean
|
|
bookmark_drag_motion (MooBookmarkView *bkview,
|
|
GdkDragContext *context,
|
|
int x,
|
|
int y,
|
|
guint time,
|
|
MooFileView *fileview)
|
|
{
|
|
MooFolder *source_dir;
|
|
MooFolder *current_dir;
|
|
GtkTreePath *path = NULL;
|
|
MooIconViewCell cell;
|
|
int cell_x, cell_y;
|
|
gboolean new_timeout = TRUE;
|
|
|
|
if (!check_drop_targets (fileview, context, GTK_WIDGET (bkview)) ||
|
|
!moo_tree_view_get_path_at_pos (bkview, x, y, &path, &cell_x, &cell_y))
|
|
{
|
|
moo_tree_view_set_drag_dest_row (bkview, NULL);
|
|
cancel_drop_open (fileview);
|
|
return FALSE;
|
|
}
|
|
|
|
moo_tree_view_set_drag_dest_row (bkview, path);
|
|
|
|
gdk_drag_status (context, context->actions & GDK_ACTION_MOVE ?
|
|
GDK_ACTION_MOVE : context->suggested_action, time);
|
|
|
|
if (fileview->priv->drop_to.row)
|
|
{
|
|
GtkTreePath *old_path = gtk_tree_row_reference_get_path (fileview->priv->drop_to.row);
|
|
|
|
if (old_path && !gtk_tree_path_compare (path, old_path) &&
|
|
fileview->priv->drop_to.cell == cell &&
|
|
!gtk_drag_check_threshold (GTK_WIDGET (bkview),
|
|
fileview->priv->drop_to.x,
|
|
fileview->priv->drop_to.y,
|
|
cell_x,
|
|
cell_y))
|
|
{
|
|
new_timeout = FALSE;
|
|
g_assert (fileview->priv->drop_to.timeout != 0);
|
|
}
|
|
}
|
|
|
|
if (new_timeout)
|
|
{
|
|
cancel_drop_open (fileview);
|
|
|
|
fileview->priv->drop_to.row =
|
|
gtk_tree_row_reference_new (moo_tree_view_get_model (bkview), path);
|
|
fileview->priv->drop_to.timeout =
|
|
g_timeout_add (DROP_OPEN_TIMEOUT,
|
|
(GSourceFunc) drop_open_timeout_func,
|
|
fileview);
|
|
fileview->priv->drop_to.x = cell_x;
|
|
fileview->priv->drop_to.y = cell_y;
|
|
fileview->priv->drop_to.cell = cell;
|
|
}
|
|
|
|
out:
|
|
if (path)
|
|
gtk_tree_path_free (path);
|
|
return TRUE;
|
|
}
|
|
#endif
|